diff --git a/Gemfile.lock b/Gemfile.lock index 4751c61aa28..50421c9b867 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -15,8 +15,8 @@ GEM minitest (>= 5.1) mutex_m tzinfo (~> 2.0) - addressable (2.8.6) - public_suffix (>= 2.0.2, < 6.0) + addressable (2.8.7) + public_suffix (>= 2.0.2, < 7.0) algoliasearch (1.27.5) httpclient (~> 2.8, >= 2.8.3) json (>= 1.5.1) @@ -62,7 +62,7 @@ GEM netrc (~> 0.11) cocoapods-try (1.2.0) colored2 (3.1.2) - concurrent-ruby (1.3.3) + concurrent-ruby (1.3.4) connection_pool (2.4.1) drb (2.2.1) escape (0.0.4) @@ -76,7 +76,7 @@ GEM i18n (1.14.5) concurrent-ruby (~> 1.0) json (2.7.2) - minitest (5.23.1) + minitest (5.25.1) molinillo (0.8.0) mutex_m (0.2.0) nanaimo (0.3.0) @@ -84,7 +84,7 @@ GEM netrc (0.11.0) nkf (0.2.0) public_suffix (4.0.7) - rexml (3.2.9) + rexml (3.3.5) strscan ruby-macho (2.5.1) strscan (3.1.0) @@ -92,13 +92,13 @@ GEM ethon (>= 0.9.0) tzinfo (2.0.6) concurrent-ruby (~> 1.0) - xcodeproj (1.24.0) + xcodeproj (1.25.0) CFPropertyList (>= 2.3.3, < 4.0) atomos (~> 0.1.3) claide (>= 1.0.2, < 2.0) colored2 (~> 3.1) nanaimo (~> 0.3.0) - rexml (~> 3.2.4) + rexml (>= 3.3.2, < 4.0) PLATFORMS ruby diff --git a/NOTICE.txt b/NOTICE.txt index b340b0d24be..eb861789b5a 100644 --- a/NOTICE.txt +++ b/NOTICE.txt @@ -1594,6 +1594,27 @@ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +--- + +## node-html-parser + +This product contains 'node-html-parser' by Xiaoyi Shi. + +A very fast HTML parser, generating a simplified DOM, with basic element query support. + +* HOMEPAGE: + * https://github.com/taoqf/node-fast-html-parser + +* LICENSE: MIT + +Copyright 2019 Tao Qiufeng + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + --- ## pako diff --git a/android/app/build.gradle b/android/app/build.gradle index ea4542d7fd1..18ec581623a 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -111,8 +111,8 @@ android { applicationId "com.mattermost.rnbeta" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 532 - versionName "2.18.0" + versionCode 555 + versionName "2.20.0" testBuildType System.getProperty('testBuildType', 'debug') testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' } diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 227ee36dccd..96183f307a5 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -21,6 +21,7 @@ + + + + + diff --git a/android/app/src/main/assets/certs/.gitignore b/android/app/src/main/assets/certs/.gitignore new file mode 100644 index 00000000000..86d0cb2726c --- /dev/null +++ b/android/app/src/main/assets/certs/.gitignore @@ -0,0 +1,4 @@ +# Ignore everything in this directory +* +# Except this file +!.gitignore \ No newline at end of file diff --git a/app/actions/app/global.test.ts b/app/actions/app/global.test.ts new file mode 100644 index 00000000000..8ac3d2c9338 --- /dev/null +++ b/app/actions/app/global.test.ts @@ -0,0 +1,186 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import {Tutorial} from '@constants'; +import DatabaseManager from '@database/manager'; +import { + getDeviceToken, + getDontAskForReview, + getFirstLaunch, + getLastAskedForReview, + getOnboardingViewed, + getLastViewedChannelIdAndServer, + getLastViewedThreadIdAndServer, + getPushDisabledInServerAcknowledged, + queryGlobalValue, +} from '@queries/app/global'; + +import { + storeGlobal, + storeDeviceToken, + storeOnboardingViewedValue, + storeMultiServerTutorial, + storeProfileLongPressTutorial, + storeSkinEmojiSelectorTutorial, + storeDontAskForReview, + storeLastAskForReview, + storeFirstLaunch, + storeLastViewedChannelIdAndServer, + storeLastViewedThreadIdAndServer, + removeLastViewedChannelIdAndServer, + removeLastViewedThreadIdAndServer, + storePushDisabledInServerAcknowledged, + removePushDisabledInServerAcknowledged, +} from './global'; + +const serverUrl = 'server.test.com'; + +jest.mock('react-native-keychain', () => { + const original = jest.requireActual('react-native-keychain'); + return { + ...original, + getAllInternetPasswordServers: jest.fn(() => Promise.resolve([serverUrl])), + }; +}); + +jest.mock('@utils/log', () => ({ + logError: jest.fn(), +})); + +describe('/app/actions/app/global', () => { + beforeEach(async () => { + await DatabaseManager.init([serverUrl]); + }); + + afterEach(async () => { + await DatabaseManager.destroyServerDatabase(serverUrl); + }); + + test('storeDeviceToken', async () => { + let storedValue = await getDeviceToken(); + expect(storedValue).toBe(''); + + const inputValue = 'new-token'; + await storeDeviceToken(inputValue); + + storedValue = await getDeviceToken(); + expect(storedValue).toBe(inputValue); + }); + + test('storeOnboardingViewedValue', async () => { + let storedValue = await getOnboardingViewed(); + expect(storedValue).toBe(false); + + await storeOnboardingViewedValue(); + storedValue = await getOnboardingViewed(); + expect(storedValue).toBe(true); + + await storeOnboardingViewedValue(false); + storedValue = await getOnboardingViewed(); + expect(storedValue).toBe(false); + }); + + test('storeMultiServerTutorial', async () => { + let records = await queryGlobalValue(Tutorial.MULTI_SERVER)?.fetch(); + expect(records?.[0]?.value).toBeUndefined(); + + await storeMultiServerTutorial(); + records = await queryGlobalValue(Tutorial.MULTI_SERVER)?.fetch(); + expect(records?.[0]?.value).toBe(true); + }); + + test('storeProfileLongPressTutorial', async () => { + let records = await queryGlobalValue(Tutorial.PROFILE_LONG_PRESS)?.fetch(); + expect(records?.[0]?.value).toBeUndefined(); + + await storeProfileLongPressTutorial(); + records = await queryGlobalValue(Tutorial.PROFILE_LONG_PRESS)?.fetch(); + expect(records?.[0]?.value).toBe(true); + }); + + test('storeSkinEmojiSelectorTutorial', async () => { + let records = await queryGlobalValue(Tutorial.EMOJI_SKIN_SELECTOR)?.fetch(); + expect(records?.[0]?.value).toBeUndefined(); + + await storeSkinEmojiSelectorTutorial(); + records = await queryGlobalValue(Tutorial.EMOJI_SKIN_SELECTOR)?.fetch(); + expect(records?.[0]?.value).toBe(true); + }); + + test('storeDontAskForReview', async () => { + let storedValue = await getDontAskForReview(); + expect(storedValue).toBe(false); + + await storeDontAskForReview(); + storedValue = await getDontAskForReview(); + expect(storedValue).toBe(true); + }); + + test('storeLastAskForReview', async () => { + let storedValue = await getLastAskedForReview(); + expect(storedValue).toBe(0); + + await storeLastAskForReview(); + storedValue = await getLastAskedForReview(); + expect(storedValue).toBeCloseTo(Date.now(), -5); + }); + + test('storeFirstLaunch', async () => { + let storedValue = await getFirstLaunch(); + expect(storedValue).toBe(0); + + await storeFirstLaunch(); + storedValue = await getFirstLaunch(); + expect(storedValue).toBeCloseTo(Date.now(), -5); + }); + + test('LastViewedChannelIdAndServer', async () => { + let storedValue = await getLastViewedChannelIdAndServer(); + expect(storedValue).toBeUndefined(); + + await storeLastViewedChannelIdAndServer('channel-id-1'); + storedValue = await getLastViewedChannelIdAndServer(); + expect(storedValue).toMatchObject({channel_id: 'channel-id-1', server_url: serverUrl}); + + await removeLastViewedChannelIdAndServer(); + storedValue = await getLastViewedChannelIdAndServer(); + expect(storedValue).toBeUndefined(); + }); + + test('LastViewedThreadIdAndServer', async () => { + let storedValue = await getLastViewedThreadIdAndServer(); + expect(storedValue).toBeUndefined(); + + await storeLastViewedThreadIdAndServer('thread-id-1'); + storedValue = await getLastViewedThreadIdAndServer(); + expect(storedValue).toMatchObject({thread_id: 'thread-id-1', server_url: serverUrl}); + + await removeLastViewedThreadIdAndServer(); + storedValue = await getLastViewedThreadIdAndServer(); + expect(storedValue).toBeUndefined(); + }); + + test('PushDisabledInServerAcknowledged', async () => { + let storedValue = await getPushDisabledInServerAcknowledged(serverUrl); + expect(storedValue).toBe(false); + + await storePushDisabledInServerAcknowledged(serverUrl); + storedValue = await getPushDisabledInServerAcknowledged(serverUrl); + expect(storedValue).toBe(true); + + await removePushDisabledInServerAcknowledged(serverUrl); + storedValue = await getPushDisabledInServerAcknowledged(serverUrl); + expect(storedValue).toBe(false); + }); + + test('storeGlobal catch error', async () => { + delete DatabaseManager.appDatabase; + + const response = await storeGlobal('key', ''); + + expect(response).toMatchObject({error: expect.any(Error)}); + + // @ts-expect-error testing error message + expect(response.error.message).toBe('App database not found'); + }); +}); diff --git a/app/actions/local/channel_bookmark.test.ts b/app/actions/local/channel_bookmark.test.ts new file mode 100644 index 00000000000..91e9b108d5e --- /dev/null +++ b/app/actions/local/channel_bookmark.test.ts @@ -0,0 +1,125 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import DatabaseManager from '@database/manager'; + +import { + handleBookmarkAddedOrDeleted, + handleBookmarkEdited, + handleBookmarkSorted, +} from './channel_bookmark'; + +import type ServerDataOperator from '@database/operator/server_data_operator'; +import type {Model} from '@nozbe/watermelondb'; + +const serverUrl = 'baseHandler.test.com'; +let operator: ServerDataOperator; + +let mockGenerateId: jest.Mock; +jest.mock('@utils/general', () => { + const original = jest.requireActual('@utils/general'); + mockGenerateId = jest.fn(() => 'testpostid'); + return { + ...original, + generateId: mockGenerateId, + }; +}); + +const channelId = 'channelid1'; +const bookmark = { + channel_id: channelId, + id: 'bookmarkid', + owner_id: 'userid1', + type: 'link', + sort_order: 0, +} as ChannelBookmark; +const channel: Channel = { + id: channelId, + team_id: 'teamid', + total_msg_count: 0, +} as Channel; +const channelMember: ChannelMembership = { + id: 'id', + channel_id: channelId, + user_id: 'userid1', + msg_count: 0, +} as ChannelMembership; + +beforeEach(async () => { + await DatabaseManager.init([serverUrl]); + operator = DatabaseManager.serverDatabases[serverUrl]!.operator; +}); + +afterEach(async () => { + await DatabaseManager.destroyServerDatabase(serverUrl); +}); + +describe('handleBookmarks', () => { + it('handleBookmarkAddedOrDeleted - handle not found database', async () => { + const {error} = await handleBookmarkAddedOrDeleted('foo', {} as WebSocketMessage) as {error: unknown}; + expect(error).toBeTruthy(); + }); + + it('handleBookmarkAddedOrDeleted - no channel', async () => { + const {models} = await handleBookmarkAddedOrDeleted(serverUrl, {data: {bookmark: JSON.stringify(bookmark)}} as WebSocketMessage) as {models: undefined}; + expect(models).toBeUndefined(); + }); + + it('handleBookmarkAddedOrDeleted - base case', async () => { + await operator.handleChannel({channels: [channel], prepareRecordsOnly: false}); + await operator.handleMyChannel({channels: [channel], myChannels: [channelMember], prepareRecordsOnly: false}); + + const {models} = await handleBookmarkAddedOrDeleted(serverUrl, {data: {bookmark: JSON.stringify(bookmark)}} as WebSocketMessage) as {models: Model[]}; + expect(models).toBeDefined(); + expect(models?.length).toBe(1); // channel bookmark + }); + + it('handleBookmarkEdited - handle not found database', async () => { + const {error} = await handleBookmarkEdited('foo', {} as WebSocketMessage) as {error: unknown}; + expect(error).toBeTruthy(); + }); + + it('handleBookmarkEdited - no channel', async () => { + const bookmarkResponse = {updated: bookmark, deleted: {...bookmark, id: 'bookmarkid2'}} as UpdateChannelBookmarkResponse; + const {models} = await handleBookmarkEdited(serverUrl, {data: {bookmarks: JSON.stringify(bookmarkResponse)}} as WebSocketMessage) as {models: undefined}; + expect(models).toBeUndefined(); + }); + + it('handleBookmarkEdited - base case', async () => { + await operator.handleChannel({channels: [channel], prepareRecordsOnly: false}); + await operator.handleMyChannel({channels: [channel], myChannels: [channelMember], prepareRecordsOnly: false}); + + const bookmarkResponse = {updated: bookmark, deleted: {...bookmark, id: 'bookmarkid2', sort_order: 1}} as UpdateChannelBookmarkResponse; + + const {models} = await handleBookmarkEdited(serverUrl, {data: {bookmarks: JSON.stringify(bookmarkResponse)}} as WebSocketMessage) as {models: Model[]}; + expect(models).toBeDefined(); + expect(models?.length).toBe(2); // 2 channel bookmarks + }); + + it('handleBookmarkSorted - handle not found database', async () => { + const {error} = await handleBookmarkSorted('foo', {} as WebSocketMessage) as {error: unknown}; + expect(error).toBeTruthy(); + }); + + it('handleBookmarkSorted - no channel', async () => { + const bookmarks = [bookmark, {...bookmark, id: 'bookmarkid2', sort_order: 1}]; + const {models} = await handleBookmarkSorted(serverUrl, {data: {bookmarks: JSON.stringify(bookmarks)}} as WebSocketMessage) as {models: undefined}; + expect(models).toBeUndefined(); + }); + + it('handleBookmarkSorted - no bookmarks', async () => { + const {models} = await handleBookmarkSorted(serverUrl, {data: {bookmarks: JSON.stringify([])}} as WebSocketMessage) as {models: undefined}; + expect(models).toBeUndefined(); + }); + + it('handleBookmarkSorted - base case', async () => { + await operator.handleChannel({channels: [channel], prepareRecordsOnly: false}); + await operator.handleMyChannel({channels: [channel], myChannels: [channelMember], prepareRecordsOnly: false}); + + const bookmarks = [bookmark, {...bookmark, id: 'bookmarkid2', sort_order: 1}]; + + const {models} = await handleBookmarkSorted(serverUrl, {data: {bookmarks: JSON.stringify(bookmarks)}} as WebSocketMessage) as {models: Model[]}; + expect(models).toBeDefined(); + expect(models?.length).toBe(2); // 2 channel bookmarks + }); +}); diff --git a/app/actions/local/channel_bookmark.ts b/app/actions/local/channel_bookmark.ts new file mode 100644 index 00000000000..ff2479756f0 --- /dev/null +++ b/app/actions/local/channel_bookmark.ts @@ -0,0 +1,55 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import DatabaseManager from '@database/manager'; +import {getMyChannel} from '@queries/servers/channel'; +import {logError} from '@utils/log'; + +async function handleBookmarks(serverUrl: string, bookmarks: ChannelBookmarkWithFileInfo[], prepareRecordsOnly = false) { + if (!bookmarks.length) { + return {models: undefined}; + } + + const {database, operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); + const myChannel = await getMyChannel(database, bookmarks[0].channel_id); + if (!myChannel) { + return {models: undefined}; + } + + const models = await operator.handleChannelBookmark({bookmarks, prepareRecordsOnly}); + return {models}; +} + +export async function handleBookmarkAddedOrDeleted(serverUrl: string, msg: WebSocketMessage, prepareRecordsOnly = false) { + try { + const bookmark: ChannelBookmarkWithFileInfo = JSON.parse(msg.data.bookmark); + return handleBookmarks(serverUrl, [bookmark], prepareRecordsOnly); + } catch (error) { + logError('cannot handle bookmark added websocket event', error); + return {error}; + } +} + +export async function handleBookmarkEdited(serverUrl: string, msg: WebSocketMessage, prepareRecordsOnly = false) { + try { + const edited: UpdateChannelBookmarkResponse = JSON.parse(msg.data.bookmarks); + const bookmarks = [edited.updated]; + if (edited.deleted) { + bookmarks.push(edited.deleted); + } + return handleBookmarks(serverUrl, bookmarks, prepareRecordsOnly); + } catch (error) { + logError('cannot handle bookmark updated websocket event', error); + return {error}; + } +} + +export async function handleBookmarkSorted(serverUrl: string, msg: WebSocketMessage, prepareRecordsOnly = false) { + try { + const bookmarks: ChannelBookmarkWithFileInfo[] = JSON.parse(msg.data.bookmarks); + return handleBookmarks(serverUrl, bookmarks, prepareRecordsOnly); + } catch (error) { + logError('cannot handle bookmark sorted websocket event', error); + return {error}; + } +} diff --git a/app/actions/local/file.ts b/app/actions/local/file.ts index 649ef9666d0..a4fdba9a781 100644 --- a/app/actions/local/file.ts +++ b/app/actions/local/file.ts @@ -34,3 +34,12 @@ export const updateLocalFilePath = async (serverUrl: string, fileId: string, loc } }; +export const getLocalFileInfo = async (serverUrl: string, fileId: string) => { + try { + const {database} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); + const file = await getFileById(database, fileId); + return {file}; + } catch (error) { + return {error}; + } +}; diff --git a/app/actions/local/post.test.ts b/app/actions/local/post.test.ts new file mode 100644 index 00000000000..79b221b6ba2 --- /dev/null +++ b/app/actions/local/post.test.ts @@ -0,0 +1,338 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import {ActionType, Post} from '@app/constants'; +import {COMBINED_USER_ACTIVITY} from '@app/utils/post_list'; +import {SYSTEM_IDENTIFIERS} from '@constants/database'; +import DatabaseManager from '@database/manager'; +import TestHelper from '@test/test_helper'; + +import { + sendAddToChannelEphemeralPost, + sendEphemeralPost, + removePost, + markPostAsDeleted, + storePostsForChannel, + getPosts, + addPostAcknowledgement, + removePostAcknowledgement, + deletePosts, + getUsersCountFromMentions, +} from './post'; + +import type ServerDataOperator from '@database/operator/server_data_operator'; +import type UserModel from '@typings/database/models/servers/user'; + +const serverUrl = 'baseHandler.test.com'; +let operator: ServerDataOperator; + +let mockGenerateId: jest.Mock; +jest.mock('@utils/general', () => { + const original = jest.requireActual('@utils/general'); + mockGenerateId = jest.fn(() => 'testpostid'); + return { + ...original, + generateId: mockGenerateId, + }; +}); + +const channelId = 'channelid1'; +const user: UserProfile = { + id: 'userid', + username: 'username', + roles: '', +} as UserProfile; + +beforeEach(async () => { + await DatabaseManager.init([serverUrl]); + operator = DatabaseManager.serverDatabases[serverUrl]!.operator; +}); + +afterEach(async () => { + await DatabaseManager.destroyServerDatabase(serverUrl); +}); + +describe('sendAddToChannelEphemeralPost', () => { + it('handle not found database', async () => { + const {posts, error} = await sendAddToChannelEphemeralPost('foo', {} as UserModel, ['username2'], ['added username2'], channelId, ''); + expect(posts).toBeUndefined(); + expect(error).toBeTruthy(); + }); + + it('base case', async () => { + const users = await operator.handleUsers({users: [user], prepareRecordsOnly: false}); + + const {posts, error} = await sendAddToChannelEphemeralPost(serverUrl, users[0], ['username2'], ['added username2'], channelId, ''); + expect(error).toBeUndefined(); + expect(posts).toBeDefined(); + expect(posts?.length).toBe(1); + expect(posts![0].message).toBe('added username2'); + }); +}); + +describe('sendEphemeralPost', () => { + it('handle not found database', async () => { + const {post, error} = await sendEphemeralPost('foo', 'message', channelId, '', user.id); + expect(post).toBeUndefined(); + expect(error).toBeTruthy(); + }); + + it('handle no channel', async () => { + const {post, error} = await sendEphemeralPost(serverUrl, 'newmessage', '', '', user.id); + expect(post).toBeUndefined(); + expect(error).toBeTruthy(); + }); + + it('handle no user', async () => { + await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_USER_ID, value: 'useridcurrent'}], prepareRecordsOnly: false}); + + const {post, error} = await sendEphemeralPost(serverUrl, 'newmessage', channelId, ''); + expect(error).toBeUndefined(); + expect(post).toBeDefined(); + expect(post?.user_id).toBe('useridcurrent'); + }); + + it('base case', async () => { + const {post, error} = await sendEphemeralPost(serverUrl, 'newmessage', channelId, '', user.id); + expect(error).toBeUndefined(); + expect(post).toBeDefined(); + expect(post?.message).toBe('newmessage'); + }); +}); + +describe('removePost', () => { + const post = {...TestHelper.fakePost(channelId), id: 'postid'}; + + it('handle not found database', async () => { + const {post: rPost, error} = await removePost('foo', post); + expect(rPost).toBeUndefined(); + expect(error).toBeTruthy(); + }); + + it('base case', async () => { + await operator.handlePosts({ + actionType: ActionType.POSTS.RECEIVED_IN_CHANNEL, + order: [post.id], + posts: [post], + prepareRecordsOnly: false, + }); + + const {post: rPost, error} = await removePost(serverUrl, post); + expect(error).toBeUndefined(); + expect(rPost).toBeDefined(); + }); + + it('base case - system message', async () => { + const systemPost = {...TestHelper.fakePost(channelId), id: `${COMBINED_USER_ACTIVITY}id1_id2`, type: Post.POST_TYPES.COMBINED_USER_ACTIVITY as PostType, props: {system_post_ids: ['id1']}}; + + await operator.handlePosts({ + actionType: ActionType.POSTS.RECEIVED_IN_CHANNEL, + order: [post.id, 'id1'], + posts: [systemPost, {...TestHelper.fakePost(channelId), id: 'id1'}], + prepareRecordsOnly: false, + }); + + const {post: rPost, error} = await removePost(serverUrl, systemPost); + expect(error).toBeUndefined(); + expect(rPost).toBeDefined(); + }); +}); + +describe('markPostAsDeleted', () => { + const post = TestHelper.fakePost(channelId); + + it('handle not found database', async () => { + const {model, error} = await markPostAsDeleted('foo', post); + expect(model).toBeUndefined(); + expect(error).toBeTruthy(); + }); + + it('handle no post', async () => { + const {model, error} = await markPostAsDeleted(serverUrl, post, false); + expect(model).toBeUndefined(); + expect(error).toBeTruthy(); + }); + + it('base case', async () => { + await operator.handlePosts({ + actionType: ActionType.POSTS.RECEIVED_IN_CHANNEL, + order: [post.id], + posts: [post], + prepareRecordsOnly: false, + }); + + const {model, error} = await markPostAsDeleted(serverUrl, post, false); + expect(error).toBeUndefined(); + expect(model).toBeDefined(); + expect(model?.deleteAt).toBeGreaterThan(0); + }); +}); + +describe('storePostsForChannel', () => { + const post = TestHelper.fakePost(channelId); + post.user_id = user.id; + const teamId = 'tId1'; + const channel: Channel = { + id: channelId, + team_id: teamId, + total_msg_count: 0, + } as Channel; + const channelMember: ChannelMembership = { + id: 'id', + channel_id: channelId, + msg_count: 0, + } as ChannelMembership; + + it('handle not found database', async () => { + const {models, error} = await storePostsForChannel('foo', channelId, [post], [post.id], '', ActionType.POSTS.RECEIVED_IN_CHANNEL, [user], false); + expect(models).toBeUndefined(); + expect(error).toBeTruthy(); + }); + + it('base case', async () => { + await operator.handleMyChannel({channels: [channel], myChannels: [channelMember], prepareRecordsOnly: false}); + await operator.handleConfigs({ + configs: [ + {id: 'CollapsedThreads', value: 'default_on'}, + {id: 'FeatureFlagCollapsedThreads', value: 'true'}, + ], + configsToDelete: [], + prepareRecordsOnly: false, + }); + + const {models, error} = await storePostsForChannel(serverUrl, channelId, [post], [post.id], '', ActionType.POSTS.RECEIVED_IN_CHANNEL, [user], false); + expect(error).toBeUndefined(); + expect(models).toBeDefined(); + expect(models?.length).toBe(5); // Post, PostsInChannel, User, MyChannel, Thread + }); +}); + +describe('getPosts', () => { + const post = TestHelper.fakePost(channelId); + + it('handle not found database', async () => { + const posts = await getPosts('foo', [post.id]); + expect(posts).toBeDefined(); + expect(posts?.length).toBe(0); + }); + + it('base case', async () => { + await operator.handlePosts({ + actionType: ActionType.POSTS.RECEIVED_IN_CHANNEL, + order: [post.id], + posts: [post], + prepareRecordsOnly: false, + }); + + const posts = await getPosts(serverUrl, [post.id]); + expect(posts).toBeDefined(); + expect(posts.length).toBe(1); + }); +}); + +describe('addPostAcknowledgement', () => { + const post = TestHelper.fakePost(channelId); + + it('handle not found database', async () => { + const {model, error} = await addPostAcknowledgement('foo', post.id, user.id, 123, false); + expect(model).toBeUndefined(); + expect(error).toBeTruthy(); + }); + + it('handle no post', async () => { + const {model, error} = await addPostAcknowledgement(serverUrl, post.id, user.id, 123, false); + expect(error).toBeDefined(); + expect(model).toBeUndefined(); + }); + + it('handle already acked', async () => { + await operator.handlePosts({ + actionType: ActionType.POSTS.RECEIVED_IN_CHANNEL, + order: [post.id], + posts: [{...post, metadata: {acknowledgements: [{user_id: user.id, post_id: post.id, acknowledged_at: 1}]}}], + prepareRecordsOnly: false, + }); + + const {model, error} = await addPostAcknowledgement(serverUrl, post.id, user.id, 123, false); + expect(error).toBeDefined(); + expect(error).toBe(false); + expect(model).toBeUndefined(); + }); + + it('base case', async () => { + await operator.handlePosts({ + actionType: ActionType.POSTS.RECEIVED_IN_CHANNEL, + order: [post.id], + posts: [post], + prepareRecordsOnly: false, + }); + + const {model, error} = await addPostAcknowledgement(serverUrl, post.id, user.id, 123, false); + expect(error).toBeUndefined(); + expect(model).toBeDefined(); + }); +}); + +describe('removePostAcknowledgement', () => { + const post = TestHelper.fakePost(channelId); + + it('handle not found database', async () => { + const {model, error} = await removePostAcknowledgement('foo', post.id, user.id, false); + expect(model).toBeUndefined(); + expect(error).toBeTruthy(); + }); + + it('handle no post', async () => { + const {model, error} = await removePostAcknowledgement(serverUrl, post.id, user.id, false); + expect(error).toBeDefined(); + expect(model).toBeUndefined(); + }); + + it('base case', async () => { + await operator.handlePosts({ + actionType: ActionType.POSTS.RECEIVED_IN_CHANNEL, + order: [post.id], + posts: [{...post, metadata: {acknowledgements: [{user_id: user.id, post_id: post.id, acknowledged_at: 1}]}}], + prepareRecordsOnly: false, + }); + + const {model, error} = await removePostAcknowledgement(serverUrl, post.id, user.id, false); + expect(error).toBeUndefined(); + expect(model).toBeDefined(); + }); +}); + +describe('deletePosts', () => { + const post = TestHelper.fakePost(channelId); + + it('handle not found database', async () => { + const {error} = await deletePosts('foo', [post.id]); + expect(error).toBeTruthy(); + }); + + it('base case', async () => { + await operator.handlePosts({ + actionType: ActionType.POSTS.RECEIVED_IN_CHANNEL, + order: [post.id], + posts: [post], + prepareRecordsOnly: false, + }); + + const {error} = await deletePosts(serverUrl, [post.id, 'id2']); + expect(error).toBeDefined(); + }); +}); + +describe('getUsersCountFromMentions', () => { + it('handle not found database', async () => { + const num = await getUsersCountFromMentions('foo', []); + expect(num).toBe(0); + }); + + it('base case', async () => { + await operator.handleUsers({users: [user], prepareRecordsOnly: false}); + + const num = await getUsersCountFromMentions(serverUrl, [user.username]); + expect(num).toBe(1); + }); +}); diff --git a/app/actions/local/reactions.test.ts b/app/actions/local/reactions.test.ts new file mode 100644 index 00000000000..ff08b5f52eb --- /dev/null +++ b/app/actions/local/reactions.test.ts @@ -0,0 +1,129 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. +import DatabaseManager from '@database/manager'; +import * as recentReactionsQueries from '@queries/servers/system'; +import * as emojiHelpers from '@utils/emoji/helpers'; +import * as logUtils from '@utils/log'; + +import {addRecentReaction} from './reactions'; + +import type ServerDataOperator from '@app/database/operator/server_data_operator'; + +jest.mock('@database/manager'); +jest.mock('@queries/servers/system'); +jest.mock('@utils/emoji/helpers'); +jest.mock('@utils/log'); + +describe('addRecentReaction', () => { + let operator: ServerDataOperator; + const serverUrl = 'baseHandler.test.com'; + + beforeEach(async () => { + jest.clearAllMocks(); + + await DatabaseManager.init([serverUrl]); + operator = DatabaseManager.serverDatabases[serverUrl]!.operator; + + (recentReactionsQueries.getRecentReactions as jest.Mock).mockResolvedValue([]); + (emojiHelpers.getEmojiFirstAlias as jest.Mock).mockImplementation((emoji) => emoji); + + operator.handleSystem = jest.fn(); + }); + + afterEach(async () => { + await DatabaseManager.destroyServerDatabase(serverUrl); + }); + + it('should return an empty array if emojiNames is empty', async () => { + const result = await addRecentReaction(serverUrl, []); + expect(result).toEqual([]); + }); + + it('should add new emoji to the beginning of the list', async () => { + (recentReactionsQueries.getRecentReactions as jest.Mock).mockResolvedValue([':water:']); + const emojiNames = [':air:', ':fire:']; + await addRecentReaction(serverUrl, emojiNames); + + expect(operator.handleSystem).toHaveBeenCalledWith({ + systems: [{ + id: 'recentReactions', + value: JSON.stringify([':fire:', ':air:', ':water:']), + }], + prepareRecordsOnly: false, + }); + }); + + it('should move existing emoji to the beginning of the list', async () => { + (recentReactionsQueries.getRecentReactions as jest.Mock).mockResolvedValue([':water:', ':fire:']); + const emojiNames = [':air:', ':fire:']; + await addRecentReaction(serverUrl, emojiNames); + + expect(operator.handleSystem).toHaveBeenCalledWith({ + systems: [{ + id: 'recentReactions', + value: JSON.stringify([':fire:', ':air:', ':water:']), + }], + prepareRecordsOnly: false, + }); + }); + + it('should limit the list to MAXIMUM_RECENT_EMOJI', async () => { + const longEmojiList = Array.from({length: 40}, (_, i) => `emoji${i}`); + (recentReactionsQueries.getRecentReactions as jest.Mock).mockResolvedValue(longEmojiList); + await addRecentReaction(serverUrl, ['newEmoji']); + + const handleSystemCall = (operator.handleSystem as jest.Mock).mock.calls[0][0]; + const savedEmojis = JSON.parse(handleSystemCall.systems[0].value); + expect(savedEmojis.length).toBe(27); + expect(savedEmojis[0]).toBe('newEmoji'); + }); + + it('should use getEmojiFirstAlias for each emoji', async () => { + (emojiHelpers.getEmojiFirstAlias as jest.Mock).mockImplementation((emoji) => { + if (emoji === ':air:') { + return ':wind:'; + } + if (emoji === ':fire:') { + return ':flame:'; + } + return emoji; + }); + + const emojiNames = [':air:', ':fire:']; + await addRecentReaction(serverUrl, emojiNames); + + expect(emojiHelpers.getEmojiFirstAlias).toHaveBeenCalledTimes(2); + expect(emojiHelpers.getEmojiFirstAlias).toHaveBeenCalledWith(':air:'); + expect(emojiHelpers.getEmojiFirstAlias).toHaveBeenCalledWith(':fire:'); + expect(operator.handleSystem).toHaveBeenCalledWith({ + systems: [{ + id: 'recentReactions', + value: JSON.stringify([':flame:', ':wind:']), + }], + prepareRecordsOnly: false, + }); + }); + + it('should handle errors and log them', async () => { + const unregisteredServerUrl = 'unregistered.test.com'; + const failedError = new Error(`${unregisteredServerUrl} database not found`); + + const result = await addRecentReaction(unregisteredServerUrl, [':air:']); + + expect(logUtils.logError).toHaveBeenCalledWith('Failed addRecentReaction', failedError); + expect(result).toEqual({error: failedError}); + }); + + it('should handle prepareRecordsOnly=true flag', async () => { + const emojiNames = [':air:']; + await addRecentReaction(serverUrl, emojiNames, true); + + expect(operator.handleSystem).toHaveBeenCalledWith({ + systems: [{ + id: 'recentReactions', + value: JSON.stringify([':air:']), + }], + prepareRecordsOnly: true, + }); + }); +}); diff --git a/app/actions/local/systems.test.ts b/app/actions/local/systems.test.ts new file mode 100644 index 00000000000..6ac807916de --- /dev/null +++ b/app/actions/local/systems.test.ts @@ -0,0 +1,174 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import Database from '@nozbe/watermelondb/Database'; + +import {ActionType} from '@app/constants'; +import {SYSTEM_IDENTIFIERS} from '@app/constants/database'; +import DatabaseManager from '@database/manager'; +import TestHelper from '@test/test_helper'; + +import { + storeConfig, + storeConfigAndLicense, + storeDataRetentionPolicies, + updateLastDataRetentionRun, + dataRetentionCleanup, + setLastServerVersionCheck, + setGlobalThreadsTab, + dismissAnnouncement, +} from './systems'; + +import type {DataRetentionPoliciesRequest} from '@actions/remote/systems'; +import type ServerDataOperator from '@database/operator/server_data_operator'; +import type SystemModel from '@typings/database/models/servers/system'; + +const serverUrl = 'baseHandler.test.com'; +let operator: ServerDataOperator; + +jest.mock('@init/credentials', () => { + const original = jest.requireActual('@init/credentials'); + return { + ...original, + getServerCredentials: jest.fn(async (url: string) => ({serverUrl: url})), + }; +}); + +beforeEach(async () => { + await DatabaseManager.init([serverUrl]); + operator = DatabaseManager.serverDatabases[serverUrl]!.operator; +}); + +afterEach(async () => { + await DatabaseManager.destroyServerDatabase(serverUrl); +}); + +describe('storeConfigAndLicense', () => { + it('handle not found database - storeConfig', async () => { + const models = await storeConfig('foo', {} as ClientConfig); + expect(models).toBeDefined(); + expect(models.length).toBe(0); + }); + + it('handle not found database', async () => { + const models = await storeConfigAndLicense('foo', {} as ClientConfig, {} as ClientLicense); + expect(models).toBeDefined(); + expect(models.length).toBe(0); + }); + + it('base case', async () => { + const models = await storeConfigAndLicense(serverUrl, {AboutLink: 'link'} as ClientConfig, {Announcement: 'test'} as ClientLicense); + expect(models).toBeDefined(); + expect(models.length).toBe(1); // config + }); +}); + +describe('dataRetention', () => { + it('handle not found database - storeDataRetentionPolicies', async () => { + const models = await storeDataRetentionPolicies('foo', {} as DataRetentionPoliciesRequest); + expect(models).toBeDefined(); + expect(models.length).toBe(0); + }); + + it('base case - storeDataRetentionPolicies', async () => { + const models = await storeDataRetentionPolicies(serverUrl, {globalPolicy: {} as GlobalDataRetentionPolicy, teamPolicies: [], channelPolicies: []} as DataRetentionPoliciesRequest); + expect(models).toBeDefined(); + expect(models.length).toBe(2); // data retention and granular data retention policies + }); + + it('handle not found database - updateLastDataRetentionRun', async () => { + const {error} = await updateLastDataRetentionRun('foo', 0) as {error: unknown}; + expect(error).toBeDefined(); + }); + + it('base case - updateLastDataRetentionRun', async () => { + const models = await updateLastDataRetentionRun(serverUrl, 10) as SystemModel[]; + expect(models).toBeDefined(); + expect(models.length).toBe(1); // data retention + }); + + it('handle not found database - dataRetentionCleanup', async () => { + const {error} = await dataRetentionCleanup('foo'); + expect(error).toBeDefined(); + }); + + it('rentention off - dataRetentionCleanup', async () => { + const post = {...TestHelper.fakePost('channelid1'), id: 'postid', create_at: 1}; + await operator.handlePosts({ + actionType: ActionType.POSTS.RECEIVED_IN_CHANNEL, + order: [post.id], + posts: [post], + prepareRecordsOnly: false, + }); + + const spy = jest.spyOn(Database.prototype, 'unsafeVacuum').mockImplementation(jest.fn()); + const {error} = await dataRetentionCleanup(serverUrl); + expect(error).toBeDefined(); // unsafeExecute loki error + spy.mockRestore(); + }); + + it('retention on - dataRetentionCleanup', async () => { + const channel: Channel = { + id: 'channelid1', + team_id: 'teamid1', + total_msg_count: 0, + } as Channel; + + await operator.handleConfigs({ + configs: [ + {id: 'DataRetentionEnableMessageDeletion', value: 'true'}, + ], + configsToDelete: [], + prepareRecordsOnly: false, + }); + await operator.handleSystem({systems: + [ + {id: SYSTEM_IDENTIFIERS.LICENSE, value: {IsLicensed: 'true', DataRetention: 'true'}}, + {id: SYSTEM_IDENTIFIERS.GRANULAR_DATA_RETENTION_POLICIES, value: {team: [{team_id: 'teamid1', post_duration: 100}], channel: [{channel_id: 'channelid1', post_duration: 100}]}}, + ], + prepareRecordsOnly: false}); + await operator.handleChannel({channels: [channel], prepareRecordsOnly: false}); + + const spy = jest.spyOn(Database.prototype, 'unsafeVacuum').mockImplementation(jest.fn()); + const {error} = await dataRetentionCleanup(serverUrl); + expect(error).toBeDefined(); // LokiJSAdapter doesn't support unsafeSqlQuery + spy.mockRestore(); + }); +}); + +describe('setLastServerVersionCheck', () => { + it('handle not found database', async () => { + const {error} = await setLastServerVersionCheck('foo'); + expect(error).toBeDefined(); + }); + + it('base case', async () => { + const {error} = await setLastServerVersionCheck(serverUrl); + expect(error).toBeUndefined(); + }); +}); + +describe('setGlobalThreadsTab', () => { + it('handle not found database', async () => { + const {error} = await setGlobalThreadsTab('foo', 'all'); + expect(error).toBeDefined(); + }); + + it('base case', async () => { + const {error} = await setGlobalThreadsTab(serverUrl, 'all'); + expect(error).toBeUndefined(); + }); +}); + +describe('dismissAnnouncement', () => { + it('handle not found database', async () => { + const {error} = await dismissAnnouncement('foo', 'text'); + expect(error).toBeDefined(); + }); + + it('base case', async () => { + const {error} = await dismissAnnouncement(serverUrl, 'text'); + expect(error).toBeUndefined(); + }); +}); + diff --git a/app/actions/local/systems.ts b/app/actions/local/systems.ts index 3dd0a20da3d..ff3707a3662 100644 --- a/app/actions/local/systems.ts +++ b/app/actions/local/systems.ts @@ -38,11 +38,12 @@ export async function storeConfigAndLicense(serverUrl: string, config: ClientCon await operator.handleSystem({systems, prepareRecordsOnly: false}); } - await storeConfig(serverUrl, config); + return await storeConfig(serverUrl, config); } } catch (error) { logError('An error occurred while saving config & license', error); } + return []; } export async function storeConfig(serverUrl: string, config: ClientConfig | undefined, prepareRecordsOnly = false) { @@ -274,8 +275,10 @@ export async function setLastServerVersionCheck(serverUrl: string, reset = false }], prepareRecordsOnly: false, }); + return {error: undefined}; } catch (error) { logError('setLastServerVersionCheck', error); + return {error}; } } @@ -289,8 +292,10 @@ export async function setGlobalThreadsTab(serverUrl: string, globalThreadsTab: G }], prepareRecordsOnly: false, }); + return {error: undefined}; } catch (error) { logError('setGlobalThreadsTab', error); + return {error}; } } @@ -298,7 +303,9 @@ export async function dismissAnnouncement(serverUrl: string, announcementText: s try { const {operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.LAST_DISMISSED_BANNER, value: announcementText}], prepareRecordsOnly: false}); + return {error: undefined}; } catch (error) { logError('An error occurred while dismissing an announcement', error); + return {error}; } } diff --git a/app/actions/local/team.test.ts b/app/actions/local/team.test.ts new file mode 100644 index 00000000000..3790cd2cfdd --- /dev/null +++ b/app/actions/local/team.test.ts @@ -0,0 +1,400 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. +import ServerDataOperator from '@app/database/operator/server_data_operator'; +import DatabaseManager from '@database/manager'; +import {getMyTeamById, getTeamById, getTeamSearchHistoryById, prepareDeleteTeam, removeTeamFromTeamHistory} from '@queries/servers/team'; +import {logError} from '@utils/log'; + +import {queryTeamSearchHistoryByTeamId} from '../../queries/servers/team'; + +import {addSearchToTeamSearchHistory, removeSearchFromTeamSearchHistory, removeUserFromTeam, MAX_TEAM_SEARCHES} from './team'; + +jest.mock('@database/manager'); +jest.mock('@queries/servers/team'); +jest.mock('@utils/log'); + +let operator: ServerDataOperator; +const serverUrl = 'baseHandler.test.com'; +const teamId = 'teamId123'; +const database = { + write: jest.fn(async (callback) => callback()), +}; + +describe('removeSearchFromTeamSearchHistory', () => { + const searchId = 'searchId123'; + + beforeEach(async () => { + jest.clearAllMocks(); + await DatabaseManager.init([serverUrl]); + operator = DatabaseManager.serverDatabases[serverUrl]!.operator; + DatabaseManager.getServerDatabaseAndOperator = jest.fn(); + (DatabaseManager.getServerDatabaseAndOperator as jest.Mock).mockReturnValue({ + database, + operator, + }); + }); + + afterEach(async () => { + await DatabaseManager.destroyServerDatabase(serverUrl); + }); + + it('should remove search from team search history successfully', async () => { + const teamSearch = { + destroyPermanently: jest.fn(), + }; + (getTeamSearchHistoryById as jest.Mock).mockResolvedValue(teamSearch); + + const result = await removeSearchFromTeamSearchHistory(serverUrl, searchId); + + expect(getTeamSearchHistoryById).toHaveBeenCalledWith(database, searchId); + expect(database.write).toHaveBeenCalledWith(expect.any(Function)); + expect(database.write).toHaveBeenCalledTimes(1); + expect(teamSearch.destroyPermanently).toHaveBeenCalledTimes(1); + expect(result).toEqual({teamSearch}); + }); + + it('should handle case when team search history is not found', async () => { + (getTeamSearchHistoryById as jest.Mock).mockResolvedValue(undefined); + + const result = await removeSearchFromTeamSearchHistory(serverUrl, searchId); + + expect(getTeamSearchHistoryById).toHaveBeenCalledWith(database, searchId); + expect(database.write).not.toHaveBeenCalled(); + expect(result).toEqual({teamSearch: undefined}); + }); + + it('should handle errors and log them', async () => { + const error = new Error('Test error'); + (getTeamSearchHistoryById as jest.Mock).mockRejectedValue(error); + + const result = await removeSearchFromTeamSearchHistory(serverUrl, searchId); + + expect(getTeamSearchHistoryById).toHaveBeenCalledWith(database, searchId); + expect(logError).toHaveBeenCalledWith('Failed removeSearchFromTeamSearchHistory', error); + expect(result).toEqual({error}); + }); + + it('should handle database write errors', async () => { + const teamSearch = { + destroyPermanently: jest.fn(), + }; + const writeError = new Error('Database write error'); + (getTeamSearchHistoryById as jest.Mock).mockResolvedValue(teamSearch); + (database.write as jest.Mock).mockRejectedValue(writeError); + + const result = await removeSearchFromTeamSearchHistory(serverUrl, searchId); + + expect(getTeamSearchHistoryById).toHaveBeenCalledWith(database, searchId); + expect(database.write).toHaveBeenCalledWith(expect.any(Function)); + expect(logError).toHaveBeenCalledWith('Failed removeSearchFromTeamSearchHistory', writeError); + expect(result).toEqual({error: writeError}); + }); +}); + +describe('removeUserFromTeam', () => { + beforeEach(async () => { + jest.clearAllMocks(); + await DatabaseManager.init([serverUrl]); + operator = DatabaseManager.serverDatabases[serverUrl]!.operator; + DatabaseManager.getServerDatabaseAndOperator = jest.fn(); + (DatabaseManager.getServerDatabaseAndOperator as jest.Mock).mockReturnValue({ + database, + operator, + }); + + operator.batchRecords = jest.fn(); + }); + + afterEach(async () => { + await DatabaseManager.destroyServerDatabase(serverUrl); + }); + + it('should remove user from team successfully', async () => { + const myTeam = {id: 'myTeamId'}; + const team = {id: 'myTeamId'}; + const preparedModels = [ + {id: 'model1', _preparedState: 'markAsDeleted'}, + {id: 'model2', _preparedState: 'markAsDeleted'}, + ]; + const systemModels = [ + {id: 'systemModel1', _preparedState: 'update', value: []}, + ]; + + (getMyTeamById as jest.Mock).mockResolvedValue(myTeam); + (getTeamById as jest.Mock).mockResolvedValue(team); + (prepareDeleteTeam as jest.Mock).mockResolvedValue(preparedModels); + (removeTeamFromTeamHistory as jest.Mock).mockResolvedValue(systemModels); + + const result = await removeUserFromTeam(serverUrl, team.id); + + expect(getMyTeamById).toHaveBeenCalledWith(database, team.id); + expect(getTeamById).toHaveBeenCalledWith(database, myTeam.id); + expect(prepareDeleteTeam).toHaveBeenCalledWith(team); + expect(removeTeamFromTeamHistory).toHaveBeenCalledWith(operator, team.id, true); + expect(operator.batchRecords).toHaveBeenCalledWith(preparedModels, 'removeUserFromTeam'); + + // Verify team id is not present + systemModels.forEach((model) => { + expect(model.value).not.toContain(team.id); + }); + + // Check if preparedModels contain systemModels + expect(preparedModels).toEqual(expect.arrayContaining(systemModels)); + + expect(result).toEqual({error: undefined}); + }); + + it('should handle errors during database write', async () => { + const myTeam = {id: 'myTeamId'}; + const team = {id: 'teamId'}; + const preparedModels = [ + {id: 'model1', _preparedState: 'markAsDeleted'}, + {id: 'model2', _preparedState: 'markAsDeleted'}, + ]; + const systemModels = [ + {id: 'systemModel1', _preparedState: 'update', value: [team.id]}, + ]; + const writeError = new Error('Database write error'); + + (getMyTeamById as jest.Mock).mockResolvedValue(myTeam); + (getTeamById as jest.Mock).mockResolvedValue(team); + (prepareDeleteTeam as jest.Mock).mockResolvedValue(preparedModels); + (removeTeamFromTeamHistory as jest.Mock).mockResolvedValue(systemModels); + (operator.batchRecords as jest.Mock).mockRejectedValue(writeError); + + const result = await removeUserFromTeam(serverUrl, teamId); + + expect(getMyTeamById).toHaveBeenCalledWith(database, teamId); + expect(getTeamById).toHaveBeenCalledWith(database, myTeam.id); + expect(prepareDeleteTeam).toHaveBeenCalledWith(team); + expect(removeTeamFromTeamHistory).toHaveBeenCalledWith(operator, team.id, true); + expect(operator.batchRecords).toHaveBeenCalledWith(preparedModels, 'removeUserFromTeam'); + expect(logError).toHaveBeenCalledWith('Failed removeUserFromTeam', writeError); + + // Verify team id is present + systemModels.forEach((model) => { + expect(model.value).toContain(team.id); + }); + + // Check if preparedModels contain systemModels + expect(preparedModels).toEqual(expect.arrayContaining(systemModels)); + + expect(result).toEqual({error: writeError}); + }); + + it('should handle case when myTeam is not found', async () => { + (getMyTeamById as jest.Mock).mockResolvedValue(null); + + const result = await removeUserFromTeam(serverUrl, teamId); + + expect(getMyTeamById).toHaveBeenCalledWith(database, teamId); + expect(getTeamById).not.toHaveBeenCalled(); + expect(prepareDeleteTeam).not.toHaveBeenCalled(); + expect(removeTeamFromTeamHistory).not.toHaveBeenCalled(); + expect(operator.batchRecords).not.toHaveBeenCalled(); + expect(result).toEqual({error: undefined}); + }); + + it('should handle case when team is not found', async () => { + const myTeam = {id: 'myTeamId'}; + + (getMyTeamById as jest.Mock).mockResolvedValue(myTeam); + (getTeamById as jest.Mock).mockResolvedValue(null); + + const result = await removeUserFromTeam(serverUrl, teamId); + + expect(getMyTeamById).toHaveBeenCalledWith(database, teamId); + expect(getTeamById).toHaveBeenCalledWith(database, myTeam.id); + expect(result.error).toEqual(new Error('Team not found')); + }); + + it('should handle errors and log them', async () => { + const error = new Error('Test error'); + (DatabaseManager.getServerDatabaseAndOperator as jest.Mock).mockImplementation(() => { + throw error; + }); + + const result = await removeUserFromTeam(serverUrl, teamId); + + expect(DatabaseManager.getServerDatabaseAndOperator).toHaveBeenCalled(); + expect(logError).toHaveBeenCalledWith('Failed removeUserFromTeam', error); + expect(result).toEqual({error}); + }); + + it('should handle when removeTeamFromTeamHistory returns undefined or empty array', async () => { + const myTeam = {id: 'myTeamId'}; + const team = {id: 'teamId'}; + const preparedModels = [ + {id: 'model1', _preparedState: 'markAsDeleted'}, + {id: 'model2', _preparedState: 'markAsDeleted'}, + ]; + + (getMyTeamById as jest.Mock).mockResolvedValue(myTeam); + (getTeamById as jest.Mock).mockResolvedValue(team); + (prepareDeleteTeam as jest.Mock).mockResolvedValue(preparedModels); + (removeTeamFromTeamHistory as jest.Mock).mockResolvedValue(undefined); + + const result = await removeUserFromTeam(serverUrl, teamId); + + expect(getMyTeamById).toHaveBeenCalledWith(database, teamId); + expect(getTeamById).toHaveBeenCalledWith(database, myTeam.id); + expect(prepareDeleteTeam).toHaveBeenCalledWith(team); + expect(removeTeamFromTeamHistory).toHaveBeenCalledWith(operator, team.id, true); + expect(operator.batchRecords).toHaveBeenCalledWith(preparedModels, 'removeUserFromTeam'); + expect(result).toEqual({error: undefined}); + }); + + it('should handle when models length is 0', async () => { + const myTeam = {id: 'myTeamId'}; + const team = {id: 'teamId'}; + const models: never[] = []; + + (getMyTeamById as jest.Mock).mockResolvedValue(myTeam); + (getTeamById as jest.Mock).mockResolvedValue(team); + (prepareDeleteTeam as jest.Mock).mockResolvedValue([]); + (removeTeamFromTeamHistory as jest.Mock).mockResolvedValue(models); + + const result = await removeUserFromTeam(serverUrl, teamId); + + expect(getMyTeamById).toHaveBeenCalledWith(database, teamId); + expect(getTeamById).toHaveBeenCalledWith(database, myTeam.id); + expect(prepareDeleteTeam).toHaveBeenCalledWith(team); + expect(removeTeamFromTeamHistory).toHaveBeenCalledWith(operator, team.id, true); + expect(operator.batchRecords).not.toHaveBeenCalled(); + expect(result).toEqual({error: undefined}); + }); +}); + +describe('addSearchToTeamSearchHistory', () => { + const terms = 'search terms'; + + beforeEach(async () => { + jest.clearAllMocks(); + await DatabaseManager.init([serverUrl]); + operator = DatabaseManager.serverDatabases[serverUrl]!.operator; + DatabaseManager.getServerDatabaseAndOperator = jest.fn(); + (DatabaseManager.getServerDatabaseAndOperator as jest.Mock).mockReturnValue({ + database, + operator, + }); + + operator.handleTeamSearchHistory = jest.fn(); + operator.batchRecords = jest.fn(); + jest.spyOn(Date, 'now').mockImplementation(() => 1724657520366); + }); + + afterEach(async () => { + await DatabaseManager.destroyServerDatabase(serverUrl); + }); + + it('should add search to team search history successfully', async () => { + const newSearch = { + created_at: Date.now(), + display_term: terms, + term: terms, + team_id: teamId, + }; + const searchModel = {id: 'searchModelId', _raw: {_changed: 'created_at'}}; + (operator.handleTeamSearchHistory as jest.Mock).mockResolvedValue([searchModel]); + + const result = await addSearchToTeamSearchHistory(serverUrl, teamId, terms); + + expect(operator.handleTeamSearchHistory).toHaveBeenCalledWith({ + teamSearchHistories: [newSearch], + prepareRecordsOnly: true, + }); + expect(operator.batchRecords).toHaveBeenCalledWith([searchModel], 'addSearchToTeamHistory'); + expect(result).toEqual({searchModel}); + }); + + it('should delete the oldest entry if team search history exceeds the limit', async () => { + const newSearch = { + created_at: Date.now(), + display_term: terms, + term: terms, + team_id: teamId, + }; + const searchModel = {id: 'searchModelId', _raw: {_changed: ''}}; + const oldSearchModel = {id: 'oldSearchModelId', prepareDestroyPermanently: jest.fn().mockReturnValue(undefined)}; + const teamSearchHistory = new Array(MAX_TEAM_SEARCHES + 1).fill(oldSearchModel); + + (operator.handleTeamSearchHistory as jest.Mock).mockResolvedValue([searchModel]); + (queryTeamSearchHistoryByTeamId as jest.Mock).mockReturnValue({ + fetch: jest.fn().mockResolvedValue(teamSearchHistory), + }); + + const result = await addSearchToTeamSearchHistory(serverUrl, teamId, terms); + + expect(operator.handleTeamSearchHistory).toHaveBeenCalledWith({ + teamSearchHistories: [newSearch], + prepareRecordsOnly: true, + }); + expect(queryTeamSearchHistoryByTeamId).toHaveBeenCalledWith(database, teamId); + expect(oldSearchModel.prepareDestroyPermanently).toHaveBeenCalledTimes(1); + expect(operator.batchRecords).toHaveBeenCalledWith([searchModel], 'addSearchToTeamHistory'); + expect(result).toEqual({searchModel}); + }); + + it('should handle case when searchModel._raw._changed is "created_at"', async () => { + const newSearch = { + created_at: Date.now(), + display_term: terms, + term: terms, + team_id: teamId, + }; + const searchModel = {id: 'searchModelId', _raw: {_changed: 'created_at'}}; + (operator.handleTeamSearchHistory as jest.Mock).mockResolvedValue([searchModel]); + + const result = await addSearchToTeamSearchHistory(serverUrl, teamId, terms); + + expect(operator.handleTeamSearchHistory).toHaveBeenCalledWith({ + teamSearchHistories: [newSearch], + prepareRecordsOnly: true, + }); + expect(queryTeamSearchHistoryByTeamId).not.toHaveBeenCalled(); + expect(operator.batchRecords).toHaveBeenCalledWith([searchModel], 'addSearchToTeamHistory'); + expect(result).toEqual({searchModel}); + }); + + it('should handle case when teamSearchHistory length is less than or equal to MAX_TEAM_SEARCHES', async () => { + const newSearch = { + created_at: Date.now(), + display_term: terms, + term: terms, + team_id: teamId, + }; + const searchModel = {id: 'searchModelId', _raw: {_changed: ''}}; + const oldSearchModel = {id: 'oldSearchModelId', prepareDestroyPermanently: jest.fn().mockReturnValue(undefined)}; + const teamSearchHistory = new Array(MAX_TEAM_SEARCHES).fill(searchModel); + + (operator.handleTeamSearchHistory as jest.Mock).mockResolvedValue([searchModel]); + (queryTeamSearchHistoryByTeamId as jest.Mock).mockReturnValue({ + fetch: jest.fn().mockResolvedValue(teamSearchHistory), + }); + + const result = await addSearchToTeamSearchHistory(serverUrl, teamId, terms); + + expect(operator.handleTeamSearchHistory).toHaveBeenCalledWith({ + teamSearchHistories: [newSearch], + prepareRecordsOnly: true, + }); + expect(queryTeamSearchHistoryByTeamId).toHaveBeenCalledWith(database, teamId); + expect(operator.batchRecords).toHaveBeenCalledWith([searchModel], 'addSearchToTeamHistory'); + expect(result).toEqual({searchModel}); + expect(oldSearchModel.prepareDestroyPermanently).not.toHaveBeenCalled(); + }); + + it('should handle errors and log them', async () => { + const error = new Error('Test error'); + (operator.handleTeamSearchHistory as jest.Mock).mockRejectedValue(error); + + const result = await addSearchToTeamSearchHistory(serverUrl, teamId, terms); + + expect(operator.handleTeamSearchHistory).toHaveBeenCalledWith({ + teamSearchHistories: [{created_at: expect.any(Number), display_term: terms, term: terms, team_id: teamId}], + prepareRecordsOnly: true, + }); + expect(logError).toHaveBeenCalledWith('Failed addSearchToTeamSearchHistory', error); + expect(result).toEqual({error}); + }); +}); diff --git a/app/actions/local/team.ts b/app/actions/local/team.ts index bfcd8ae5bd6..c620e140a30 100644 --- a/app/actions/local/team.ts +++ b/app/actions/local/team.ts @@ -7,6 +7,8 @@ import {logError} from '@utils/log'; import type Model from '@nozbe/watermelondb/Model'; +export const MAX_TEAM_SEARCHES = 15; + export async function removeUserFromTeam(serverUrl: string, teamId: string) { try { const {database, operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); @@ -34,7 +36,6 @@ export async function removeUserFromTeam(serverUrl: string, teamId: string) { } export async function addSearchToTeamSearchHistory(serverUrl: string, teamId: string, terms: string) { - const MAX_TEAM_SEARCHES = 15; try { const {database, operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); const newSearch: TeamSearchHistory = { diff --git a/app/actions/local/thread.test.ts b/app/actions/local/thread.test.ts new file mode 100644 index 00000000000..a8c44d5510e --- /dev/null +++ b/app/actions/local/thread.test.ts @@ -0,0 +1,296 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import {ActionType} from '@app/constants'; +import {SYSTEM_IDENTIFIERS} from '@constants/database'; +import Preferences from '@constants/preferences'; +import DatabaseManager from '@database/manager'; +import EphemeralStore from '@store/ephemeral_store'; +import TestHelper from '@test/test_helper'; + +import { + switchToGlobalThreads, + switchToThread, + createThreadFromNewPost, + processReceivedThreads, + markTeamThreadsAsRead, + markThreadAsViewed, + updateThread, + updateTeamThreadsSync, +} from './thread'; + +import type ServerDataOperator from '@database/operator/server_data_operator'; + +const serverUrl = 'baseHandler.test.com'; +let operator: ServerDataOperator; + +beforeEach(async () => { + await DatabaseManager.init([serverUrl]); + operator = DatabaseManager.serverDatabases[serverUrl]!.operator; +}); + +afterEach(async () => { + await DatabaseManager.destroyServerDatabase(serverUrl); +}); + +jest.mock('@store/navigation_store', () => { + const original = jest.requireActual('@store/navigation_store'); + return { + ...original, + waitUntilScreenIsTop: jest.fn(() => Promise.resolve()), + }; +}); + +const teamId = 'tId1'; +const team: Team = { + id: teamId, +} as Team; +const channelId = 'channelid1'; +const channel: Channel = { + id: channelId, + team_id: teamId, + total_msg_count: 0, +} as Channel; +const user: UserProfile = { + id: 'userid', + username: 'username', + roles: '', +} as UserProfile; +const user2: UserProfile = { + id: 'userid2', + username: 'username2', + first_name: 'first', + last_name: 'last', + roles: '', +} as UserProfile; + +const rootPost = {...TestHelper.fakePost(channelId, user.id), id: 'rootpostid', create_at: 1}; +const threads = [ + { + id: rootPost.id, + reply_count: 0, + last_reply_at: 123, + last_viewed_at: 123, + participants: [{ + id: user.id, + }], + is_following: true, + unread_replies: 0, + unread_mentions: 0, + lastFetchedAt: 0, + }, +] as ThreadWithLastFetchedAt[]; + +describe('switchToGlobalThreads', () => { + it('handle not found database', async () => { + const {models, error} = await switchToGlobalThreads('foo', undefined, false); + expect(models).toBeUndefined(); + expect(error).toBeDefined(); + }); + + it('handle no team', async () => { + const {models, error} = await switchToGlobalThreads(serverUrl, undefined, false); + expect(models).toBeUndefined(); + expect(error).toBeDefined(); + expect((error as Error).message).toBe('no team to switch to'); + }); + + it('base case', async () => { + await operator.handleTeam({teams: [team], prepareRecordsOnly: false}); + await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_TEAM_ID, value: teamId}], prepareRecordsOnly: false}); + + const {models, error} = await switchToGlobalThreads(serverUrl, undefined, false); + expect(error).toBeUndefined(); + expect(models).toBeDefined(); + expect(models?.length).toBe(1); // history + }); +}); + +describe('switchToThread', () => { + it('handle not found database', async () => { + const {error} = await switchToThread('foo', '', false); + expect(error).toBeDefined(); + }); + + it('handle no user', async () => { + const {error} = await switchToThread(serverUrl, '', false); + expect((error as Error).message).toBe('User not found'); + }); + + it('handle no post', async () => { + await operator.handleUsers({users: [user], prepareRecordsOnly: false}); + await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_USER_ID, value: user.id}], prepareRecordsOnly: false}); + + const {error} = await switchToThread(serverUrl, '', false); + expect((error as Error).message).toBe('Post not found'); + }); + + it('handle no channel', async () => { + await operator.handleUsers({users: [user], prepareRecordsOnly: false}); + await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_USER_ID, value: user.id}], prepareRecordsOnly: false}); + const post = {...TestHelper.fakePost(channelId, user2.id), id: 'postid', create_at: 1}; + await operator.handlePosts({ + actionType: ActionType.POSTS.RECEIVED_IN_CHANNEL, + order: [post.id], + posts: [post], + prepareRecordsOnly: false, + }); + + const {error} = await switchToThread(serverUrl, post.id, false); + expect((error as Error).message).toBe('Channel not found'); + }); + + it('base case', async () => { + EphemeralStore.theme = Preferences.THEMES.denim; + await operator.handleUsers({users: [user, user2], prepareRecordsOnly: false}); + await operator.handleTeam({teams: [team], prepareRecordsOnly: false}); + await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_TEAM_ID, value: 'teamid2'}, {id: SYSTEM_IDENTIFIERS.CURRENT_USER_ID, value: user.id}], prepareRecordsOnly: false}); + await operator.handleChannel({channels: [channel], prepareRecordsOnly: false}); + const post = {...TestHelper.fakePost(channelId, user2.id), id: 'postid', create_at: 1}; + await operator.handlePosts({ + actionType: ActionType.POSTS.RECEIVED_IN_CHANNEL, + order: [post.id], + posts: [post], + prepareRecordsOnly: false, + }); + + const {error} = await switchToThread(serverUrl, post.id, true); + expect(error).toBeUndefined(); + }); +}); + +describe('createThreadFromNewPost', () => { + it('handle not found database', async () => { + const {models, error} = await createThreadFromNewPost('foo', {} as Post, false); + expect(models).toBeUndefined(); + expect(error).toBeDefined(); + }); + + it('base case', async () => { + await operator.handleUsers({users: [user, user2], prepareRecordsOnly: false}); + await operator.handleThreads({threads, prepareRecordsOnly: false, teamId: team.id}); + const post = {...TestHelper.fakePost(channelId, user2.id), id: 'postid', create_at: 1, root_id: rootPost.id}; + + const {models, error} = await createThreadFromNewPost(serverUrl, post, false); + expect(error).toBeUndefined(); + expect(models).toBeDefined(); + expect(models?.length).toBe(2); // thread, thread participant + }); + + it('base case - no root post', async () => { + await operator.handleUsers({users: [user2], prepareRecordsOnly: false}); + const post = {...TestHelper.fakePost(channelId, user2.id), id: 'postid', create_at: 1}; + + const {models, error} = await createThreadFromNewPost(serverUrl, post, false); + expect(error).toBeUndefined(); + expect(models).toBeDefined(); + expect(models?.length).toBe(1); // thread + }); +}); + +describe('processReceivedThreads', () => { + it('handle not found database', async () => { + const {models, error} = await processReceivedThreads('foo', [], '', false); + expect(models).toBeUndefined(); + expect(error).toBeDefined(); + }); + + it('base case', async () => { + await operator.handleTeam({teams: [team], prepareRecordsOnly: false}); + await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_TEAM_ID, value: teamId}], prepareRecordsOnly: false}); + await operator.handleUsers({users: [user], prepareRecordsOnly: false}); + const thread = [ + { + id: rootPost.id, + reply_count: 0, + last_reply_at: 123, + last_viewed_at: 123, + participants: [{ + id: user.id, + }], + is_following: true, + unread_replies: 0, + unread_mentions: 0, + post: rootPost, + }, + ] as Thread[]; + + const {models, error} = await processReceivedThreads(serverUrl, thread, team.id, false); + expect(error).toBeUndefined(); + expect(models).toBeDefined(); + expect(models?.length).toBe(4); // post, thread, thread participant, thread in team + }); +}); + +describe('markTeamThreadsAsRead', () => { + it('handle not found database', async () => { + const {models, error} = await markTeamThreadsAsRead('foo', '', false); + expect(models).toBeUndefined(); + expect(error).toBeDefined(); + }); + + it('base case', async () => { + const {models, error} = await markTeamThreadsAsRead(serverUrl, team.id, false); + expect(error).toBeUndefined(); + expect(models).toBeDefined(); + expect(models?.length).toBe(0); + }); +}); + +describe('markThreadAsViewed', () => { + it('handle not found database', async () => { + const {model, error} = await markThreadAsViewed('foo', '', false); + expect(model).toBeUndefined(); + expect(error).toBeDefined(); + }); + + it('handle no thread', async () => { + const {model, error} = await markThreadAsViewed(serverUrl, '', false); + expect(model).toBeUndefined(); + expect(error).toBeDefined(); + expect(error).toBe('Thread not found'); + }); + + it('base case', async () => { + await operator.handleThreads({threads, prepareRecordsOnly: false, teamId: team.id}); + const {model, error} = await markThreadAsViewed(serverUrl, rootPost.id, false); + expect(error).toBeUndefined(); + expect(model).toBeDefined(); + }); +}); + +describe('updateThread', () => { + it('handle not found database', async () => { + const {model, error} = await updateThread('foo', '', {}, false); + expect(model).toBeUndefined(); + expect(error).toBeDefined(); + }); + + it('handle no thread', async () => { + const {model, error} = await updateThread(serverUrl, '', {}, false); + expect(model).toBeUndefined(); + expect(error).toBeDefined(); + expect((error as Error).message).toBe('Thread not found'); + }); + + it('base case', async () => { + await operator.handleThreads({threads, prepareRecordsOnly: false, teamId: team.id}); + const {model, error} = await updateThread(serverUrl, rootPost.id, threads[0], false); + expect(error).toBeUndefined(); + expect(model).toBeDefined(); + }); +}); + +describe('updateTeamThreadsSync', () => { + it('handle not found database', async () => { + const {models, error} = await updateTeamThreadsSync('foo', {} as TeamThreadsSync, false); + expect(models).toBeUndefined(); + expect(error).toBeDefined(); + }); + + it('base case', async () => { + const {models, error} = await updateTeamThreadsSync(serverUrl, {id: 'id1', earliest: 1, latest: 2}, false); + expect(error).toBeUndefined(); + expect(models).toBeDefined(); + }); +}); diff --git a/app/actions/local/user.test.ts b/app/actions/local/user.test.ts new file mode 100644 index 00000000000..bb0cca55847 --- /dev/null +++ b/app/actions/local/user.test.ts @@ -0,0 +1,131 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import {SYSTEM_IDENTIFIERS} from '@app/constants/database'; +import DatabaseManager from '@database/manager'; + +import { + setCurrentUserStatus, + updateLocalCustomStatus, + updateRecentCustomStatuses, + updateLocalUser, + storeProfile, +} from './user'; + +import type ServerDataOperator from '@database/operator/server_data_operator'; +import type SystemModel from '@typings/database/models/servers/system'; +import type UserModel from '@typings/database/models/servers/user'; + +const serverUrl = 'baseHandler.test.com'; +let operator: ServerDataOperator; + +jest.mock('@init/credentials', () => { + const original = jest.requireActual('@init/credentials'); + return { + ...original, + getServerCredentials: jest.fn(async (url: string) => ({serverUrl: url})), + }; +}); + +const user: UserProfile = { + id: 'userid', + username: 'username', + roles: '', +} as UserProfile; + +beforeEach(async () => { + await DatabaseManager.init([serverUrl]); + operator = DatabaseManager.serverDatabases[serverUrl]!.operator; +}); + +afterEach(async () => { + await DatabaseManager.destroyServerDatabase(serverUrl); +}); + +describe('setCurrentUserStatus', () => { + it('handle not found database', async () => { + const result = await setCurrentUserStatus('foo', '') as {error: unknown}; + expect(result?.error).toBeDefined(); + }); + + it('handle no user', async () => { + const result = await setCurrentUserStatus(serverUrl, '') as {error: unknown}; + expect(result?.error).toBeDefined(); + expect((result?.error as Error).message).toBe(`No current user for ${serverUrl}`); + }); + + it('base case', async () => { + await operator.handleUsers({users: [user], prepareRecordsOnly: false}); + await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_USER_ID, value: user.id}], prepareRecordsOnly: false}); + const result = await setCurrentUserStatus(serverUrl, 'away'); + expect(result).toBeNull(); + }); +}); + +describe('updateLocalCustomStatus', () => { + it('handle not found database', async () => { + const result = await updateLocalCustomStatus('foo', {} as UserModel) as {error: unknown}; + expect(result?.error).toBeDefined(); + }); + + it('base case', async () => { + const userModels = await operator.handleUsers({users: [user], prepareRecordsOnly: false}); + await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_USER_ID, value: user.id}], prepareRecordsOnly: false}); + const result = await updateLocalCustomStatus(serverUrl, userModels[0], {text: 'customstatus'}) as {}; + expect(result).toBeDefined(); + expect(result).not.toHaveProperty('error'); + }); +}); + +describe('updateRecentCustomStatuses', () => { + it('handle not found database', async () => { + const result = await updateRecentCustomStatuses('foo', {text: 'customstatus'}) as {error: unknown}; + expect(result?.error).toBeDefined(); + }); + + it('base case', async () => { + const result = await updateRecentCustomStatuses(serverUrl, {text: 'customstatus'}) as SystemModel[]; + expect(result).toBeDefined(); + expect(result.length).toBe(1); // system + }); +}); + +describe('updateLocalUser', () => { + it('handle not found database', async () => { + const {error} = await updateLocalUser('foo', user); + expect(error).toBeDefined(); + }); + + it('base case', async () => { + await operator.handleUsers({users: [user], prepareRecordsOnly: false}); + await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_USER_ID, value: user.id}], prepareRecordsOnly: false}); + const {user: userModel} = await updateLocalUser(serverUrl, user); + expect(userModel).toBeDefined(); + }); + + it('base case with user id', async () => { + await operator.handleUsers({users: [user], prepareRecordsOnly: false}); + await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_USER_ID, value: user.id}], prepareRecordsOnly: false}); + const {user: userModel} = await updateLocalUser(serverUrl, {status: 'away'}, user.id); + expect(userModel).toBeDefined(); + }); +}); + +describe('storeProfile', () => { + it('handle not found database', async () => { + const {error} = await storeProfile('foo', user); + expect(error).toBeDefined(); + }); + + it('base case - user exists', async () => { + await operator.handleUsers({users: [user], prepareRecordsOnly: false}); + const {user: userModel} = await storeProfile(serverUrl, user); + expect(userModel).toBeDefined(); + }); + + it('base case - no user', async () => { + const {user: userModel} = await storeProfile(serverUrl, user); + expect(userModel).toBeDefined(); + }); +}); + diff --git a/app/actions/remote/channel.test.ts b/app/actions/remote/channel.test.ts new file mode 100644 index 00000000000..4a86cb6b8b0 --- /dev/null +++ b/app/actions/remote/channel.test.ts @@ -0,0 +1,762 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +/* eslint-disable max-lines */ + +import {createIntl} from 'react-intl'; + +import {DeepLink} from '@constants'; +import {SYSTEM_IDENTIFIERS} from '@constants/database'; +import DatabaseManager from '@database/manager'; +import NetworkManager from '@managers/network_manager'; + +import { + removeMemberFromChannel, + fetchChannelMembersByIds, + updateChannelMemberSchemeRoles, + fetchMemberInChannel, + fetchChannelMemberships, + addMembersToChannel, + fetchChannelByName, + createChannel, + patchChannel, + leaveChannel, + fetchChannelCreator, + fetchMyChannelsForTeam, + fetchMyChannel, + joinChannel, + joinChannelIfNeeded, + markChannelAsRead, + unsetActiveChannelOnServer, + switchToChannelByName, + goToNPSChannel, + fetchMissingDirectChannelsInfo, + fetchDirectChannelsInfo, + createDirectChannel, + fetchChannels, + makeDirectChannel, + fetchArchivedChannels, + createGroupChannel, + fetchSharedChannels, + makeGroupChannel, + getChannelMemberCountsByGroup, + getChannelTimezones, + switchToChannelById, + switchToPenultimateChannel, + switchToLastChannel, + searchChannels, + fetchChannelById, + searchAllChannels, + updateChannelNotifyProps, + toggleMuteChannel, + archiveChannel, + unarchiveChannel, + convertChannelToPrivate, + handleKickFromChannel, + fetchGroupMessageMembersCommonTeams, + convertGroupMessageToPrivateChannel, +} from './channel'; + +import type ServerDataOperator from '@database/operator/server_data_operator'; +import type ChannelModel from '@typings/database/models/servers/channel'; + +const serverUrl = 'baseHandler.test.com'; +let operator: ServerDataOperator; + +const user: UserProfile = { + id: 'userid', + username: 'username', + roles: '', +} as UserProfile; + +let mockIsTablet: jest.Mock; +jest.mock('@utils/helpers', () => { + const original = jest.requireActual('@utils/helpers'); + mockIsTablet = jest.fn(() => false); + return { + ...original, + isTablet: mockIsTablet, + }; +}); + +let mockGetActiveServer: jest.Mock; +jest.mock('@queries/app/servers', () => { + const original = jest.requireActual('@queries/app/servers'); + mockGetActiveServer = jest.fn(() => false); + return { + ...original, + getActiveServer: mockGetActiveServer, + }; +}); + +const mockClient = { + removeFromChannel: jest.fn(), + getChannelMembersByIds: jest.fn((channelId: string, userIds: string[]) => userIds.map((uid) => ({user_id: uid, channel_id: channelId, roles: ''}))), + updateChannelMemberSchemeRoles: jest.fn(), + getMemberInChannel: jest.fn((channelId: string, userId: string) => ({id: userId + '-' + channelId, user_id: userId, channel_id: channelId, roles: ''})), + getChannel: jest.fn((channelId: string) => ({id: channelId, name: 'channel1', creatorId: user.id})), + getProfilesInChannel: jest.fn(() => ([user])), + addToChannel: jest.fn((channelId: string, userId: string) => ({id: userId + '-' + channelId, user_id: userId, channel_id: channelId, roles: ''})), + getProfilesByIds: jest.fn((userIds: string[]) => userIds.map((uid) => ({id: uid, username: 'u' + uid, roles: ''}))), + getChannelByName: jest.fn((teamId: string, channelName: string) => ({id: channelId, name: channelName, team_id: teamId})), + createChannel: jest.fn((channel: Channel) => ({...channel, id: channelId})), + getChannelMember: jest.fn((channelId: string, userId: string) => ({id: userId + '-' + channelId, user_id: userId, channel_id: channelId, roles: ''})), + patchChannel: jest.fn((channelId: string, channel: ChannelPatch) => ({...channel, id: channelId})), + getUser: jest.fn((userId: string) => ({...user, id: userId})), + getMyChannels: jest.fn((teamId: string) => ([{id: channelId, name: 'channel1', creatorId: user.id, team_id: teamId}])), + getMyChannelMembers: jest.fn(() => ([{id: user.id + '-' + channelId, user_id: user.id, channel_id: channelId, roles: ''}])), + getCategories: jest.fn((userId: string, teamId: string) => ({categories: [{id: 'categoryid', channel_id: [channelId], team_id: teamId}], order: ['categoryid']})), + viewMyChannel: jest.fn(), + getTeamByName: jest.fn((teamName: string) => ({id: teamId, name: teamName})), + getTeam: jest.fn((id: string) => ({id, name: 'teamname'})), + addToTeam: jest.fn((teamId: string, userId: string) => ({id: userId + '-' + teamId, user_id: userId, team_id: teamId, roles: ''})), + getUserByUsername: jest.fn((username: string) => ({...user, id: 'userid2', username})), + createDirectChannel: jest.fn((userId1: string, userId2: string) => ({id: userId1 + '__' + userId2, team_id: '', type: 'D', display_name: 'displayname'})), + getChannels: jest.fn((teamId: string) => ([{id: channelId, name: 'channel1', creatorId: user.id, team_id: teamId}])), + getArchivedChannels: jest.fn((teamId: string) => ([{id: channelId + 'old', name: 'channel1old', creatorId: user.id, team_id: teamId, delete_at: 1}])), + createGroupChannel: jest.fn(() => ({id: 'groupid', team_id: '', type: 'G', display_name: 'displayname'})), + getProfilesInGroupChannels: jest.fn(() => ({groupid: [user, {...user, id: 'userid2'}]})), + savePreferences: jest.fn(), + getRolesByNames: jest.fn((roles: string[]) => roles.map((r) => ({id: r, name: r} as Role))), + getSharedChannels: jest.fn((teamId: string) => ([{id: channelId + 'shared', name: 'channel1shared', creatorId: user.id, team_id: teamId, shared: true}])), + getChannelMemberCountsByGroup: jest.fn((channelId: string) => ({group_id: channelId, channel_member_count: 3, channel_member_timezones_count: 2})), + getChannelTimezones: jest.fn(() => ['est']), + autocompleteChannels: jest.fn((teamId: string) => ([{id: channelId, name: 'channel1', creatorId: user.id, team_id: teamId}])), + searchAllChannels: jest.fn(() => ([{id: channelId, name: 'channel1', creatorId: user.id, team_id: teamId}])), + updateChannelNotifyProps: jest.fn(), + deleteChannel: jest.fn(), + unarchiveChannel: jest.fn(), + convertChannelToPrivate: jest.fn(), + getGroupMessageMembersCommonTeams: jest.fn(() => ({id: teamId, name: 'teamname'})), + convertGroupMessageToPrivateChannel: jest.fn((channelId: string) => ({id: channelId, name: 'channel1', creatorId: user.id, type: 'P'})), +}; + +const teamId = 'teamid1'; +const channelId = 'channelid1'; + +const intl = createIntl({ + locale: 'en', + messages: {}, +}); + +beforeAll(() => { + // eslint-disable-next-line + // @ts-ignore + NetworkManager.getClient = () => mockClient; +}); + +beforeEach(async () => { + await DatabaseManager.init([serverUrl]); + operator = DatabaseManager.serverDatabases[serverUrl]!.operator; +}); + +afterEach(async () => { + await DatabaseManager.destroyServerDatabase(serverUrl); +}); + +describe('channelMember', () => { + it('removeMemberFromChannel - handle not found database', async () => { + const result = await removeMemberFromChannel('foo', '', '') as {error: unknown}; + expect(result?.error).toBeDefined(); + }); + + it('removeMemberFromChannel - base case', async () => { + const result = await removeMemberFromChannel(serverUrl, channelId, user.id); + expect(result).toBeDefined(); + }); + + it('fetchChannelMembersByIds - handle not found database', async () => { + const {members, error} = await fetchChannelMembersByIds('foo', '', []); + expect(members).toBeUndefined(); + expect(error).toBeDefined(); + }); + + it('fetchChannelMembersByIds - base case', async () => { + const {members, error} = await fetchChannelMembersByIds(serverUrl, channelId, [user.id]); + expect(error).toBeUndefined(); + expect(members).toBeDefined(); + expect(members?.length).toBe(1); + }); + + it('updateChannelMemberSchemeRoles - base case', async () => { + const result = await updateChannelMemberSchemeRoles(serverUrl, channelId, user.id, true, true); + expect(result).toBeDefined(); + }); + + it('fetchMemberInChannel - handle not found database', async () => { + const {member, error} = await fetchMemberInChannel('foo', '', ''); + expect(member).toBeUndefined(); + expect(error).toBeDefined(); + }); + + it('fetchMemberInChannel - base case', async () => { + await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_USER_ID, value: user.id}], prepareRecordsOnly: false}); + const {member, error} = await fetchMemberInChannel(serverUrl, channelId, user.id); + expect(error).toBeUndefined(); + expect(member).toBeDefined(); + }); + + it('fetchChannelMemberships - base case', async () => { + const {members, users} = await fetchChannelMemberships(serverUrl, channelId, {}); + expect(users).toBeDefined(); + expect(users.length).toBe(1); + expect(members).toBeDefined(); + expect(members.length).toBe(1); + }); + + it('addMembersToChannel - handle not found database', async () => { + const {channelMemberships, error} = await addMembersToChannel('foo', '', []); + expect(channelMemberships).toBeUndefined(); + expect(error).toBeDefined(); + }); + + it('addMembersToChannel - base case', async () => { + const {channelMemberships, error} = await addMembersToChannel(serverUrl, channelId, [user.id]); + expect(error).toBeUndefined(); + expect(channelMemberships).toBeDefined(); + expect(channelMemberships?.length).toBe(1); + }); + + it('getChannelMemberCountsByGroup - base case', async () => { + const {channelMemberCountsByGroup, error} = await getChannelMemberCountsByGroup(serverUrl, channelId, true); + expect(error).toBeUndefined(); + expect(channelMemberCountsByGroup).toBeDefined(); + }); + + it('updateChannelNotifyProps - handle not found database', async () => { + const {notifyProps, error} = await updateChannelNotifyProps('foo', '', {}); + expect(notifyProps).toBeUndefined(); + expect(error).toBeDefined(); + }); + + it('updateChannelNotifyProps - base case', async () => { + const {notifyProps, error} = await updateChannelNotifyProps(serverUrl, channelId, {}); + expect(error).toBeUndefined(); + expect(notifyProps).toBeDefined(); + }); + + it('toggleMuteChannel - handle not found database', async () => { + const {notifyProps, error} = await toggleMuteChannel('foo', ''); + expect(notifyProps).toBeUndefined(); + expect(error).toBeDefined(); + }); + + it('toggleMuteChannel - base case', async () => { + await operator.handleMyChannelSettings({ + settings: [{id: channelId, user_id: user.id, channel_id: channelId, roles: '', last_viewed_at: 1, last_update_at: 1, msg_count: 10, mention_count: 0, notify_props: {}}], + prepareRecordsOnly: false, + }); + + const {notifyProps, error} = await toggleMuteChannel(serverUrl, channelId, true); + expect(error).toBeUndefined(); + expect(notifyProps).toBeDefined(); + }); +}); + +describe('channel', () => { + it('fetchChannelByName - handle not found database', async () => { + const {channel, error} = await fetchChannelByName('foo', '', ''); + expect(channel).toBeUndefined(); + expect(error).toBeDefined(); + }); + + it('fetchChannelByName - base case', async () => { + const {channel, error} = await fetchChannelByName(serverUrl, channelId, 'channelname'); + expect(error).toBeUndefined(); + expect(channel).toBeDefined(); + }); + + it('createChannel - handle not found database', async () => { + const {channel, error} = await createChannel('foo', '', '', '', 'O'); + expect(channel).toBeUndefined(); + expect(error).toBeDefined(); + }); + + it('createChannel - base case', async () => { + await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_USER_ID, value: user.id}, {id: SYSTEM_IDENTIFIERS.CURRENT_TEAM_ID, value: teamId}], prepareRecordsOnly: false}); + const {channel, error} = await createChannel(serverUrl, 'channeldisplayname', 'purpose', 'header', 'O'); + expect(error).toBeUndefined(); + expect(channel).toBeDefined(); + }); + + it('patchChannel - handle not found database', async () => { + const {channel, error} = await patchChannel('foo', '', {}); + expect(channel).toBeUndefined(); + expect(error).toBeDefined(); + }); + + it('patchChannel - base case', async () => { + await operator.handleChannel({channels: [{ + id: channelId, + purpose: 'oldpurpose', + team_id: teamId, + total_msg_count: 0, + } as Channel], + prepareRecordsOnly: false}); + + const {channel, error} = await patchChannel(serverUrl, channelId, {name: 'channelname', display_name: 'Channel Name', purpose: 'purpose', header: 'header'}); + expect(error).toBeUndefined(); + expect(channel).toBeDefined(); + }); + + it('leaveChannel - handle not found database', async () => { + const {error} = await leaveChannel('foo', ''); + expect(error).toBeDefined(); + }); + + it('leaveChannel - no user', async () => { + const {error} = await leaveChannel(serverUrl, channelId); + expect(error).toBeDefined(); + expect(error).toBe('current user not found'); + }); + + it('leaveChannel - base case', async () => { + await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_USER_ID, value: user.id}, {id: SYSTEM_IDENTIFIERS.CURRENT_TEAM_ID, value: teamId}], prepareRecordsOnly: false}); + await operator.handleUsers({users: [user], prepareRecordsOnly: false}); + + const result = await leaveChannel(serverUrl, channelId); + expect(result.error).toBeUndefined(); + expect(result).toBeDefined(); + }); + + it('fetchChannelCreator - handle not found database', async () => { + const {user: fetchedUser, error} = await fetchChannelCreator('foo', ''); + expect(fetchedUser).toBeUndefined(); + expect(error).toBeDefined(); + }); + + it('fetchChannelCreator - base case', async () => { + await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_USER_ID, value: user.id}, {id: SYSTEM_IDENTIFIERS.CURRENT_TEAM_ID, value: teamId}], prepareRecordsOnly: false}); + await operator.handleChannel({channels: [{ + id: channelId, + team_id: teamId, + total_msg_count: 0, + creator_id: user.id, + } as Channel], + prepareRecordsOnly: false}); + + const {user: fetchedUser, error} = await fetchChannelCreator(serverUrl, channelId); + expect(error).toBeUndefined(); + expect(fetchedUser).toBeDefined(); + }); + + it('fetchMyChannelsForTeam - handle not found database', async () => { + const {error} = await fetchMyChannelsForTeam('foo', ''); + expect(error).toBeDefined(); + }); + + it('fetchMyChannelsForTeam - base case', async () => { + await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_USER_ID, value: user.id}, {id: SYSTEM_IDENTIFIERS.CURRENT_TEAM_ID, value: teamId}], prepareRecordsOnly: false}); + const {channels, memberships, categories, error} = await fetchMyChannelsForTeam(serverUrl, teamId, true, 0, false, true); + expect(error).toBeUndefined(); + expect(channels).toBeDefined(); + expect(memberships).toBeDefined(); + expect(categories).toBeDefined(); + }); + + it('fetchMyChannel - base case', async () => { + await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_USER_ID, value: user.id}, {id: SYSTEM_IDENTIFIERS.CURRENT_TEAM_ID, value: teamId}], prepareRecordsOnly: false}); + const {channels, memberships, error} = await fetchMyChannel(serverUrl, teamId, channelId); + expect(error).toBeUndefined(); + expect(channels).toBeDefined(); + expect(memberships).toBeDefined(); + }); + + it('joinChannel - handle not found database', async () => { + const {error} = await joinChannel('foo', ''); + expect(error).toBeDefined(); + }); + + it('joinChannel - by id', async () => { + await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_USER_ID, value: user.id}, {id: SYSTEM_IDENTIFIERS.CURRENT_TEAM_ID, value: teamId}], prepareRecordsOnly: false}); + const {channel, member, error} = await joinChannel(serverUrl, teamId, channelId); + expect(error).toBeUndefined(); + expect(channel).toBeDefined(); + expect(member).toBeDefined(); + }); + + it('joinChannel - by name', async () => { + await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_USER_ID, value: user.id}, {id: SYSTEM_IDENTIFIERS.CURRENT_TEAM_ID, value: teamId}], prepareRecordsOnly: false}); + const {channel, member, error} = await joinChannel(serverUrl, teamId, undefined, 'channelname'); + expect(error).toBeUndefined(); + expect(channel).toBeDefined(); + expect(member).toBeDefined(); + }); + + it('joinChannelIfNeeded - handle not found database', async () => { + const {error} = await joinChannelIfNeeded('foo', '') as {error: unknown}; + expect(error).toBeDefined(); + }); + + it('joinChannelIfNeeded - base case', async () => { + await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_USER_ID, value: user.id}, {id: SYSTEM_IDENTIFIERS.CURRENT_TEAM_ID, value: teamId}], prepareRecordsOnly: false}); + const {channel, member} = await joinChannelIfNeeded(serverUrl, channelId) as {channel: Channel; member: ChannelMember}; + expect(channel).toBeDefined(); + expect(member).toBeDefined(); + }); + + it('joinChannelIfNeeded - not needed', async () => { + await operator.handleMyChannel({channels: [{ + id: channelId, + team_id: teamId, + total_msg_count: 0, + creator_id: user.id, + } as Channel], + myChannels: [{ + id: 'id', + channel_id: channelId, + user_id: user.id, + msg_count: 0, + } as ChannelMembership], + prepareRecordsOnly: false}); + + const result = await joinChannelIfNeeded(serverUrl, channelId) as {}; + expect(result).toBeDefined(); + expect(result).not.toHaveProperty('channel'); + expect(result).not.toHaveProperty('error'); + }); + + it('markChannelAsRead - base case', async () => { + const result = await markChannelAsRead(serverUrl, channelId, true); + expect(result).toBeDefined(); + expect(result).not.toHaveProperty('error'); + }); + + it('unsetActiveChannelOnServer - base case', async () => { + const result = await unsetActiveChannelOnServer(serverUrl); + expect(result).toBeDefined(); + expect(result).not.toHaveProperty('error'); + }); + + it('switchToChannelByName - handle not found database', async () => { + const {error} = await switchToChannelByName('foo', '', '', () => {}, intl); + expect(error).toBeDefined(); + }); + + it('switchToChannelByName - base case', async () => { + await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_USER_ID, value: user.id}, {id: SYSTEM_IDENTIFIERS.CURRENT_TEAM_ID, value: teamId}], prepareRecordsOnly: false}); + + const result = await switchToChannelByName(serverUrl, 'channelname', 'teamname', () => {}, intl); + expect(result).toBeDefined(); + expect(result).not.toHaveProperty('error'); + }); + + it('switchToChannelByName - team redirect', async () => { + await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_USER_ID, value: user.id}, {id: SYSTEM_IDENTIFIERS.CURRENT_TEAM_ID, value: teamId}], prepareRecordsOnly: false}); + + const result = await switchToChannelByName(serverUrl, 'channelname', DeepLink.Redirect, () => {}, intl); + expect(result).toBeDefined(); + expect(result).not.toHaveProperty('error'); + }); + + it('goToNPSChannel - handle not found database', async () => { + const {error} = await goToNPSChannel('foo'); + expect(error).toBeDefined(); + }); + + it('goToNPSChannel - base case', async () => { + await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_USER_ID, value: user.id}, {id: SYSTEM_IDENTIFIERS.CURRENT_TEAM_ID, value: teamId}], prepareRecordsOnly: false}); + await operator.handleUsers({users: [user], prepareRecordsOnly: false}); + + const result = await goToNPSChannel(serverUrl); + expect(result).toBeDefined(); + expect(result).not.toHaveProperty('error'); + }); + + it('fetchChannels - base case', async () => { + const {channels, error} = await fetchChannels(serverUrl, teamId); + expect(error).toBeUndefined(); + expect(channels).toBeDefined(); + }); + + it('fetchArchivedChannels - base case', async () => { + const {channels, error} = await fetchArchivedChannels(serverUrl, teamId); + expect(error).toBeUndefined(); + expect(channels).toBeDefined(); + }); + + it('fetchSharedChannels - base case', async () => { + const {channels, error} = await fetchSharedChannels(serverUrl, teamId); + expect(error).toBeUndefined(); + expect(channels).toBeDefined(); + }); + + it('getChannelTimezones - base case', async () => { + const {channelTimezones, error} = await getChannelTimezones(serverUrl, channelId); + expect(error).toBeUndefined(); + expect(channelTimezones).toBeDefined(); + }); + + it('switchToChannelById - handle not found database', async () => { + const {error} = await switchToChannelById('foo', '') as {error: unknown}; + expect(error).toBeDefined(); + }); + + it('switchToChannelById - base case', async () => { + await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_USER_ID, value: user.id}, {id: SYSTEM_IDENTIFIERS.CURRENT_TEAM_ID, value: teamId}], prepareRecordsOnly: false}); + + const result = await switchToChannelById(serverUrl, channelId); + expect(result).toBeDefined(); + expect(result).not.toHaveProperty('error'); + }); + + it('switchToPenultimateChannel - handle not found database', async () => { + const {error} = await switchToPenultimateChannel('foo') as {error: unknown}; + expect(error).toBeDefined(); + }); + + it('switchToPenultimateChannel - base case', async () => { + await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_USER_ID, value: user.id}, {id: SYSTEM_IDENTIFIERS.CURRENT_TEAM_ID, value: teamId}], prepareRecordsOnly: false}); + + const result = await switchToPenultimateChannel(serverUrl); + expect(result).toBeDefined(); + expect(result).not.toHaveProperty('error'); + }); + + it('switchToLastChannel - handle not found database', async () => { + const {error} = await switchToLastChannel('foo') as {error: unknown}; + expect(error).toBeDefined(); + }); + + it('switchToLastChannel - base case', async () => { + await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_USER_ID, value: user.id}, {id: SYSTEM_IDENTIFIERS.CURRENT_TEAM_ID, value: teamId}], prepareRecordsOnly: false}); + + const result = await switchToLastChannel(serverUrl); + expect(result).toBeDefined(); + expect(result).not.toHaveProperty('error'); + }); + + it('searchChannels - handle error', async () => { + const {error} = await searchChannels('foo', '', teamId, true) as {error: unknown}; + expect(error).toBeDefined(); + }); + + it('searchChannels - base case', async () => { + const result = await searchChannels(serverUrl, 'searchterm', teamId, false); + expect(result).toBeDefined(); + expect(result).not.toHaveProperty('error'); + expect(result).toHaveProperty('channels'); + }); + + it('fetchChannelById - base case', async () => { + const result = await fetchChannelById(serverUrl, channelId); + expect(result).toBeDefined(); + expect(result).not.toHaveProperty('error'); + }); + + it('searchAllChannels - handle not found database', async () => { + const {error} = await searchAllChannels('foo', '') as {error: unknown}; + expect(error).toBeDefined(); + }); + + it('searchAllChannels - base case', async () => { + const result = await searchAllChannels(serverUrl, 'searchterm'); + expect(result).toBeDefined(); + expect(result).not.toHaveProperty('error'); + expect(result).toHaveProperty('channels'); + }); + + it('archiveChannel - handle not found database', async () => { + const {error} = await archiveChannel('foo', '') as {error: unknown}; + expect(error).toBeDefined(); + }); + + it('archiveChannel - base case', async () => { + const result = await archiveChannel(serverUrl, channelId); + expect(result).toBeDefined(); + expect(result).not.toHaveProperty('error'); + }); + + it('unarchiveChannel - base case', async () => { + const result = await unarchiveChannel(serverUrl, channelId); + expect(result).toBeDefined(); + expect(result).not.toHaveProperty('error'); + }); + + it('convertChannelToPrivate - handle not found database', async () => { + const {error} = await convertChannelToPrivate('foo', '') as {error: unknown}; + expect(error).toBeDefined(); + }); + + it('convertChannelToPrivate - base case', async () => { + await operator.handleChannel({channels: [{ + id: channelId, + team_id: teamId, + total_msg_count: 0, + type: 'O', + } as Channel], + prepareRecordsOnly: false}); + + const result = await convertChannelToPrivate(serverUrl, channelId); + expect(result).toBeDefined(); + expect(result).not.toHaveProperty('error'); + }); + + it('handleKickFromChannel - handle not found database', async () => { + const {error} = await handleKickFromChannel('foo', ''); + expect(error).toBeDefined(); + }); + + it('handleKickFromChannel - not current channel', async () => { + const result = await handleKickFromChannel(serverUrl, channelId); + expect(result).toBeDefined(); + expect(result).not.toHaveProperty('error'); + }); + + it('handleKickFromChannel - base case', async () => { + mockIsTablet.mockImplementationOnce(() => true); + mockGetActiveServer.mockImplementationOnce(() => ({url: serverUrl})); + await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_CHANNEL_ID, value: channelId}, {id: SYSTEM_IDENTIFIERS.CURRENT_TEAM_ID, value: teamId}], prepareRecordsOnly: false}); + + const result = await handleKickFromChannel(serverUrl, channelId); + expect(result).toBeDefined(); + expect(result).not.toHaveProperty('error'); + }); +}); + +describe('direct and group', () => { + it('fetchMissingDirectChannelsInfo - handle not found database', async () => { + const {directChannels, error} = await fetchMissingDirectChannelsInfo('foo', []); + expect(directChannels).toBeUndefined(); + expect(error).toBeDefined(); + }); + + it('fetchMissingDirectChannelsInfo - base case', async () => { + const {directChannels, error} = await fetchMissingDirectChannelsInfo(serverUrl, [{id: 'id', name: 'name', type: 'D'} as Channel], 'channelname'); + expect(error).toBeUndefined(); + expect(directChannels).toBeDefined(); + }); + + it('fetchDirectChannelsInfo - handle not found database', async () => { + const {directChannels, error} = await fetchDirectChannelsInfo('foo', []); + expect(directChannels).toBeUndefined(); + expect(error).toBeDefined(); + }); + + it('fetchDirectChannelsInfo - base case', async () => { + const channel = {id: 'id', name: 'name', type: 'D'} as Channel; + const {directChannels, error} = await fetchDirectChannelsInfo(serverUrl, [{...channel, toApi: () => channel} as unknown as ChannelModel]); + expect(error).toBeUndefined(); + expect(directChannels).toBeDefined(); + }); + + it('createDirectChannel - handle not found database', async () => { + const {data, error} = await createDirectChannel('foo', ''); + expect(data).toBeUndefined(); + expect(error).toBeDefined(); + }); + + it('createDirectChannel - no user', async () => { + await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_USER_ID, value: user.id}], prepareRecordsOnly: false}); + const {data, error} = await createDirectChannel(serverUrl, 'userid2'); + expect(data).toBeUndefined(); + expect(error).toBeDefined(); + expect(error).toBe('Cannot get the current user'); + }); + + it('createDirectChannel - base case', async () => { + await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_USER_ID, value: user.id}], prepareRecordsOnly: false}); + await operator.handleUsers({users: [user], prepareRecordsOnly: false}); + + const {data, error} = await createDirectChannel(serverUrl, 'userid2'); + expect(error).toBeUndefined(); + expect(data).toBeDefined(); + }); + + it('createDirectChannel - with display name', async () => { + await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_USER_ID, value: user.id}], prepareRecordsOnly: false}); + await operator.handleUsers({users: [user], prepareRecordsOnly: false}); + + const {data, error} = await createDirectChannel(serverUrl, 'userid2', 'displayname'); + expect(error).toBeUndefined(); + expect(data).toBeDefined(); + }); + + it('makeDirectChannel - handle not found database', async () => { + const {data, error} = await makeDirectChannel('foo', ''); + expect(data).toBeUndefined(); + expect(error).toBeDefined(); + }); + + it('makeDirectChannel - base case', async () => { + await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_USER_ID, value: user.id}], prepareRecordsOnly: false}); + await operator.handleUsers({users: [user], prepareRecordsOnly: false}); + + const {data, error} = await makeDirectChannel(serverUrl, 'userid2'); + expect(error).toBeUndefined(); + expect(data).toBeDefined(); + }); + + it('makeDirectChannel - with display name', async () => { + await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_USER_ID, value: user.id}], prepareRecordsOnly: false}); + await operator.handleUsers({users: [user], prepareRecordsOnly: false}); + + const {data, error} = await makeDirectChannel(serverUrl, 'userid2', 'displayname'); + expect(error).toBeUndefined(); + expect(data).toBeDefined(); + }); + + it('createGroupChannel - handle not found database', async () => { + const {data, error} = await createGroupChannel('foo', []); + expect(data).toBeUndefined(); + expect(error).toBeDefined(); + }); + + it('createGroupChannel - no user', async () => { + await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_USER_ID, value: user.id}], prepareRecordsOnly: false}); + const {data, error} = await createGroupChannel(serverUrl, ['userid2']); + expect(data).toBeUndefined(); + expect(error).toBeDefined(); + expect(error).toBe('Cannot get the current user'); + }); + + it('createGroupChannel - base case', async () => { + await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_USER_ID, value: user.id}], prepareRecordsOnly: false}); + await operator.handleUsers({users: [user], prepareRecordsOnly: false}); + + const {data, error} = await createGroupChannel(serverUrl, ['userid2']); + expect(error).toBeUndefined(); + expect(data).toBeDefined(); + }); + + it('makeGroupChannel - handle not found database', async () => { + const {data, error} = await makeGroupChannel('foo', []); + expect(data).toBeUndefined(); + expect(error).toBeDefined(); + }); + + it('makeGroupChannel - base case', async () => { + await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_USER_ID, value: user.id}], prepareRecordsOnly: false}); + await operator.handleUsers({users: [user], prepareRecordsOnly: false}); + + const {data, error} = await makeGroupChannel(serverUrl, ['userid2']); + expect(error).toBeUndefined(); + expect(data).toBeDefined(); + }); + + it('fetchGroupMessageMembersCommonTeams - base case', async () => { + const {teams, error} = await fetchGroupMessageMembersCommonTeams(serverUrl, channelId); + expect(error).toBeUndefined(); + expect(teams).toBeDefined(); + }); + + it('convertGroupMessageToPrivateChannel - handle not found database', async () => { + const {updatedChannel, error} = await convertGroupMessageToPrivateChannel('foo', '', '', ''); + expect(updatedChannel).toBeUndefined(); + expect(error).toBeDefined(); + }); + + it('convertGroupMessageToPrivateChannel - base case', async () => { + await operator.handleChannel({channels: [{ + id: channelId, + team_id: teamId, + total_msg_count: 0, + type: 'G', + } as Channel], + prepareRecordsOnly: false}); + + const {updatedChannel, error} = await convertGroupMessageToPrivateChannel(serverUrl, channelId, teamId, 'newprivatechannel'); + expect(error).toBeUndefined(); + expect(updatedChannel).toBeDefined(); + }); +}); diff --git a/app/actions/remote/channel.ts b/app/actions/remote/channel.ts index 3d0e36bad34..5140cc576fc 100644 --- a/app/actions/remote/channel.ts +++ b/app/actions/remote/channel.ts @@ -31,6 +31,7 @@ import {logDebug, logError, logInfo} from '@utils/log'; import {showMuteChannelSnackbar} from '@utils/snack_bar'; import {displayGroupMessageName, displayUsername} from '@utils/user'; +import {fetchChannelBookmarks} from './channel_bookmark'; import {fetchGroupsForChannelIfConstrained} from './groups'; import {fetchPostsForChannel} from './post'; import {openChannelIfNeeded, savePreference} from './preference'; @@ -98,6 +99,7 @@ export async function fetchChannelMembersByIds(serverUrl: string, channelId: str return {error}; } } + export async function updateChannelMemberSchemeRoles(serverUrl: string, channelId: string, userId: string, isSchemeUser: boolean, isSchemeAdmin: boolean, fetchOnly = false) { try { const client = NetworkManager.getClient(serverUrl); @@ -796,7 +798,7 @@ export async function createDirectChannel(serverUrl: string, userId: string, dis const config = await getConfig(database); const teammateDisplayNameSetting = getTeammateNameDisplaySetting(preferences || [], config.LockTeammateNameDisplay, config.TeammateNameDisplay, license); const {directChannels, users} = await fetchMissingDirectChannelsInfo(serverUrl, [created], currentUser.locale, teammateDisplayNameSetting, currentUser.id, true); - created.display_name = directChannels?.[0].display_name || created.display_name; + created.display_name = (directChannels?.length && directChannels?.[0].display_name) || created.display_name; if (users?.length) { profiles.push(...users); } @@ -1049,6 +1051,7 @@ export async function switchToChannelById(serverUrl: string, channelId: string, DeviceEventEmitter.emit(Events.CHANNEL_SWITCH, true); fetchPostsForChannel(serverUrl, channelId); + fetchChannelBookmarks(serverUrl, channelId); await switchToChannel(serverUrl, channelId, teamId, skipLastUnread); openChannelIfNeeded(serverUrl, channelId); markChannelAsRead(serverUrl, channelId); @@ -1240,7 +1243,7 @@ export const handleKickFromChannel = async (serverUrl: string, channelId: string const currentChannelId = await getCurrentChannelId(database); if (currentChannelId !== channelId) { - return; + return {}; } const currentServer = await getActiveServer(); @@ -1270,8 +1273,10 @@ export const handleKickFromChannel = async (serverUrl: string, channelId: string } else { await setCurrentChannelId(operator, ''); } + return {}; } catch (error) { logDebug('cannot kick user from channel', error); + return {error}; } }; diff --git a/app/actions/remote/channel_bookmark.ts b/app/actions/remote/channel_bookmark.ts new file mode 100644 index 00000000000..825295ad5b8 --- /dev/null +++ b/app/actions/remote/channel_bookmark.ts @@ -0,0 +1,100 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import DatabaseManager from '@database/manager'; +import NetworkManager from '@managers/network_manager'; +import websocketManager from '@managers/websocket_manager'; +import {getBookmarksSince, getChannelBookmarkById} from '@queries/servers/channel_bookmark'; +import {getConfigValue} from '@queries/servers/system'; +import {getFullErrorMessage} from '@utils/errors'; +import {logError} from '@utils/log'; + +import {forceLogoutIfNecessary} from './session'; + +export async function fetchChannelBookmarks(serverUrl: string, channelId: string, fetchOnly = false) { + try { + const client = NetworkManager.getClient(serverUrl); + const {database, operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); + + const bookmarksEnabled = (await getConfigValue(database, 'FeatureFlagChannelBookmarks')) === 'true'; + if (!bookmarksEnabled) { + return {bookmarks: []}; + } + + const since = await getBookmarksSince(database, channelId); + const bookmarks = await client.getChannelBookmarksForChannel(channelId, since); + + if (!fetchOnly && bookmarks.length) { + await operator.handleChannelBookmark({bookmarks, prepareRecordsOnly: false}); + } + + return {bookmarks}; + } catch (error) { + logError('error on fetchChannelBookmarks', getFullErrorMessage(error)); + forceLogoutIfNecessary(serverUrl, error); + return {error}; + } +} + +export async function createChannelBookmark(serverUrl: string, channelId: string, bookmark: ChannelBookmark, fetchOnly = false) { + try { + const client = NetworkManager.getClient(serverUrl); + const {operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); + const ws = websocketManager.getClient(serverUrl); + + const created = await client.createChannelBookmark(channelId, bookmark, ws?.getConnectionId()); + if (!fetchOnly) { + await operator.handleChannelBookmark({bookmarks: [created], prepareRecordsOnly: false}); + } + return {bookmark: created}; + } catch (error) { + logError('error on createChannelBookmark', getFullErrorMessage(error)); + forceLogoutIfNecessary(serverUrl, error); + return {error}; + } +} + +export async function editChannelBookmark(serverUrl: string, bookmark: ChannelBookmark, fetchOnly = false) { + try { + const client = NetworkManager.getClient(serverUrl); + const {operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); + const ws = websocketManager.getClient(serverUrl); + + const result = await client.updateChannelBookmark(bookmark.channel_id, bookmark, ws?.getConnectionId()); + const bookmarks = [result.updated]; + if (result.deleted) { + bookmarks.push(result.deleted); + } + if (!fetchOnly) { + await operator.handleChannelBookmark({bookmarks, prepareRecordsOnly: false}); + } + return {bookmarks: result}; + } catch (error) { + logError('error on editChannelBookmark', getFullErrorMessage(error)); + forceLogoutIfNecessary(serverUrl, error); + return {error}; + } +} + +export async function deleteChannelBookmark(serverUrl: string, channelId: string, bookmarkId: string, fetchOnly = false) { + try { + const client = NetworkManager.getClient(serverUrl); + const {database, operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); + const ws = websocketManager.getClient(serverUrl); + + const result = await client.deleteChannelBookmark(channelId, bookmarkId, ws?.getConnectionId()); + + const bookmark = await getChannelBookmarkById(database, bookmarkId); + if (bookmark && !fetchOnly) { + const b = bookmark.toApi(); + b.delete_at = Date.now(); + await operator.handleChannelBookmark({bookmarks: [b], prepareRecordsOnly: false}); + } + + return {bookmarks: result}; + } catch (error) { + logError('error on deleteChannelBookmark', getFullErrorMessage(error)); + forceLogoutIfNecessary(serverUrl, error); + return {error}; + } +} diff --git a/app/actions/remote/entry/login.ts b/app/actions/remote/entry/login.ts index 6206e03e347..48b7349141a 100644 --- a/app/actions/remote/entry/login.ts +++ b/app/actions/remote/entry/login.ts @@ -4,6 +4,7 @@ import {fetchConfigAndLicense} from '@actions/remote/systems'; import DatabaseManager from '@database/manager'; import {getServerCredentials} from '@init/credentials'; +import PerformanceMetricsManager from '@managers/performance_metrics_manager'; import WebsocketManager from '@managers/websocket_manager'; type AfterLoginArgs = { @@ -16,6 +17,11 @@ export async function loginEntry({serverUrl}: AfterLoginArgs): Promise<{error?: return {error: `${serverUrl} database not found`}; } + // There are cases where the target may be reset and a performance metric + // be added after login. This would be done with a wrong value, so we make + // sure we don't do this by skipping the load metric here. + PerformanceMetricsManager.skipLoadMetric(); + try { const clData = await fetchConfigAndLicense(serverUrl, false); if (clData.error) { diff --git a/app/actions/remote/file.ts b/app/actions/remote/file.ts index 14461f10878..0a4fefb9809 100644 --- a/app/actions/remote/file.ts +++ b/app/actions/remote/file.ts @@ -23,16 +23,17 @@ export const downloadProfileImage = (serverUrl: string, userId: string, lastPict export const uploadFile = ( serverUrl: string, - file: FileInfo, + file: FileInfo | ExtractedFileInfo, channelId: string, onProgress: (fractionCompleted: number, bytesRead?: number | null | undefined) => void = () => {/*Do Nothing*/}, onComplete: (response: ClientResponse) => void = () => {/*Do Nothing*/}, onError: (response: ClientResponseError) => void = () => {/*Do Nothing*/}, skipBytes = 0, + isBookmark = false, ) => { try { const client = NetworkManager.getClient(serverUrl); - return {cancel: client.uploadPostAttachment(file, channelId, onProgress, onComplete, onError, skipBytes)}; + return {cancel: client.uploadAttachment(file, channelId, onProgress, onComplete, onError, skipBytes, isBookmark)}; } catch (error) { logDebug('error on uploadFile', getFullErrorMessage(error)); return {error}; diff --git a/app/actions/remote/post.ts b/app/actions/remote/post.ts index 8f80cf53e3e..342478896d9 100644 --- a/app/actions/remote/post.ts +++ b/app/actions/remote/post.ts @@ -454,7 +454,7 @@ export async function fetchPostsSince(serverUrl: string, channelId: string, sinc const isCRTEnabled = await getIsCRTEnabled(database); const data = await client.getPostsSince(channelId, since, isCRTEnabled, isCRTEnabled); - const result = await processPostsFetched(data); + const result = processPostsFetched(data); if (!fetchOnly) { const models = await operator.handlePosts({ ...result, diff --git a/app/actions/remote/search.ts b/app/actions/remote/search.ts index 86ab63a8cb4..0010ab1a1f8 100644 --- a/app/actions/remote/search.ts +++ b/app/actions/remote/search.ts @@ -2,11 +2,12 @@ // See LICENSE.txt for license information. import {getPosts} from '@actions/local/post'; +import {General} from '@app/constants'; import {SYSTEM_IDENTIFIERS} from '@constants/database'; import DatabaseManager from '@database/manager'; import NetworkManager from '@managers/network_manager'; import {prepareMissingChannelsForAllTeams} from '@queries/servers/channel'; -import {getConfigValue} from '@queries/servers/system'; +import {getConfigValue, getCurrentTeamId} from '@queries/servers/system'; import {getIsCRTEnabled, prepareThreadsFromReceivedPosts} from '@queries/servers/thread'; import {getCurrentUser} from '@queries/servers/user'; import {getFullErrorMessage} from '@utils/errors'; @@ -18,6 +19,7 @@ import {fetchPostAuthors, fetchMissingChannelsFromPosts} from './post'; import {forceLogoutIfNecessary} from './session'; import type Model from '@nozbe/watermelondb/Model'; +import type ChannelModel from '@typings/database/models/servers/channel'; import type PostModel from '@typings/database/models/servers/post'; export async function fetchRecentMentions(serverUrl: string): Promise { @@ -129,10 +131,15 @@ export const searchPosts = async (serverUrl: string, teamId: string, params: Pos } }; -export const searchFiles = async (serverUrl: string, teamId: string, params: FileSearchParams): Promise<{files?: FileInfo[]; channels?: string[]; error?: unknown}> => { +export const searchFiles = async (serverUrl: string, teamId: string, params: FileSearchParams, channel?: ChannelModel): Promise<{files?: FileInfo[]; channels?: string[]; error?: unknown}> => { try { + let currentTeamId = teamId; + if (channel && (channel.type === General.DM_CHANNEL || channel.type === General.GM_CHANNEL)) { + const {database} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); + currentTeamId = await getCurrentTeamId(database); + } const client = NetworkManager.getClient(serverUrl); - const result = await client.searchFiles(teamId, params.terms); + const result = await client.searchFiles(currentTeamId, params.terms); const files = result?.file_infos ? Object.values(result.file_infos) : []; const [allChannelIds, allPostIds] = files.reduce<[Set, Set]>((acc, f) => { if (f.channel_id) { @@ -153,7 +160,9 @@ export const searchFiles = async (serverUrl: string, teamId: string, params: Fil return acc; }, {}); files.forEach((f) => { - f.postProps = idToPost[f.post_id]?.props; + if (f.post_id) { + f.postProps = idToPost[f.post_id]?.props; + } }); return {files, channels}; } catch (error) { diff --git a/app/actions/websocket/event.ts b/app/actions/websocket/event.ts new file mode 100644 index 00000000000..fa4aedd86f3 --- /dev/null +++ b/app/actions/websocket/event.ts @@ -0,0 +1,298 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import * as bookmark from '@actions/local/channel_bookmark'; +import * as calls from '@calls/connection/websocket_event_handlers'; +import {WebsocketEvents} from '@constants'; + +import * as category from './category'; +import * as channel from './channel'; +import * as group from './group'; +import {handleOpenDialogEvent} from './integrations'; +import * as posts from './posts'; +import * as preferences from './preferences'; +import {handleAddCustomEmoji, handleReactionRemovedFromPostEvent, handleReactionAddedToPostEvent} from './reactions'; +import {handleUserRoleUpdatedEvent, handleTeamMemberRoleUpdatedEvent, handleRoleUpdatedEvent} from './roles'; +import {handleLicenseChangedEvent, handleConfigChangedEvent} from './system'; +import * as teams from './teams'; +import {handleThreadUpdatedEvent, handleThreadReadChangedEvent, handleThreadFollowChangedEvent} from './threads'; +import {handleUserUpdatedEvent, handleUserTypingEvent, handleStatusChangedEvent} from './users'; + +export async function handleWebSocketEvent(serverUrl: string, msg: WebSocketMessage) { + switch (msg.event) { + case WebsocketEvents.POSTED: + case WebsocketEvents.EPHEMERAL_MESSAGE: + posts.handleNewPostEvent(serverUrl, msg); + break; + case WebsocketEvents.POST_EDITED: + posts.handlePostEdited(serverUrl, msg); + break; + case WebsocketEvents.POST_DELETED: + posts.handlePostDeleted(serverUrl, msg); + break; + case WebsocketEvents.POST_UNREAD: + posts.handlePostUnread(serverUrl, msg); + break; + case WebsocketEvents.POST_ACKNOWLEDGEMENT_ADDED: + posts.handlePostAcknowledgementAdded(serverUrl, msg); + break; + case WebsocketEvents.POST_ACKNOWLEDGEMENT_REMOVED: + posts.handlePostAcknowledgementRemoved(serverUrl, msg); + break; + + case WebsocketEvents.LEAVE_TEAM: + teams.handleLeaveTeamEvent(serverUrl, msg); + break; + case WebsocketEvents.UPDATE_TEAM: + teams.handleUpdateTeamEvent(serverUrl, msg); + break; + case WebsocketEvents.ADDED_TO_TEAM: + teams.handleUserAddedToTeamEvent(serverUrl, msg); + break; + case WebsocketEvents.DELETE_TEAM: + teams.handleTeamArchived(serverUrl, msg); + break; + case WebsocketEvents.RESTORE_TEAM: + teams.handleTeamRestored(serverUrl, msg); + break; + + case WebsocketEvents.USER_ADDED: + channel.handleUserAddedToChannelEvent(serverUrl, msg); + break; + case WebsocketEvents.USER_REMOVED: + channel.handleUserRemovedFromChannelEvent(serverUrl, msg); + break; + case WebsocketEvents.USER_UPDATED: + handleUserUpdatedEvent(serverUrl, msg); + break; + case WebsocketEvents.ROLE_UPDATED: + handleRoleUpdatedEvent(serverUrl, msg); + break; + case WebsocketEvents.USER_ROLE_UPDATED: + handleUserRoleUpdatedEvent(serverUrl, msg); + break; + case WebsocketEvents.MEMBERROLE_UPDATED: + handleTeamMemberRoleUpdatedEvent(serverUrl, msg); + break; + + case WebsocketEvents.CATEGORY_CREATED: + category.handleCategoryCreatedEvent(serverUrl, msg); + break; + case WebsocketEvents.CATEGORY_UPDATED: + category.handleCategoryUpdatedEvent(serverUrl, msg); + break; + case WebsocketEvents.CATEGORY_ORDER_UPDATED: + category.handleCategoryOrderUpdatedEvent(serverUrl, msg); + break; + case WebsocketEvents.CATEGORY_DELETED: + category.handleCategoryDeletedEvent(serverUrl, msg); + break; + + case WebsocketEvents.CHANNEL_CREATED: + channel.handleChannelCreatedEvent(serverUrl, msg); + break; + case WebsocketEvents.CHANNEL_DELETED: + channel.handleChannelDeletedEvent(serverUrl, msg); + break; + case WebsocketEvents.CHANNEL_UNARCHIVED: + channel.handleChannelUnarchiveEvent(serverUrl, msg); + break; + case WebsocketEvents.CHANNEL_UPDATED: + channel.handleChannelUpdatedEvent(serverUrl, msg); + break; + case WebsocketEvents.CHANNEL_CONVERTED: + channel.handleChannelConvertedEvent(serverUrl, msg); + break; + case WebsocketEvents.CHANNEL_VIEWED: + channel.handleChannelViewedEvent(serverUrl, msg); + break; + case WebsocketEvents.MULTIPLE_CHANNELS_VIEWED: + channel.handleMultipleChannelsViewedEvent(serverUrl, msg); + break; + case WebsocketEvents.CHANNEL_MEMBER_UPDATED: + channel.handleChannelMemberUpdatedEvent(serverUrl, msg); + break; + case WebsocketEvents.CHANNEL_SCHEME_UPDATED: + // Do nothing, handled by CHANNEL_UPDATED due to changes in the channel scheme. + break; + case WebsocketEvents.DIRECT_ADDED: + case WebsocketEvents.GROUP_ADDED: + channel.handleDirectAddedEvent(serverUrl, msg); + break; + + case WebsocketEvents.PREFERENCE_CHANGED: + preferences.handlePreferenceChangedEvent(serverUrl, msg); + break; + case WebsocketEvents.PREFERENCES_CHANGED: + preferences.handlePreferencesChangedEvent(serverUrl, msg); + break; + case WebsocketEvents.PREFERENCES_DELETED: + preferences.handlePreferencesDeletedEvent(serverUrl, msg); + break; + + case WebsocketEvents.STATUS_CHANGED: + handleStatusChangedEvent(serverUrl, msg); + break; + case WebsocketEvents.TYPING: + handleUserTypingEvent(serverUrl, msg); + break; + + case WebsocketEvents.REACTION_ADDED: + handleReactionAddedToPostEvent(serverUrl, msg); + break; + case WebsocketEvents.REACTION_REMOVED: + handleReactionRemovedFromPostEvent(serverUrl, msg); + break; + case WebsocketEvents.EMOJI_ADDED: + handleAddCustomEmoji(serverUrl, msg); + break; + + case WebsocketEvents.LICENSE_CHANGED: + handleLicenseChangedEvent(serverUrl, msg); + break; + case WebsocketEvents.CONFIG_CHANGED: + handleConfigChangedEvent(serverUrl, msg); + break; + + case WebsocketEvents.OPEN_DIALOG: + handleOpenDialogEvent(serverUrl, msg); + break; + case WebsocketEvents.APPS_FRAMEWORK_REFRESH_BINDINGS: + break; + + case WebsocketEvents.THREAD_UPDATED: + handleThreadUpdatedEvent(serverUrl, msg); + break; + case WebsocketEvents.THREAD_READ_CHANGED: + handleThreadReadChangedEvent(serverUrl, msg); + break; + case WebsocketEvents.THREAD_FOLLOW_CHANGED: + handleThreadFollowChangedEvent(serverUrl, msg); + break; + + // Calls ws events: + case WebsocketEvents.CALLS_CHANNEL_ENABLED: + calls.handleCallChannelEnabled(serverUrl, msg); + break; + case WebsocketEvents.CALLS_CHANNEL_DISABLED: + calls.handleCallChannelDisabled(serverUrl, msg); + break; + + // DEPRECATED in favour of user_joined (since v0.21.0) + case WebsocketEvents.CALLS_USER_CONNECTED: + calls.handleCallUserConnected(serverUrl, msg); + break; + + // DEPRECATED in favour of user_left (since v0.21.0) + case WebsocketEvents.CALLS_USER_DISCONNECTED: + calls.handleCallUserDisconnected(serverUrl, msg); + break; + + case WebsocketEvents.CALLS_USER_JOINED: + calls.handleCallUserJoined(serverUrl, msg); + break; + case WebsocketEvents.CALLS_USER_LEFT: + calls.handleCallUserLeft(serverUrl, msg); + break; + case WebsocketEvents.CALLS_USER_MUTED: + calls.handleCallUserMuted(serverUrl, msg); + break; + case WebsocketEvents.CALLS_USER_UNMUTED: + calls.handleCallUserUnmuted(serverUrl, msg); + break; + case WebsocketEvents.CALLS_USER_VOICE_ON: + calls.handleCallUserVoiceOn(msg); + break; + case WebsocketEvents.CALLS_USER_VOICE_OFF: + calls.handleCallUserVoiceOff(msg); + break; + case WebsocketEvents.CALLS_CALL_START: + calls.handleCallStarted(serverUrl, msg); + break; + case WebsocketEvents.CALLS_SCREEN_ON: + calls.handleCallScreenOn(serverUrl, msg); + break; + case WebsocketEvents.CALLS_SCREEN_OFF: + calls.handleCallScreenOff(serverUrl, msg); + break; + case WebsocketEvents.CALLS_USER_RAISE_HAND: + calls.handleCallUserRaiseHand(serverUrl, msg); + break; + case WebsocketEvents.CALLS_USER_UNRAISE_HAND: + calls.handleCallUserUnraiseHand(serverUrl, msg); + break; + case WebsocketEvents.CALLS_CALL_END: + calls.handleCallEnded(serverUrl, msg); + break; + case WebsocketEvents.CALLS_USER_REACTED: + calls.handleCallUserReacted(serverUrl, msg); + break; + + // DEPRECATED in favour of CALLS_JOB_STATE (since v2.15.0) + case WebsocketEvents.CALLS_RECORDING_STATE: + calls.handleCallRecordingState(serverUrl, msg); + break; + case WebsocketEvents.CALLS_JOB_STATE: + calls.handleCallJobState(serverUrl, msg); + break; + case WebsocketEvents.CALLS_HOST_CHANGED: + calls.handleCallHostChanged(serverUrl, msg); + break; + case WebsocketEvents.CALLS_USER_DISMISSED_NOTIFICATION: + calls.handleUserDismissedNotification(serverUrl, msg); + break; + case WebsocketEvents.CALLS_CAPTION: + calls.handleCallCaption(serverUrl, msg); + break; + case WebsocketEvents.CALLS_HOST_MUTE: + calls.handleHostMute(serverUrl, msg); + break; + case WebsocketEvents.CALLS_HOST_LOWER_HAND: + calls.handleHostLowerHand(serverUrl, msg); + break; + case WebsocketEvents.CALLS_HOST_REMOVED: + calls.handleHostRemoved(serverUrl, msg); + break; + case WebsocketEvents.CALLS_CALL_STATE: + calls.handleCallState(serverUrl, msg); + break; + case WebsocketEvents.GROUP_RECEIVED: + group.handleGroupReceivedEvent(serverUrl, msg); + break; + case WebsocketEvents.GROUP_MEMBER_ADD: + group.handleGroupMemberAddEvent(serverUrl, msg); + break; + case WebsocketEvents.GROUP_MEMBER_DELETE: + group.handleGroupMemberDeleteEvent(serverUrl, msg); + break; + case WebsocketEvents.GROUP_ASSOCIATED_TO_TEAM: + group.handleGroupTeamAssociatedEvent(serverUrl, msg); + break; + case WebsocketEvents.GROUP_DISSOCIATED_TO_TEAM: + group.handleGroupTeamDissociateEvent(serverUrl, msg); + break; + case WebsocketEvents.GROUP_ASSOCIATED_TO_CHANNEL: + break; + case WebsocketEvents.GROUP_DISSOCIATED_TO_CHANNEL: + break; + + // Plugins + case WebsocketEvents.PLUGIN_STATUSES_CHANGED: + case WebsocketEvents.PLUGIN_ENABLED: + case WebsocketEvents.PLUGIN_DISABLED: + // Do nothing, this event doesn't need logic in the mobile app + break; + + // bookmarks + case WebsocketEvents.CHANNEL_BOOKMARK_CREATED: + case WebsocketEvents.CHANNEL_BOOKMARK_DELETED: + bookmark.handleBookmarkAddedOrDeleted(serverUrl, msg); + break; + case WebsocketEvents.CHANNEL_BOOKMARK_UPDATED: + bookmark.handleBookmarkEdited(serverUrl, msg); + break; + case WebsocketEvents.CHANNEL_BOOKMARK_SORTED: + bookmark.handleBookmarkSorted(serverUrl, msg); + break; + } +} diff --git a/app/actions/websocket/index.ts b/app/actions/websocket/index.ts index 96d173242fb..0c233c6dd5b 100644 --- a/app/actions/websocket/index.ts +++ b/app/actions/websocket/index.ts @@ -14,36 +14,8 @@ import {fetchPostsForChannel, fetchPostThread} from '@actions/remote/post'; import {openAllUnreadChannels} from '@actions/remote/preference'; import {autoUpdateTimezone} from '@actions/remote/user'; import {loadConfigAndCalls} from '@calls/actions/calls'; -import { - handleCallCaption, - handleCallChannelDisabled, - handleCallChannelEnabled, - handleCallEnded, - handleCallHostChanged, - handleCallJobState, - handleCallRecordingState, - handleCallScreenOff, - handleCallScreenOn, - handleCallStarted, - handleCallState, - handleCallUserConnected, - handleCallUserDisconnected, - handleCallUserJoined, - handleCallUserLeft, - handleCallUserMuted, - handleCallUserRaiseHand, - handleCallUserReacted, - handleCallUserUnmuted, - handleCallUserUnraiseHand, - handleCallUserVoiceOff, - handleCallUserVoiceOn, - handleHostLowerHand, - handleHostMute, - handleHostRemoved, - handleUserDismissedNotification, -} from '@calls/connection/websocket_event_handlers'; import {isSupportedServerCalls} from '@calls/utils'; -import {Screens, WebsocketEvents} from '@constants'; +import {Screens} from '@constants'; import DatabaseManager from '@database/manager'; import AppsManager from '@managers/apps_manager'; import {getActiveServerUrl} from '@queries/app/servers'; @@ -64,58 +36,6 @@ import {setTeamLoading} from '@store/team_load_store'; import {isTablet} from '@utils/helpers'; import {logDebug, logInfo} from '@utils/log'; -import { - handleCategoryCreatedEvent, - handleCategoryDeletedEvent, - handleCategoryOrderUpdatedEvent, - handleCategoryUpdatedEvent, -} from './category'; -import { - handleChannelConvertedEvent, handleChannelCreatedEvent, - handleChannelDeletedEvent, - handleChannelMemberUpdatedEvent, - handleChannelUnarchiveEvent, - handleChannelUpdatedEvent, - handleChannelViewedEvent, - handleMultipleChannelsViewedEvent, - handleDirectAddedEvent, - handleUserAddedToChannelEvent, - handleUserRemovedFromChannelEvent, -} from './channel'; -import { - handleGroupMemberAddEvent, - handleGroupMemberDeleteEvent, - handleGroupReceivedEvent, - handleGroupTeamAssociatedEvent, - handleGroupTeamDissociateEvent, -} from './group'; -import {handleOpenDialogEvent} from './integrations'; -import { - handleNewPostEvent, - handlePostAcknowledgementAdded, - handlePostAcknowledgementRemoved, - handlePostDeleted, - handlePostEdited, - handlePostUnread, -} from './posts'; -import { - handlePreferenceChangedEvent, - handlePreferencesChangedEvent, - handlePreferencesDeletedEvent, -} from './preferences'; -import {handleAddCustomEmoji, handleReactionRemovedFromPostEvent, handleReactionAddedToPostEvent} from './reactions'; -import {handleUserRoleUpdatedEvent, handleTeamMemberRoleUpdatedEvent, handleRoleUpdatedEvent} from './roles'; -import {handleLicenseChangedEvent, handleConfigChangedEvent} from './system'; -import { - handleLeaveTeamEvent, - handleUserAddedToTeamEvent, - handleUpdateTeamEvent, - handleTeamArchived, - handleTeamRestored, -} from './teams'; -import {handleThreadUpdatedEvent, handleThreadReadChangedEvent, handleThreadFollowChangedEvent} from './threads'; -import {handleUserUpdatedEvent, handleUserTypingEvent, handleStatusChangedEvent} from './users'; - export async function handleFirstConnect(serverUrl: string) { registerDeviceToken(serverUrl); autoUpdateTimezone(serverUrl); @@ -192,300 +112,6 @@ async function doReconnect(serverUrl: string) { return undefined; } -export async function handleEvent(serverUrl: string, msg: WebSocketMessage) { - switch (msg.event) { - case WebsocketEvents.POSTED: - case WebsocketEvents.EPHEMERAL_MESSAGE: - handleNewPostEvent(serverUrl, msg); - break; - - case WebsocketEvents.POST_EDITED: - handlePostEdited(serverUrl, msg); - break; - - case WebsocketEvents.POST_DELETED: - handlePostDeleted(serverUrl, msg); - break; - - case WebsocketEvents.POST_UNREAD: - handlePostUnread(serverUrl, msg); - break; - - case WebsocketEvents.POST_ACKNOWLEDGEMENT_ADDED: - handlePostAcknowledgementAdded(serverUrl, msg); - break; - case WebsocketEvents.POST_ACKNOWLEDGEMENT_REMOVED: - handlePostAcknowledgementRemoved(serverUrl, msg); - break; - - case WebsocketEvents.LEAVE_TEAM: - handleLeaveTeamEvent(serverUrl, msg); - break; - case WebsocketEvents.UPDATE_TEAM: - handleUpdateTeamEvent(serverUrl, msg); - break; - case WebsocketEvents.ADDED_TO_TEAM: - handleUserAddedToTeamEvent(serverUrl, msg); - break; - - case WebsocketEvents.USER_ADDED: - handleUserAddedToChannelEvent(serverUrl, msg); - break; - case WebsocketEvents.USER_REMOVED: - handleUserRemovedFromChannelEvent(serverUrl, msg); - break; - case WebsocketEvents.USER_UPDATED: - handleUserUpdatedEvent(serverUrl, msg); - break; - case WebsocketEvents.ROLE_UPDATED: - handleRoleUpdatedEvent(serverUrl, msg); - break; - - case WebsocketEvents.USER_ROLE_UPDATED: - handleUserRoleUpdatedEvent(serverUrl, msg); - break; - - case WebsocketEvents.MEMBERROLE_UPDATED: - handleTeamMemberRoleUpdatedEvent(serverUrl, msg); - break; - - case WebsocketEvents.CATEGORY_CREATED: - handleCategoryCreatedEvent(serverUrl, msg); - break; - case WebsocketEvents.CATEGORY_UPDATED: - handleCategoryUpdatedEvent(serverUrl, msg); - break; - case WebsocketEvents.CATEGORY_ORDER_UPDATED: - handleCategoryOrderUpdatedEvent(serverUrl, msg); - break; - case WebsocketEvents.CATEGORY_DELETED: - handleCategoryDeletedEvent(serverUrl, msg); - break; - - case WebsocketEvents.CHANNEL_CREATED: - handleChannelCreatedEvent(serverUrl, msg); - break; - - case WebsocketEvents.CHANNEL_DELETED: - handleChannelDeletedEvent(serverUrl, msg); - break; - case WebsocketEvents.CHANNEL_UNARCHIVED: - handleChannelUnarchiveEvent(serverUrl, msg); - break; - - case WebsocketEvents.CHANNEL_UPDATED: - handleChannelUpdatedEvent(serverUrl, msg); - break; - - case WebsocketEvents.CHANNEL_CONVERTED: - handleChannelConvertedEvent(serverUrl, msg); - break; - - case WebsocketEvents.CHANNEL_VIEWED: - handleChannelViewedEvent(serverUrl, msg); - break; - - case WebsocketEvents.MULTIPLE_CHANNELS_VIEWED: - handleMultipleChannelsViewedEvent(serverUrl, msg); - break; - - case WebsocketEvents.CHANNEL_MEMBER_UPDATED: - handleChannelMemberUpdatedEvent(serverUrl, msg); - break; - - case WebsocketEvents.CHANNEL_SCHEME_UPDATED: - // Do nothing, handled by CHANNEL_UPDATED due to changes in the channel scheme. - break; - - case WebsocketEvents.DIRECT_ADDED: - case WebsocketEvents.GROUP_ADDED: - handleDirectAddedEvent(serverUrl, msg); - break; - - case WebsocketEvents.PREFERENCE_CHANGED: - handlePreferenceChangedEvent(serverUrl, msg); - break; - - case WebsocketEvents.PREFERENCES_CHANGED: - handlePreferencesChangedEvent(serverUrl, msg); - break; - - case WebsocketEvents.PREFERENCES_DELETED: - handlePreferencesDeletedEvent(serverUrl, msg); - break; - - case WebsocketEvents.STATUS_CHANGED: - handleStatusChangedEvent(serverUrl, msg); - break; - case WebsocketEvents.TYPING: - handleUserTypingEvent(serverUrl, msg); - break; - - case WebsocketEvents.REACTION_ADDED: - handleReactionAddedToPostEvent(serverUrl, msg); - break; - - case WebsocketEvents.REACTION_REMOVED: - handleReactionRemovedFromPostEvent(serverUrl, msg); - break; - - case WebsocketEvents.EMOJI_ADDED: - handleAddCustomEmoji(serverUrl, msg); - break; - - case WebsocketEvents.LICENSE_CHANGED: - handleLicenseChangedEvent(serverUrl, msg); - break; - - case WebsocketEvents.CONFIG_CHANGED: - handleConfigChangedEvent(serverUrl, msg); - break; - - case WebsocketEvents.OPEN_DIALOG: - handleOpenDialogEvent(serverUrl, msg); - break; - - case WebsocketEvents.DELETE_TEAM: - handleTeamArchived(serverUrl, msg); - break; - - case WebsocketEvents.RESTORE_TEAM: - handleTeamRestored(serverUrl, msg); - break; - - case WebsocketEvents.THREAD_UPDATED: - handleThreadUpdatedEvent(serverUrl, msg); - break; - - case WebsocketEvents.THREAD_READ_CHANGED: - handleThreadReadChangedEvent(serverUrl, msg); - break; - - case WebsocketEvents.THREAD_FOLLOW_CHANGED: - handleThreadFollowChangedEvent(serverUrl, msg); - break; - - case WebsocketEvents.APPS_FRAMEWORK_REFRESH_BINDINGS: - break; - - // return dispatch(handleRefreshAppsBindings()); - - // Calls ws events: - case WebsocketEvents.CALLS_CHANNEL_ENABLED: - handleCallChannelEnabled(serverUrl, msg); - break; - case WebsocketEvents.CALLS_CHANNEL_DISABLED: - handleCallChannelDisabled(serverUrl, msg); - break; - - // DEPRECATED in favour of user_joined (since v0.21.0) - case WebsocketEvents.CALLS_USER_CONNECTED: - handleCallUserConnected(serverUrl, msg); - break; - - // DEPRECATED in favour of user_left (since v0.21.0) - case WebsocketEvents.CALLS_USER_DISCONNECTED: - handleCallUserDisconnected(serverUrl, msg); - break; - - case WebsocketEvents.CALLS_USER_JOINED: - handleCallUserJoined(serverUrl, msg); - break; - case WebsocketEvents.CALLS_USER_LEFT: - handleCallUserLeft(serverUrl, msg); - break; - case WebsocketEvents.CALLS_USER_MUTED: - handleCallUserMuted(serverUrl, msg); - break; - case WebsocketEvents.CALLS_USER_UNMUTED: - handleCallUserUnmuted(serverUrl, msg); - break; - case WebsocketEvents.CALLS_USER_VOICE_ON: - handleCallUserVoiceOn(msg); - break; - case WebsocketEvents.CALLS_USER_VOICE_OFF: - handleCallUserVoiceOff(msg); - break; - case WebsocketEvents.CALLS_CALL_START: - handleCallStarted(serverUrl, msg); - break; - case WebsocketEvents.CALLS_SCREEN_ON: - handleCallScreenOn(serverUrl, msg); - break; - case WebsocketEvents.CALLS_SCREEN_OFF: - handleCallScreenOff(serverUrl, msg); - break; - case WebsocketEvents.CALLS_USER_RAISE_HAND: - handleCallUserRaiseHand(serverUrl, msg); - break; - case WebsocketEvents.CALLS_USER_UNRAISE_HAND: - handleCallUserUnraiseHand(serverUrl, msg); - break; - case WebsocketEvents.CALLS_CALL_END: - handleCallEnded(serverUrl, msg); - break; - case WebsocketEvents.CALLS_USER_REACTED: - handleCallUserReacted(serverUrl, msg); - break; - - // DEPRECATED in favour of CALLS_JOB_STATE (since v2.15.0) - case WebsocketEvents.CALLS_RECORDING_STATE: - handleCallRecordingState(serverUrl, msg); - break; - case WebsocketEvents.CALLS_JOB_STATE: - handleCallJobState(serverUrl, msg); - break; - case WebsocketEvents.CALLS_HOST_CHANGED: - handleCallHostChanged(serverUrl, msg); - break; - case WebsocketEvents.CALLS_USER_DISMISSED_NOTIFICATION: - handleUserDismissedNotification(serverUrl, msg); - break; - case WebsocketEvents.CALLS_CAPTION: - handleCallCaption(serverUrl, msg); - break; - case WebsocketEvents.CALLS_HOST_MUTE: - handleHostMute(serverUrl, msg); - break; - case WebsocketEvents.CALLS_HOST_LOWER_HAND: - handleHostLowerHand(serverUrl, msg); - break; - case WebsocketEvents.CALLS_HOST_REMOVED: - handleHostRemoved(serverUrl, msg); - break; - case WebsocketEvents.CALLS_CALL_STATE: - handleCallState(serverUrl, msg); - break; - - case WebsocketEvents.GROUP_RECEIVED: - handleGroupReceivedEvent(serverUrl, msg); - break; - case WebsocketEvents.GROUP_MEMBER_ADD: - handleGroupMemberAddEvent(serverUrl, msg); - break; - case WebsocketEvents.GROUP_MEMBER_DELETE: - handleGroupMemberDeleteEvent(serverUrl, msg); - break; - case WebsocketEvents.GROUP_ASSOCIATED_TO_TEAM: - handleGroupTeamAssociatedEvent(serverUrl, msg); - break; - case WebsocketEvents.GROUP_DISSOCIATED_TO_TEAM: - handleGroupTeamDissociateEvent(serverUrl, msg); - break; - case WebsocketEvents.GROUP_ASSOCIATED_TO_CHANNEL: - break; - case WebsocketEvents.GROUP_DISSOCIATED_TO_CHANNEL: - break; - - // Plugins - case WebsocketEvents.PLUGIN_STATUSES_CHANGED: - case WebsocketEvents.PLUGIN_ENABLED: - case WebsocketEvents.PLUGIN_DISABLED: - // Do nothing, this event doesn't need logic in the mobile app - break; - } -} - async function fetchPostDataIfNeeded(serverUrl: string) { try { const isActiveServer = (await getActiveServerUrl()) === serverUrl; diff --git a/app/client/rest/base.ts b/app/client/rest/base.ts index 5259b9361dc..0240d3f6761 100644 --- a/app/client/rest/base.ts +++ b/app/client/rest/base.ts @@ -131,6 +131,14 @@ export default class ClientBase { return `${this.getChannelsRoute()}/${channelId}`; } + getChannelBookmarksRoute(channelId: string) { + return `${this.getChannelRoute(channelId)}/bookmarks`; + } + + getChannelBookmarkRoute(channelId: string, bookmarkId: string) { + return `${this.getChannelBookmarksRoute(channelId)}/${bookmarkId}`; + } + getSharedChannelsRoute() { return `${this.urlVersion}/sharedchannels`; } diff --git a/app/client/rest/channel_bookmark.ts b/app/client/rest/channel_bookmark.ts new file mode 100644 index 00000000000..07dd0a27e7a --- /dev/null +++ b/app/client/rest/channel_bookmark.ts @@ -0,0 +1,68 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import {buildQueryString} from '@utils/helpers'; + +import type ClientBase from './base'; + +export interface ClientChannelBookmarksMix { + createChannelBookmark(channelId: string, bookmark: ChannelBookmark, connectionId?: string): Promise; + updateChannelBookmark(channelId: string, bookmark: ChannelBookmark, connectionId?: string): Promise; + updateChannelBookmarkSortOrder(channelId: string, bookmarkId: string, newSortOrder: number, connectionId?: string): Promise; + deleteChannelBookmark(channelId: string, bookmarkId: string, connectionId?: string): Promise; + getChannelBookmarksForChannel(channelId: string, since: number): Promise; +} + +const ClientChannelBookmarks = >(superclass: TBase) => class extends superclass { + createChannelBookmark = async (channelId: string, bookmark: ChannelBookmark, connectionId = '') => { + return this.doFetch( + this.getChannelBookmarksRoute(channelId), + { + method: 'post', + body: bookmark, + headers: {'Connection-Id': connectionId}, + }, + ); + }; + + updateChannelBookmark(channelId: string, bookmark: ChannelBookmark, connectionId?: string) { + return this.doFetch( + this.getChannelBookmarkRoute(channelId, bookmark.id), + { + method: 'patch', + body: bookmark, + headers: {'Connection-Id': connectionId}, + }, + ); + } + + updateChannelBookmarkSortOrder(channelId: string, bookmarkId: string, newSortOrder: number, connectionId?: string) { + return this.doFetch( + `${this.getChannelBookmarkRoute(channelId, bookmarkId)}/sort_order`, + { + method: 'post', + body: newSortOrder, + headers: {'Connection-Id': connectionId}, + }, + ); + } + + deleteChannelBookmark(channelId: string, bookmarkId: string, connectionId?: string) { + return this.doFetch( + this.getChannelBookmarkRoute(channelId, bookmarkId), + { + method: 'delete', + headers: {'Connection-Id': connectionId}, + }, + ); + } + + getChannelBookmarksForChannel(channelId: string, since: number) { + return this.doFetch( + `${this.getChannelBookmarksRoute(channelId)}${buildQueryString({bookmarks_since: since})}`, + {method: 'get'}, + ); + } +}; + +export default ClientChannelBookmarks; diff --git a/app/client/rest/channels.ts b/app/client/rest/channels.ts index b3904b74c94..2d909b80b68 100644 --- a/app/client/rest/channels.ts +++ b/app/client/rest/channels.ts @@ -132,14 +132,16 @@ const ClientChannels = >(superclass: TBase getChannel = async (channelId: string) => { return this.doFetch( - `${this.getChannelRoute(channelId)}`, + this.getChannelRoute(channelId), {method: 'get'}, ); }; getChannelByName = async (teamId: string, channelName: string, includeDeleted = false) => { return this.doFetch( - `${this.getTeamRoute(teamId)}/channels/name/${channelName}?include_deleted=${includeDeleted}`, + `${this.getTeamRoute(teamId)}/channels/name/${channelName}${buildQueryString({ + include_deleted: includeDeleted, + })}`, {method: 'get'}, ); }; @@ -153,14 +155,20 @@ const ClientChannels = >(superclass: TBase getChannels = async (teamId: string, page = 0, perPage = PER_PAGE_DEFAULT) => { return this.doFetch( - `${this.getTeamRoute(teamId)}/channels${buildQueryString({page, per_page: perPage})}`, + `${this.getTeamRoute(teamId)}/channels${buildQueryString({ + page, + per_page: perPage, + })}`, {method: 'get'}, ); }; getArchivedChannels = async (teamId: string, page = 0, perPage = PER_PAGE_DEFAULT) => { return this.doFetch( - `${this.getTeamRoute(teamId)}/channels/deleted${buildQueryString({page, per_page: perPage})}`, + `${this.getTeamRoute(teamId)}/channels/deleted${buildQueryString({ + page, + per_page: perPage, + })}`, {method: 'get'}, ); }; @@ -172,11 +180,11 @@ const ClientChannels = >(superclass: TBase ); }; - getMyChannels = async (teamId: string, includeDeleted = false, lastDeleteAt = 0) => { + getMyChannels = async (teamId: string, includeDeleted = false, since = 0) => { return this.doFetch( `${this.getUserRoute('me')}/teams/${teamId}/channels${buildQueryString({ include_deleted: includeDeleted, - last_delete_at: lastDeleteAt, + last_delete_at: since, })}`, {method: 'get'}, ); diff --git a/app/client/rest/files.ts b/app/client/rest/files.ts index 036ec582fb2..d899acdf0cc 100644 --- a/app/client/rest/files.ts +++ b/app/client/rest/files.ts @@ -11,13 +11,14 @@ export interface ClientFilesMix { getFileThumbnailUrl: (fileId: string, timestamp: number) => string; getFilePreviewUrl: (fileId: string, timestamp: number) => string; getFilePublicLink: (fileId: string) => Promise<{link: string}>; - uploadPostAttachment: ( - file: FileInfo, + uploadAttachment: ( + file: FileInfo | ExtractedFileInfo, channelId: string, onProgress: (fractionCompleted: number, bytesRead?: number | null | undefined) => void, onComplete: (response: ClientResponse) => void, onError: (response: ClientResponseError) => void, skipBytes?: number, + isBookmark?: boolean, ) => () => void; searchFiles: (teamId: string, terms: string) => Promise; searchFilesWithParams: (teamId: string, FileSearchParams: string) => Promise; @@ -58,15 +59,19 @@ const ClientFiles = >(superclass: TBase) = ); }; - uploadPostAttachment = ( - file: FileInfo, + uploadAttachment = ( + file: FileInfo | ExtractedFileInfo, channelId: string, onProgress: (fractionCompleted: number, bytesRead?: number | null | undefined) => void, onComplete: (response: ClientResponse) => void, onError: (response: ClientResponseError) => void, skipBytes = 0, + isBookmark = false, ) => { - const url = this.getFilesRoute(); + let url = this.getFilesRoute(); + if (isBookmark) { + url = `${url}?bookmark=true`; + } const options: UploadRequestOptions = { skipBytes, method: 'POST', diff --git a/app/client/rest/index.ts b/app/client/rest/index.ts index 382afcbfd73..e023d8ca359 100644 --- a/app/client/rest/index.ts +++ b/app/client/rest/index.ts @@ -8,6 +8,7 @@ import mix from '@utils/mix'; import ClientApps, {type ClientAppsMix} from './apps'; import ClientBase from './base'; import ClientCategories, {type ClientCategoriesMix} from './categories'; +import ClientChannelBookmarks, {type ClientChannelBookmarksMix} from './channel_bookmark'; import ClientChannels, {type ClientChannelsMix} from './channels'; import {DEFAULT_LIMIT_AFTER, DEFAULT_LIMIT_BEFORE, HEADER_X_VERSION_ID} from './constants'; import ClientEmojis, {type ClientEmojisMix} from './emojis'; @@ -29,6 +30,7 @@ interface Client extends ClientBase, ClientAppsMix, ClientCategoriesMix, ClientChannelsMix, + ClientChannelBookmarksMix, ClientEmojisMix, ClientFilesMix, ClientGeneralMix, @@ -49,6 +51,7 @@ class Client extends mix(ClientBase).with( ClientApps, ClientCategories, ClientChannels, + ClientChannelBookmarks, ClientEmojis, ClientFiles, ClientGeneral, diff --git a/app/client/websocket/index.ts b/app/client/websocket/index.ts index 3070b0684c5..0b98f06179b 100644 --- a/app/client/websocket/index.ts +++ b/app/client/websocket/index.ts @@ -9,7 +9,7 @@ import DatabaseManager from '@database/manager'; import {getConfigValue} from '@queries/servers/system'; import {hasReliableWebsocket} from '@utils/config'; import {toMilliseconds} from '@utils/datetime'; -import {logError, logInfo, logWarning} from '@utils/log'; +import {logDebug, logError, logInfo, logWarning} from '@utils/log'; const MAX_WEBSOCKET_FAILS = 7; const WEBSOCKET_TIMEOUT = toMilliseconds({seconds: 30}); @@ -18,6 +18,7 @@ const MAX_WEBSOCKET_RETRY_TIME = toMilliseconds({minutes: 5}); const DEFAULT_OPTIONS = { forceConnection: true, }; +const TLS_HANDSHARE_ERROR = 1015; export default class WebSocketClient { private conn?: WebSocketClientInterface; @@ -179,7 +180,7 @@ export default class WebSocketClient { this.connectFailCount = 0; }); - this.conn!.onClose(() => { + this.conn!.onClose((ev) => { clearTimeout(this.connectionTimeout); this.conn = undefined; this.responseSequence = 1; @@ -190,6 +191,12 @@ export default class WebSocketClient { // reliable websockets are enabled this won't trigger a new sync. this.shouldSkipSync = false; + if (ev.message && typeof ev.message === 'object' && 'code' in ev.message && ev.message.code === TLS_HANDSHARE_ERROR) { + logDebug('websocket did not connect', this.url, ev.message.reason); + this.closeCallback?.(this.connectFailCount); + return; + } + if (this.connectFailCount === 0) { logInfo('websocket closed', this.url); } @@ -366,4 +373,8 @@ export default class WebSocketClient { public isConnected(): boolean { return this.conn?.readyState === WebSocketReadyState.OPEN; } + + public getConnectionId(): string { + return this.connectionId; + } } diff --git a/app/components/button/index.tsx b/app/components/button/index.tsx index 51f3059407e..0d9aa5f6836 100644 --- a/app/components/button/index.tsx +++ b/app/components/button/index.tsx @@ -3,10 +3,11 @@ import {Button as ElementButton} from '@rneui/base'; import React, {useMemo, type ReactNode} from 'react'; -import {type StyleProp, StyleSheet, Text, type TextStyle, View, type ViewStyle} from 'react-native'; +import {type StyleProp, StyleSheet, Text, type TextStyle, View, type ViewStyle, type Insets} from 'react-native'; import CompassIcon from '@components/compass_icon'; import {buttonBackgroundStyle, buttonTextStyle} from '@utils/buttonStyles'; +import {changeOpacity} from '@utils/theme'; type ConditionalProps = | {iconName: string; iconSize: number} | {iconName?: never; iconSize?: never} @@ -22,6 +23,8 @@ type Props = ConditionalProps & { onPress: () => void; text: string; iconComponent?: ReactNode; + disabled?: boolean; + hitSlop?: Insets; } const styles = StyleSheet.create({ @@ -43,6 +46,8 @@ const Button = ({ iconName, iconSize, iconComponent, + disabled, + hitSlop, }: Props) => { const bgStyle = useMemo(() => [ buttonBackgroundStyle(theme, size, emphasis, buttonType, buttonState), @@ -63,6 +68,14 @@ const Button = ({ [iconSize], ); + let buttonContainerStyle = StyleSheet.flatten(bgStyle); + if (disabled) { + buttonContainerStyle = { + ...buttonContainerStyle, + backgroundColor: changeOpacity(buttonContainerStyle.backgroundColor! as string, 0.4), + }; + } + let icon: ReactNode; if (iconComponent) { @@ -80,9 +93,11 @@ const Button = ({ return ( {icon} diff --git a/app/components/channel_bookmarks/add_bookmark.tsx b/app/components/channel_bookmarks/add_bookmark.tsx new file mode 100644 index 00000000000..2a71f4a6895 --- /dev/null +++ b/app/components/channel_bookmarks/add_bookmark.tsx @@ -0,0 +1,163 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import React, {useCallback} from 'react'; +import {useIntl} from 'react-intl'; +import {Alert, View, type Insets} from 'react-native'; +import {useSafeAreaInsets} from 'react-native-safe-area-context'; + +import Button from '@components/button'; +import {ITEM_HEIGHT} from '@components/option_item'; +import {Screens} from '@constants'; +import {useTheme} from '@context/theme'; +import {TITLE_HEIGHT} from '@screens/bottom_sheet'; +import {bottomSheet, showModal} from '@screens/navigation'; +import {bottomSheetSnapPoint} from '@utils/helpers'; +import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme'; +import {typography} from '@utils/typography'; + +import CompassIcon from '../compass_icon'; + +import AddBookmarkOptions from './add_bookmark_options'; + +type Props = { + bookmarksCount: number; + canUploadFiles: boolean; + channelId: string; + currentUserId: string; + showLarge: boolean; +} + +const MAX_BOOKMARKS_PER_CHANNEL = 50; +const hitSlop: Insets = {top: 10, bottom: 10, left: 10, right: 10}; + +const getStyleSheet = makeStyleSheetFromTheme((theme) => ({ + container: { + alignSelf: 'flex-start', + marginBottom: 16, + }, + largeButton: { + backgroundColor: changeOpacity(theme.centerChannelColor, 0.08), + height: 32, + paddingHorizontal: 8, + paddingVertical: 0, + borderRadius: 16, + }, + largeButtonText: { + color: theme.centerChannelColor, + lineHeight: undefined, + marginTop: undefined, + ...typography('Body', 100, 'SemiBold'), + paddingRight: 6, + }, + largeButtonIcon: { + color: changeOpacity(theme.centerChannelColor, 0.56), + paddingRight: 4, + paddingTop: 3, + }, + smallButton: { + backgroundColor: undefined, + paddingHorizontal: undefined, + paddingVertical: undefined, + alignItems: 'flex-end', + justifyContent: 'center', + margin: undefined, + top: 3, + right: 0, + }, + smallButtonText: { + color: theme.centerChannelColor, + lineHeight: undefined, + marginTop: undefined, + ...typography('Body', 100, 'SemiBold'), + marginRight: undefined, + padding: undefined, + }, + smallButtonIcon: { + color: changeOpacity(theme.centerChannelColor, 0.56), + }, +})); + +const AddBookmark = ({bookmarksCount, channelId, currentUserId, canUploadFiles, showLarge}: Props) => { + const theme = useTheme(); + const {formatMessage} = useIntl(); + const {bottom} = useSafeAreaInsets(); + const styles = getStyleSheet(theme); + + const onPress = useCallback(() => { + if (bookmarksCount >= MAX_BOOKMARKS_PER_CHANNEL) { + Alert.alert( + formatMessage({id: 'channel_info.add_bookmark', defaultMessage: 'Add a bookmark'}), + formatMessage({ + id: 'channel_info.add_bookmark.max_reached', + defaultMessage: 'This channel has reached the maximum number of bookmarks ({count}).', + }, {count: MAX_BOOKMARKS_PER_CHANNEL}), + ); + return; + } + + if (!canUploadFiles) { + const title = formatMessage({id: 'screens.channel_bookmark_add', defaultMessage: 'Add a bookmark'}); + const closeButton = CompassIcon.getImageSourceSync('close', 24, theme.sidebarHeaderTextColor); + const closeButtonId = 'close-channel-bookmark-add'; + + const options = { + topBar: { + leftButtons: [{ + id: closeButtonId, + icon: closeButton, + testID: 'close.channel_bookmark_add.button', + }], + }, + }; + showModal(Screens.CHANNEL_BOOKMARK, title, { + channelId, + closeButtonId, + type: 'link', + ownerId: currentUserId, + }, options); + return; + } + + const renderContent = () => ( + + ); + + bottomSheet({ + title: formatMessage({id: 'channel_info.add_bookmark', defaultMessage: 'Add a bookmark'}), + renderContent, + snapPoints: [1, bottomSheetSnapPoint(1, (2 * ITEM_HEIGHT), bottom) + TITLE_HEIGHT], + theme, + closeButtonId: 'close-channel-quick-actions', + }); + }, [bottom, bookmarksCount, canUploadFiles, currentUserId, channelId, theme]); + + const button = ( + + + ); +}; + +export default BookmarkDocument; diff --git a/app/components/channel_bookmarks/channel_bookmark/bookmark_icon.tsx b/app/components/channel_bookmarks/channel_bookmark/bookmark_icon.tsx new file mode 100644 index 00000000000..ef107052639 --- /dev/null +++ b/app/components/channel_bookmarks/channel_bookmark/bookmark_icon.tsx @@ -0,0 +1,68 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import {Image, type ImageStyle} from 'expo-image'; +import React, {useState, useCallback} from 'react'; +import {type StyleProp, type TextStyle, type ViewStyle} from 'react-native'; + +import CompassIcon from '@components/compass_icon'; +import Emoji from '@components/emoji'; +import FileIcon from '@components/files/file_icon'; +import {useTheme} from '@context/theme'; + +type Props = { + emoji?: string; + emojiSize: number; + emojiStyle?: StyleProp; + file?: FileInfo | ExtractedFileInfo; + iconSize: number; + imageStyle?: StyleProp; + imageUrl?: string; + genericStyle: StyleProp; +} + +const BookmarkIcon = ({emoji, emojiSize, emojiStyle, file, genericStyle, iconSize, imageStyle, imageUrl}: Props) => { + const theme = useTheme(); + const [hasImageError, setHasImageError] = useState(false); + + const handleImageError = useCallback(() => { + setHasImageError(true); + }, []); + + if (file && !emoji && !hasImageError) { + return ( + + ); + } else if (imageUrl && !emoji && !hasImageError) { + return ( + + ); + } else if (emoji) { + return ( + + ); + } + + return ( + + ); +}; + +export default BookmarkIcon; diff --git a/app/components/channel_bookmarks/channel_bookmark/bookmark_options.tsx b/app/components/channel_bookmarks/channel_bookmark/bookmark_options.tsx new file mode 100644 index 00000000000..ae96992bb54 --- /dev/null +++ b/app/components/channel_bookmarks/channel_bookmark/bookmark_options.tsx @@ -0,0 +1,259 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import Clipboard from '@react-native-clipboard/clipboard'; +import React, {useCallback, useMemo} from 'react'; +import {useIntl} from 'react-intl'; +import {Alert, Text, View} from 'react-native'; +import Share from 'react-native-share'; + +import {deleteChannelBookmark} from '@actions/remote/channel_bookmark'; +import {fetchPublicLink} from '@actions/remote/file'; +import CompassIcon from '@components/compass_icon'; +import OptionItem from '@components/option_item'; +import {Screens} from '@constants'; +import {useServerUrl} from '@context/server'; +import {useTheme} from '@context/theme'; +import {useIsTablet} from '@hooks/device'; +import DownloadWithAction from '@screens/gallery/footer/download_with_action'; +import {dismissBottomSheet, showModal, showOverlay} from '@screens/navigation'; +import {getFullErrorMessage} from '@utils/errors'; +import {isImage, isVideo} from '@utils/file'; +import {showSnackBar} from '@utils/snack_bar'; +import {makeStyleSheetFromTheme} from '@utils/theme'; +import {typography} from '@utils/typography'; + +import type ChannelBookmarkModel from '@typings/database/models/servers/channel_bookmark'; +import type FileModel from '@typings/database/models/servers/file'; +import type {GalleryAction, GalleryFileType, GalleryItemType} from '@typings/screens/gallery'; + +type Props = { + bookmark: ChannelBookmarkModel; + canCopyPublicLink: boolean; + canDeleteBookmarks: boolean; + canDownloadFiles: boolean; + canEditBookmarks: boolean; + file?: FileModel; + setAction: React.Dispatch>; +} + +const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({ + flex: {flex: 1}, + header: { + marginBottom: 12, + }, + headerText: { + color: theme.centerChannelColor, + ...typography('Heading', 600, 'SemiBold'), + }, +})); + +const ChannelBookmarkOptions = ({ + bookmark, + canCopyPublicLink, + canDeleteBookmarks, + canDownloadFiles, + canEditBookmarks, + file, + setAction, +}: Props) => { + const serverUrl = useServerUrl(); + const theme = useTheme(); + const isTablet = useIsTablet(); + const intl = useIntl(); + const styles = getStyleSheet(theme); + const canShare = canDownloadFiles || bookmark.type === 'link'; + + const isVideoFile = useMemo(() => isVideo(file), [file]); + const isImageFile = useMemo(() => isImage(file), [file]); + const galleryItem = useMemo(() => { + if (file) { + const fileInfo = file.toFileInfo(bookmark.ownerId); + let type: GalleryFileType = 'file'; + if (isImageFile) { + type = 'image'; + } else if (isVideoFile) { + type = 'video'; + } + const item: GalleryItemType = { + ...fileInfo, + id: fileInfo.id!, + type, + lastPictureUpdate: 0, + uri: '', + }; + + return item; + } + return null; + }, [bookmark, file]); + + const handleDelete = useCallback(async () => { + const {error} = await deleteChannelBookmark(serverUrl, bookmark.channelId, bookmark.id); + if (error) { + Alert.alert( + intl.formatMessage({id: 'channel_bookmark.delete.failed_title', defaultMessage: 'Error deleting bookmark'}), + intl.formatMessage({id: 'channel_bookmark.delete.failed_detail', defaultMessage: 'Details: {error}'}, { + error: getFullErrorMessage(error), + }), + ); + return; + } + + await dismissBottomSheet(); + }, [bookmark, intl, serverUrl]); + + const onCopy = useCallback(async () => { + await dismissBottomSheet(); + + if (bookmark.type === 'link' && bookmark.linkUrl) { + Clipboard.setString(bookmark.linkUrl); + showSnackBar({barType: 'LINK_COPIED'}); + return; + } + + try { + const publicLink = await fetchPublicLink(serverUrl, bookmark.fileId!); + if ('link' in publicLink) { + Clipboard.setString(publicLink.link); + showSnackBar({barType: 'LINK_COPIED'}); + } else { + showSnackBar({barType: 'LINK_COPY_FAILED'}); + } + } catch { + showSnackBar({barType: 'LINK_COPY_FAILED'}); + } + }, [bookmark, serverUrl]); + + const onDelete = useCallback(async () => { + Alert.alert( + intl.formatMessage({id: 'channel_bookmark.delete.confirm_title', defaultMessage: 'Delete bookmark'}), + intl.formatMessage({id: 'channel_bookmark.delete.confirm', defaultMessage: 'You sure want to delete the bookmark {displayName}?'}, { + displayName: bookmark.displayName, + }), + [{ + text: intl.formatMessage({id: 'channel_bookmark.delete.yes', defaultMessage: 'Yes'}), + style: 'destructive', + isPreferred: true, + onPress: handleDelete, + }, { + text: intl.formatMessage({id: 'channel_bookmark.add.file_cancel', defaultMessage: 'Cancel'}), + style: 'cancel', + }], + ); + }, [bookmark, handleDelete]); + + const onEdit = useCallback(async () => { + await dismissBottomSheet(); + + const title = intl.formatMessage({id: 'screens.channel_bookmark_edit', defaultMessage: 'Edit bookmark'}); + const closeButton = CompassIcon.getImageSourceSync('close', 24, theme.sidebarHeaderTextColor); + const closeButtonId = 'close-channel_bookmark_edit'; + + const options = { + topBar: { + leftButtons: [{ + id: closeButtonId, + icon: closeButton, + testID: 'close.channel_bookmark_edit.button', + }], + }, + }; + + showModal(Screens.CHANNEL_BOOKMARK, title, { + bookmark: bookmark.toApi(), + canDeleteBookmarks, + channelId: bookmark.channelId, + closeButtonId, + file: file?.toFileInfo(bookmark.ownerId), + ownerId: bookmark.ownerId, + type: bookmark.type, + }, options); + }, [bookmark, canDeleteBookmarks, file, intl, theme]); + + const onShare = useCallback(async () => { + await dismissBottomSheet(); + + if (bookmark.type === 'file') { + if (file) { + setAction('sharing'); + showOverlay(Screens.GENERIC_OVERLAY, { + children: ( + + ), + }, {}, bookmark.id); + } + return; + } + + if (bookmark.type === 'link') { + const title = bookmark.displayName; + const url = bookmark.linkUrl!; + Share.open({ + title, + message: title, + url, + showAppsToView: true, + }).catch(() => { + // do nothing + }); + } + }, [bookmark, file, serverUrl, setAction]); + + return ( + <> + {!isTablet && ( + + + {bookmark.displayName} + + + )} + + {canEditBookmarks && + + } + {canCopyPublicLink && + + } + {canShare && + + } + {canDeleteBookmarks && + + } + + + ); +}; + +export default ChannelBookmarkOptions; diff --git a/app/components/channel_bookmarks/channel_bookmark/channel_bookmark.tsx b/app/components/channel_bookmarks/channel_bookmark/channel_bookmark.tsx new file mode 100644 index 00000000000..db6f07a3f94 --- /dev/null +++ b/app/components/channel_bookmarks/channel_bookmark/channel_bookmark.tsx @@ -0,0 +1,169 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import {useManagedConfig} from '@mattermost/react-native-emm'; +import {Button} from '@rneui/base'; +import React, {useCallback, useEffect, useMemo, useState} from 'react'; +import {useIntl, type IntlShape} from 'react-intl'; +import {Alert, StyleSheet} from 'react-native'; +import {useSafeAreaInsets} from 'react-native-safe-area-context'; + +import {ITEM_HEIGHT} from '@components/option_item'; +import {useServerUrl} from '@context/server'; +import {useTheme} from '@context/theme'; +import {useGalleryItem} from '@hooks/gallery'; +import {TITLE_HEIGHT} from '@screens/bottom_sheet'; +import {bottomSheet, dismissOverlay} from '@screens/navigation'; +import {handleDeepLink, matchDeepLink} from '@utils/deep_link'; +import {isDocument} from '@utils/file'; +import {bottomSheetSnapPoint} from '@utils/helpers'; +import {normalizeProtocol, tryOpenURL} from '@utils/url'; + +import BookmarkDetails from './bookmark_details'; +import BookmarkDocument from './bookmark_document'; +import ChannelBookmarkOptions from './bookmark_options'; + +import type ChannelBookmarkModel from '@typings/database/models/servers/channel_bookmark'; +import type FileModel from '@typings/database/models/servers/file'; +import type {GalleryAction} from '@typings/screens/gallery'; + +type Props = { + bookmark: ChannelBookmarkModel; + canDeleteBookmarks: boolean; + canDownloadFiles: boolean; + canEditBookmarks: boolean; + file?: FileModel; + galleryIdentifier: string; + index?: number; + onPress?: (index: number) => void; + publicLinkEnabled: boolean; + siteURL: string; +} + +const styles = StyleSheet.create({ + container: { + alignItems: 'center', + flexDirection: 'row', + paddingVertical: 6, + height: 48, + }, + button: {backgroundColor: 'transparent'}, +}); + +const openLink = async (href: string, serverUrl: string, siteURL: string, intl: IntlShape) => { + const url = normalizeProtocol(href); + if (!url) { + return; + } + + const match = matchDeepLink(url, serverUrl, siteURL); + + const onLinkError = () => { + Alert.alert( + intl.formatMessage({ + id: 'mobile.link.error.title', + defaultMessage: 'Error', + }), + intl.formatMessage({ + id: 'mobile.link.error.text', + defaultMessage: 'Unable to open the link.', + }), + ); + }; + + if (match) { + const {error} = await handleDeepLink(match.url, intl); + if (error) { + tryOpenURL(match.url, onLinkError); + } + } else { + tryOpenURL(url, onLinkError); + } +}; + +const ChannelBookmark = ({ + bookmark, canDeleteBookmarks, canDownloadFiles, canEditBookmarks, + file, galleryIdentifier, index, onPress, publicLinkEnabled, siteURL, +}: Props) => { + const theme = useTheme(); + const managedConfig = useManagedConfig(); + const serverUrl = useServerUrl(); + const intl = useIntl(); + const {bottom} = useSafeAreaInsets(); + const [action, setAction] = useState('none'); + const isDocumentFile = useMemo(() => isDocument(file), [file]); + const canCopyPublicLink = Boolean((bookmark.type === 'link' || (file?.id && publicLinkEnabled)) && managedConfig.copyAndPasteProtection !== 'true'); + + const handlePress = useCallback(() => { + if (bookmark.linkUrl) { + openLink(bookmark.linkUrl, siteURL, serverUrl, intl); + return; + } + + onPress?.(index || 0); + }, [bookmark, index, intl, onPress, serverUrl, siteURL]); + + const handleLongPress = useCallback(() => { + const canShare = canDownloadFiles || bookmark.type === 'link'; + const count = [canCopyPublicLink, canDeleteBookmarks, canShare, canEditBookmarks]. + filter((e) => e).length; + + const renderContent = () => ( + + ); + + bottomSheet({ + title: bookmark.displayName, + renderContent, + snapPoints: [1, bottomSheetSnapPoint(1, (count * ITEM_HEIGHT), bottom) + TITLE_HEIGHT], + theme, + closeButtonId: 'close-channel-bookmark-actions', + }); + }, [bookmark, bottom, canCopyPublicLink, canDeleteBookmarks, canDownloadFiles, canEditBookmarks, file, theme]); + + const {onGestureEvent, ref} = useGalleryItem(galleryIdentifier, index || 0, handlePress); + + useEffect(() => { + if (action === 'none' && bookmark.id) { + dismissOverlay(bookmark.id); + } + }, [action]); + + if (isDocumentFile) { + return ( + + ); + } + + return ( + + ); +}; + +export default ChannelBookmark; diff --git a/app/components/channel_bookmarks/channel_bookmark/index.tsx b/app/components/channel_bookmarks/channel_bookmark/index.tsx new file mode 100644 index 00000000000..81833f8d90b --- /dev/null +++ b/app/components/channel_bookmarks/channel_bookmark/index.tsx @@ -0,0 +1,32 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import {withDatabase, withObservables} from '@nozbe/watermelondb/react'; +import {switchMap} from 'rxjs'; + +import {observeFileById} from '@queries/servers/file'; +import {observeConfigValue} from '@queries/servers/system'; + +import ChannelBookmark from './channel_bookmark'; + +import type {WithDatabaseArgs} from '@typings/database/database'; +import type ChannelBookmarkModel from '@typings/database/models/servers/channel_bookmark'; + +type Props = WithDatabaseArgs & { + bookmark: ChannelBookmarkModel; +} + +const enhanced = withObservables([], ({bookmark, database}: Props) => { + const observed = bookmark.observe(); + const file = observed.pipe( + switchMap((b) => observeFileById(database, b.fileId || '')), + ); + + return { + bookmark: observed, + file, + siteURL: observeConfigValue(database, 'SiteURL'), + }; +}); + +export default withDatabase(enhanced(ChannelBookmark)); diff --git a/app/components/channel_bookmarks/channel_bookmarks.tsx b/app/components/channel_bookmarks/channel_bookmarks.tsx new file mode 100644 index 00000000000..2812c3cc065 --- /dev/null +++ b/app/components/channel_bookmarks/channel_bookmarks.tsx @@ -0,0 +1,179 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import {LinearGradient} from 'expo-linear-gradient'; +import React, {useCallback, useMemo, useState} from 'react'; +import {FlatList, View, type ListRenderItemInfo, type NativeSyntheticEvent, type NativeScrollEvent} from 'react-native'; +import Animated from 'react-native-reanimated'; + +import {GalleryInit} from '@context/gallery'; +import {useTheme} from '@context/theme'; +import {useChannelBookmarkFiles} from '@hooks/files'; +import ChannelBookmarkModel from '@typings/database/models/servers/channel_bookmark'; +import {fileToGalleryItem, openGalleryAtIndex} from '@utils/gallery'; +import {preventDoubleTap} from '@utils/tap'; +import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme'; + +import AddBookmark from './add_bookmark'; +import ChannelBookmark from './channel_bookmark'; + +type Props = { + bookmarks: ChannelBookmarkModel[]; + canAddBookmarks: boolean; + canDeleteBookmarks: boolean; + canDownloadFiles: boolean; + canEditBookmarks: boolean; + canUploadFiles: boolean; + channelId: string; + currentUserId: string; + publicLinkEnabled: boolean; + showInInfo: boolean; + separator?: boolean; +} + +const GRADIENT_LOCATIONS = [0, 0.64, 1]; +const SCROLL_OFFSET = 10; + +const isCloseToBottom = ({layoutMeasurement, contentOffset, contentSize}: NativeScrollEvent) => { + return layoutMeasurement.width + contentOffset.x <= contentSize.width - SCROLL_OFFSET; +}; + +const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({ + container: { + flexDirection: 'row', + }, + separator: { + height: 1, + backgroundColor: changeOpacity(theme.centerChannelColor, 0.08), + marginTop: 8, + marginBottom: 24, + }, + emptyItemSeparator: { + width: 20, + }, + addContainer: { + width: 40, + alignContent: 'center', + }, + gradient: { + height: 48, + width: 68, + position: 'absolute', + right: 0, + }, + channelView: { + paddingHorizontal: 8, + }, +})); + +const ChannelBookmarks = ({ + bookmarks, canAddBookmarks, canDeleteBookmarks, canDownloadFiles, canEditBookmarks, canUploadFiles, + channelId, currentUserId, publicLinkEnabled, showInInfo, separator = true, +}: Props) => { + const galleryIdentifier = `${channelId}-bookmarks`; + const theme = useTheme(); + const styles = getStyleSheet(theme); + const files = useChannelBookmarkFiles(bookmarks, publicLinkEnabled); + const [allowEndFade, setAllowEndFade] = useState(true); + + const attachmentIndex = useCallback((fileId: string) => { + return files.findIndex((file) => file.id === fileId) || 0; + }, [files]); + + const handlePreviewPress = useCallback(preventDoubleTap((idx: number) => { + if (files.length) { + const items = files.map((f) => fileToGalleryItem(f, f.user_id)); + openGalleryAtIndex(galleryIdentifier, idx, items); + } + }), [files]); + + const renderItem = useCallback(({item}: ListRenderItemInfo) => { + return ( + + ); + }, [ + attachmentIndex, bookmarks, canDownloadFiles, canDeleteBookmarks, canEditBookmarks, + handlePreviewPress, publicLinkEnabled, + ]); + + const renderItemSeparator = useCallback(() => (), []); + + const onScrolled = useCallback((e: NativeSyntheticEvent) => { + setAllowEndFade(isCloseToBottom(e.nativeEvent)); + }, []); + + const gradientColors = useMemo(() => [ + theme.centerChannelBg, + changeOpacity(theme.centerChannelBg, 0.6458), + changeOpacity(theme.centerChannelBg, 0), + ], [theme]); + + if (!bookmarks.length && showInInfo && canAddBookmarks) { + return ( + + ); + } + + if (bookmarks.length) { + return ( + + + + + {canAddBookmarks && + + {allowEndFade && + + } + + + } + + {separator && + + } + + + ); + } + + return null; +}; + +export default ChannelBookmarks; diff --git a/app/components/channel_bookmarks/index.ts b/app/components/channel_bookmarks/index.ts new file mode 100644 index 00000000000..1248da1864a --- /dev/null +++ b/app/components/channel_bookmarks/index.ts @@ -0,0 +1,29 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import {withDatabase, withObservables} from '@nozbe/watermelondb/react'; + +import {observeBookmarks, observeCanDeleteBookmarks, observeCanEditBookmarks} from '@queries/servers/channel_bookmark'; +import {observeCanDownloadFiles, observeCanUploadFiles, observeConfigBooleanValue, observeCurrentUserId} from '@queries/servers/system'; + +import ChannelBookmarks from './channel_bookmarks'; + +import type {WithDatabaseArgs} from '@typings/database/database'; + +type Props = WithDatabaseArgs & { + channelId: string; +} + +const enhanced = withObservables([], ({channelId, database}: Props) => { + return { + bookmarks: observeBookmarks(database, channelId), + canDeleteBookmarks: observeCanDeleteBookmarks(database, channelId), + canDownloadFiles: observeCanDownloadFiles(database), + canEditBookmarks: observeCanEditBookmarks(database, channelId), + canUploadFiles: observeCanUploadFiles(database), + currentUserId: observeCurrentUserId(database), + publicLinkEnabled: observeConfigBooleanValue(database, 'EnablePublicLink'), + }; +}); + +export default withDatabase(enhanced(ChannelBookmarks)); diff --git a/app/components/document/index.tsx b/app/components/document/index.tsx new file mode 100644 index 00000000000..66755634484 --- /dev/null +++ b/app/components/document/index.tsx @@ -0,0 +1,168 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import {deleteAsync} from 'expo-file-system'; +import {forwardRef, useImperativeHandle, useRef, useState, type ReactNode, useCallback} from 'react'; +import {useIntl} from 'react-intl'; +import {Platform, StatusBar, type StatusBarStyle} from 'react-native'; +import FileViewer from 'react-native-file-viewer'; +import tinyColor from 'tinycolor2'; + +import {downloadFile} from '@actions/remote/file'; +import {useServerUrl} from '@context/server'; +import {useTheme} from '@context/theme'; +import {alertDownloadDocumentDisabled, alertDownloadFailed, alertFailedToOpenDocument} from '@utils/document'; +import {getFullErrorMessage, isErrorWithMessage} from '@utils/errors'; +import {fileExists, getLocalFilePathFromFile} from '@utils/file'; +import {logDebug} from '@utils/log'; + +import type {ClientResponse, ProgressPromise} from '@mattermost/react-native-network-client'; + +export type DocumentRef = { + handlePreviewPress: () => void; +} + +type DocumentProps = { + canDownloadFiles: boolean; + file: FileInfo; + children: ReactNode; + onProgress: (progress: number) => void; +} + +const Document = forwardRef(({canDownloadFiles, children, onProgress, file}: DocumentProps, ref) => { + const intl = useIntl(); + const serverUrl = useServerUrl(); + const theme = useTheme(); + const [didCancel, setDidCancel] = useState(false); + const [downloading, setDownloading] = useState(false); + const [preview, setPreview] = useState(false); + const downloadTask = useRef>(); + + const cancelDownload = () => { + setDidCancel(true); + if (downloadTask.current?.cancel) { + downloadTask.current.cancel(); + } + }; + + const downloadAndPreviewFile = useCallback(async () => { + setDidCancel(false); + let path; + let exists = false; + + try { + path = decodeURIComponent(file.localPath || ''); + if (path) { + exists = await fileExists(path); + } + + if (!exists) { + path = getLocalFilePathFromFile(serverUrl, file); + exists = await fileExists(path); + } + + if (exists) { + openDocument(); + } else { + setDownloading(true); + downloadTask.current = downloadFile(serverUrl, file.id!, path!); + downloadTask.current?.progress?.(onProgress); + + await downloadTask.current; + onProgress(1); + openDocument(); + } + } catch (error) { + if (path) { + deleteAsync(path, {idempotent: true}); + } + setDownloading(false); + onProgress(0); + + if (!isErrorWithMessage(error) || error.message !== 'cancelled') { + logDebug('error on downloadAndPreviewFile', getFullErrorMessage(error)); + alertDownloadFailed(intl); + } + } + }, [file, onProgress]); + + const setStatusBarColor = useCallback((style: StatusBarStyle = 'light-content') => { + if (Platform.OS === 'ios') { + if (style) { + StatusBar.setBarStyle(style, true); + } else { + const headerColor = tinyColor(theme.sidebarHeaderBg); + let barStyle: StatusBarStyle = 'light-content'; + if (headerColor.isLight() && Platform.OS === 'ios') { + barStyle = 'dark-content'; + } + StatusBar.setBarStyle(barStyle, true); + } + } + }, [theme]); + + const openDocument = useCallback(async () => { + if (!didCancel && !preview) { + let path = decodeURIComponent(file.localPath || ''); + let exists = false; + if (path) { + exists = await fileExists(path); + } + + if (!exists) { + path = getLocalFilePathFromFile(serverUrl, file); + } + + setPreview(true); + setStatusBarColor('dark-content'); + FileViewer.open(path!.replace('file://', ''), { + displayName: decodeURIComponent(file.name), + onDismiss: onDonePreviewingFile, + showOpenWithDialog: true, + showAppsSuggestions: true, + }).then(() => { + setDownloading(false); + onProgress(0); + }).catch(() => { + alertFailedToOpenDocument(file, intl); + onDonePreviewingFile(); + + if (path) { + deleteAsync(path, {idempotent: true}); + } + }); + } + }, [didCancel, preview, file, onProgress, setStatusBarColor]); + + const handlePreviewPress = useCallback(async () => { + if (!canDownloadFiles) { + alertDownloadDocumentDisabled(intl); + return; + } + + if (downloading) { + onProgress(0); + cancelDownload(); + setDownloading(false); + } else { + downloadAndPreviewFile(); + } + }, [canDownloadFiles, downloadAndPreviewFile, downloading, intl, onProgress, openDocument]); + + const onDonePreviewingFile = () => { + onProgress(0); + setDownloading(false); + setPreview(false); + setStatusBarColor(); + }; + + useImperativeHandle(ref, () => ({ + handlePreviewPress, + }), [handlePreviewPress]); + + return children; +}); + +Document.displayName = 'Document'; + +export default Document; diff --git a/app/components/files/document_file.tsx b/app/components/files/document_file.tsx index 1d5763c1ab1..dbf65bdb7fc 100644 --- a/app/components/files/document_file.tsx +++ b/app/components/files/document_file.tsx @@ -1,34 +1,18 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {deleteAsync} from 'expo-file-system'; import React, {forwardRef, useImperativeHandle, useRef, useState} from 'react'; -import {useIntl} from 'react-intl'; -import {Platform, StatusBar, type StatusBarStyle, StyleSheet, TouchableOpacity, View} from 'react-native'; -import FileViewer from 'react-native-file-viewer'; -import tinyColor from 'tinycolor2'; +import {StyleSheet, TouchableOpacity, View} from 'react-native'; +import Document, {type DocumentRef} from '@components/document'; import ProgressBar from '@components/progress_bar'; -import {DOWNLOAD_TIMEOUT} from '@constants/network'; -import {useServerUrl} from '@context/server'; import {useTheme} from '@context/theme'; -import NetworkManager from '@managers/network_manager'; -import {alertDownloadDocumentDisabled, alertDownloadFailed, alertFailedToOpenDocument} from '@utils/document'; -import {getFullErrorMessage, isErrorWithMessage} from '@utils/errors'; -import {fileExists, getLocalFilePathFromFile} from '@utils/file'; -import {logDebug} from '@utils/log'; import FileIcon from './file_icon'; -import type {Client} from '@client/rest'; -import type {ClientResponse, ProgressPromise} from '@mattermost/react-native-network-client'; - -export type DocumentFileRef = { - handlePreviewPress: () => void; -} - type DocumentFileProps = { backgroundColor?: string; + disabled?: boolean; canDownloadFiles: boolean; file: FileInfo; } @@ -43,122 +27,13 @@ const styles = StyleSheet.create({ }, }); -const DocumentFile = forwardRef(({backgroundColor, canDownloadFiles, file}: DocumentFileProps, ref) => { - const intl = useIntl(); - const serverUrl = useServerUrl(); +const DocumentFile = forwardRef(({backgroundColor, canDownloadFiles, disabled = false, file}: DocumentFileProps, ref) => { const theme = useTheme(); - const [didCancel, setDidCancel] = useState(false); - const [downloading, setDownloading] = useState(false); - const [preview, setPreview] = useState(false); const [progress, setProgress] = useState(0); - let client: Client | undefined; - try { - client = NetworkManager.getClient(serverUrl); - } catch { - // do nothing - } - const downloadTask = useRef>(); - - const cancelDownload = () => { - setDidCancel(true); - if (downloadTask.current?.cancel) { - downloadTask.current.cancel(); - } - }; - - const downloadAndPreviewFile = async () => { - setDidCancel(false); - let path; - - try { - path = getLocalFilePathFromFile(serverUrl, file); - const exists = await fileExists(path); - if (exists) { - openDocument(); - } else { - setDownloading(true); - downloadTask.current = client?.apiClient.download(client?.getFileRoute(file.id!), path!.replace('file://', ''), {timeoutInterval: DOWNLOAD_TIMEOUT}); - downloadTask.current?.progress?.(setProgress); - - await downloadTask.current; - setProgress(1); - openDocument(); - } - } catch (error) { - if (path) { - deleteAsync(path, {idempotent: true}); - } - setDownloading(false); - setProgress(0); - - if (!isErrorWithMessage(error) || error.message !== 'cancelled') { - logDebug('error on downloadAndPreviewFile', getFullErrorMessage(error)); - alertDownloadFailed(intl); - } - } - }; + const document = useRef(null); const handlePreviewPress = async () => { - if (!canDownloadFiles) { - alertDownloadDocumentDisabled(intl); - return; - } - - if (downloading && progress < 1) { - cancelDownload(); - } else if (downloading) { - setProgress(0); - setDidCancel(true); - setDownloading(false); - } else { - downloadAndPreviewFile(); - } - }; - - const onDonePreviewingFile = () => { - setProgress(0); - setDownloading(false); - setPreview(false); - setStatusBarColor(); - }; - - const openDocument = () => { - if (!didCancel && !preview) { - const path = getLocalFilePathFromFile(serverUrl, file); - setPreview(true); - setStatusBarColor('dark-content'); - FileViewer.open(path!, { - displayName: file.name, - onDismiss: onDonePreviewingFile, - showOpenWithDialog: true, - showAppsSuggestions: true, - }).then(() => { - setDownloading(false); - setProgress(0); - }).catch(() => { - alertFailedToOpenDocument(file, intl); - onDonePreviewingFile(); - - if (path) { - deleteAsync(path, {idempotent: true}); - } - }); - } - }; - - const setStatusBarColor = (style: StatusBarStyle = 'light-content') => { - if (Platform.OS === 'ios') { - if (style) { - StatusBar.setBarStyle(style, true); - } else { - const headerColor = tinyColor(theme.sidebarHeaderBg); - let barStyle: StatusBarStyle = 'light-content'; - if (headerColor.isLight() && Platform.OS === 'ios') { - barStyle = 'dark-content'; - } - StatusBar.setBarStyle(barStyle, true); - } - } + document.current?.handlePreviewPress(); }; useImperativeHandle(ref, () => ({ @@ -173,13 +48,13 @@ const DocumentFile = forwardRef(({background ); let fileAttachmentComponent = icon; - if (downloading) { + if (progress) { fileAttachmentComponent = ( <> {icon} @@ -188,9 +63,19 @@ const DocumentFile = forwardRef(({background } return ( - - {fileAttachmentComponent} - + + + {fileAttachmentComponent} + + ); }); diff --git a/app/components/files/file.tsx b/app/components/files/file.tsx index 4f3e8efa6c1..bd3ce0d4083 100644 --- a/app/components/files/file.tsx +++ b/app/components/files/file.tsx @@ -11,7 +11,7 @@ import {useGalleryItem} from '@hooks/gallery'; import {isDocument, isImage, isVideo} from '@utils/file'; import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme'; -import DocumentFile, {type DocumentFileRef} from './document_file'; +import DocumentFile from './document_file'; import FileIcon from './file_icon'; import FileInfo from './file_info'; import FileOptionsIcon from './file_options_icon'; @@ -19,6 +19,8 @@ import ImageFile from './image_file'; import ImageFileOverlay from './image_file_overlay'; import VideoFile from './video_file'; +import type {DocumentRef} from '@components/document'; + type FileProps = { canDownloadFiles: boolean; file: FileInfo; @@ -36,6 +38,7 @@ type FileProps = { showDate?: boolean; updateFileForGallery: (idx: number, file: FileInfo) => void; asCard?: boolean; + isPressDisabled?: boolean; }; const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => { @@ -79,8 +82,9 @@ const File = ({ showDate = false, updateFileForGallery, wrapperWidth = 300, + isPressDisabled = false, }: FileProps) => { - const document = useRef(null); + const document = useRef(null); const theme = useTheme(); const style = getStyleSheet(theme); @@ -101,6 +105,7 @@ const File = ({ const renderCardWithImage = (fileIcon: JSX.Element) => { const fileInfo = ( + + @@ -184,6 +196,7 @@ const File = ({ const fileInfo = ( { }; }); -const FileInfo = ({file, channelName, showDate, onPress}: FileInfoProps) => { +const FileInfo = ({disabled, file, channelName, showDate, onPress}: FileInfoProps) => { const theme = useTheme(); const style = getStyleSheet(theme); + return ( - + - {file.name.trim()} + {decodeURIComponent(file.name.trim())} {channelName && diff --git a/app/components/files/image_file.tsx b/app/components/files/image_file.tsx index 9c95945d28f..c604ef7cb92 100644 --- a/app/components/files/image_file.tsx +++ b/app/components/files/image_file.tsx @@ -5,7 +5,7 @@ import {LinearGradient} from 'expo-linear-gradient'; import React, {useCallback, useMemo, useState} from 'react'; import {StyleSheet, useWindowDimensions, View} from 'react-native'; -import {buildFilePreviewUrl, buildFileThumbnailUrl} from '@actions/remote/file'; +import {buildFilePreviewUrl, buildFileThumbnailUrl, buildFileUrl} from '@actions/remote/file'; import CompassIcon from '@components/compass_icon'; import ProgressiveImage from '@components/progressive_image'; import {useServerUrl} from '@context/server'; @@ -99,12 +99,17 @@ const ImageFile = ({ } else if (file.id) { if (file.mini_preview && file.mime_type) { props.thumbnailUri = `data:${file.mime_type};base64,${file.mini_preview}`; - } else { + } else if (file.has_preview_image) { props.thumbnailUri = buildFileThumbnailUrl(serverUrl, file.id); } - props.imageUri = buildFilePreviewUrl(serverUrl, file.id); + if (file.has_preview_image) { + props.imageUri = buildFilePreviewUrl(serverUrl, file.id); + } else { + props.imageUri = buildFileUrl(serverUrl, file.id, file.update_at); + } props.inViewPort = inViewPort; } + return props; }; diff --git a/app/components/floating_text_input_label/index.tsx b/app/components/floating_text_input_label/index.tsx index 7f8a91b3dd1..69914bc28dd 100644 --- a/app/components/floating_text_input_label/index.tsx +++ b/app/components/floating_text_input_label/index.tsx @@ -59,7 +59,7 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({ maxWidth: 315, }, readOnly: { - backgroundColor: changeOpacity(theme.centerChannelBg, 0.16), + backgroundColor: changeOpacity(theme.centerChannelColor, 0.16), }, smallLabel: { fontSize: 10, diff --git a/app/components/post_draft/post_input/post_input.tsx b/app/components/post_draft/post_input/post_input.tsx index b04a7998691..756a05e7737 100644 --- a/app/components/post_draft/post_input/post_input.tsx +++ b/app/components/post_draft/post_input/post_input.tsx @@ -220,7 +220,7 @@ export default function PostInput({ addFiles(await extractFileInfo(files)); }, [addFiles, intl]); - const handleHardwareEnterPress = () => { + const handleHardwareEnterPress = useCallback(() => { const topScreen = NavigationStore.getVisibleScreen(); let sourceScreen: AvailableScreens = Screens.CHANNEL; if (rootId) { @@ -231,9 +231,9 @@ export default function PostInput({ if (topScreen === sourceScreen) { sendMessage(); } - }; + }, [sendMessage, rootId, isTablet]); - const handleHardwareShiftEnter = () => { + const handleHardwareShiftEnter = useCallback(() => { const topScreen = NavigationStore.getVisibleScreen(); let sourceScreen: AvailableScreens = Screens.CHANNEL; if (rootId) { @@ -251,7 +251,7 @@ export default function PostInput({ updateCursorPosition((pos) => pos + 1); propagateValue(newValue!); } - }; + }, [rootId, isTablet, updateValue, updateCursorPosition, cursorPosition, propagateValue]); const onAppStateChange = useCallback((appState: AppStateStatus) => { if (appState !== 'active' && previousAppState.current === 'active') { @@ -307,10 +307,11 @@ export default function PostInput({ } }, [value]); - useHardwareKeyboardEvents({ + const events = useMemo(() => ({ onEnterPressed: handleHardwareEnterPress, onShiftEnterPressed: handleHardwareShiftEnter, - }); + }), [handleHardwareEnterPress, handleHardwareShiftEnter]); + useHardwareKeyboardEvents(events); return ( ); } diff --git a/app/components/post_draft/send_handler/send_handler.tsx b/app/components/post_draft/send_handler/send_handler.tsx index e9aa0d71fb8..a936bbe81ad 100644 --- a/app/components/post_draft/send_handler/send_handler.tsx +++ b/app/components/post_draft/send_handler/send_handler.tsx @@ -166,7 +166,7 @@ export default function SendHandler({ const sendCommand = useCallback(async () => { if (value.trim().startsWith('/call')) { - const {handled, error} = await handleCallsSlashCommand(value.trim(), serverUrl, channelId, rootId, currentUserId, intl); + const {handled, error} = await handleCallsSlashCommand(value.trim(), serverUrl, channelId, channelType ?? '', rootId, currentUserId, intl); if (handled) { setSendingMessage(false); clearDraft(); diff --git a/app/components/post_list/more_messages/more_messages.tsx b/app/components/post_list/more_messages/more_messages.tsx index e98632e898a..cd18f4cb0e1 100644 --- a/app/components/post_list/more_messages/more_messages.tsx +++ b/app/components/post_list/more_messages/more_messages.tsx @@ -13,6 +13,7 @@ import FormattedText from '@components/formatted_text'; import TouchableWithFeedback from '@components/touchable_with_feedback'; import {Events} from '@constants'; import {useServerUrl} from '@context/server'; +import {useIsTablet} from '@hooks/device'; import useDidUpdate from '@hooks/did_update'; import EphemeralStore from '@store/ephemeral_store'; import {makeStyleSheetFromTheme, hexToHue, changeOpacity} from '@utils/theme'; @@ -116,6 +117,7 @@ const MoreMessages = ({ }: Props) => { const serverUrl = useServerUrl(); const insets = useSafeAreaInsets(); + const isTablet = useIsTablet(); const pressed = useRef(false); const resetting = useRef(false); const initialScroll = useRef(false); @@ -127,7 +129,7 @@ const MoreMessages = ({ const callsAdjustment = useCallsAdjustment(serverUrl, channelId); // The final top: - const adjustedTop = insets.top + callsAdjustment; + const adjustedTop = (isTablet ? 0 : insets.top) + callsAdjustment; const BARS_FACTOR = Math.abs((1) / (HIDDEN_TOP - SHOWN_TOP)); diff --git a/app/components/post_list/post/body/content/message_attachments/__snapshots__/attachment_author.test.tsx.snap b/app/components/post_list/post/body/content/message_attachments/__snapshots__/attachment_author.test.tsx.snap index 39f9714c0d2..abfe08e023b 100644 --- a/app/components/post_list/post/body/content/message_attachments/__snapshots__/attachment_author.test.tsx.snap +++ b/app/components/post_list/post/body/content/message_attachments/__snapshots__/attachment_author.test.tsx.snap @@ -10,6 +10,7 @@ exports[`AttachmentAuthor it matches snapshot when both name and icon are provid } > ([ @@ -174,6 +178,7 @@ export const SCREENS_WITH_TRANSPARENT_BACKGROUND = new Set([ PERMALINK, REVIEW_APP, SNACK_BAR, + GENERIC_OVERLAY, ]); export const SCREENS_AS_BOTTOM_SHEET = new Set([ diff --git a/app/constants/snack_bar.ts b/app/constants/snack_bar.ts index 57491b93fc2..f36053ab00a 100644 --- a/app/constants/snack_bar.ts +++ b/app/constants/snack_bar.ts @@ -10,6 +10,7 @@ export const SNACK_BAR_TYPE = keyMirror({ FOLLOW_THREAD: null, INFO_COPIED: null, LINK_COPIED: null, + LINK_COPY_FAILED: null, MESSAGE_COPIED: null, MUTE_CHANNEL: null, REMOVE_CHANNEL_USER: null, @@ -65,6 +66,13 @@ export const SNACK_BAR_CONFIG: Record = { canUndo: false, type: MESSAGE_TYPE.SUCCESS, }, + LINK_COPY_FAILED: { + id: t('gallery.copy_link.failed'), + defaultMessage: 'Failed to copy link to clipboard', + iconName: 'link-variant', + canUndo: false, + type: MESSAGE_TYPE.ERROR, + }, MESSAGE_COPIED: { id: t('snack.bar.message.copied'), defaultMessage: 'Text copied to clipboard', diff --git a/app/constants/view.ts b/app/constants/view.ts index 50ee021a0d0..d42ab69148b 100644 --- a/app/constants/view.ts +++ b/app/constants/view.ts @@ -27,6 +27,7 @@ export const CALL_ERROR_BAR_HEIGHT = 52; export const CALL_NOTIFICATION_BAR_HEIGHT = 40; export const ANNOUNCEMENT_BAR_HEIGHT = 40; +export const BOOKMARKS_BAR_HEIGHT = 48; export const HOME_PADDING = { paddingLeft: 18, diff --git a/app/constants/websocket.ts b/app/constants/websocket.ts index 100773417f0..d4034e213b7 100644 --- a/app/constants/websocket.ts +++ b/app/constants/websocket.ts @@ -100,5 +100,9 @@ const WebsocketEvents = { GROUP_DISSOCIATED_TO_TEAM: 'received_group_not_associated_to_team', GROUP_ASSOCIATED_TO_CHANNEL: 'received_group_associated_to_channel', GROUP_DISSOCIATED_TO_CHANNEL: 'received_group_not_associated_to_channel', + CHANNEL_BOOKMARK_CREATED: 'channel_bookmark_created', + CHANNEL_BOOKMARK_UPDATED: 'channel_bookmark_updated', + CHANNEL_BOOKMARK_SORTED: 'channel_bookmark_sorted', + CHANNEL_BOOKMARK_DELETED: 'channel_bookmark_deleted', }; export default WebsocketEvents; diff --git a/app/database/manager/__mocks__/index.ts b/app/database/manager/__mocks__/index.ts index 8c9d2eada8c..a58b052eaa3 100644 --- a/app/database/manager/__mocks__/index.ts +++ b/app/database/manager/__mocks__/index.ts @@ -11,7 +11,7 @@ import {DatabaseType, MIGRATION_EVENTS, MM_TABLES} from '@constants/database'; import AppDatabaseMigrations from '@database/migration/app'; import ServerDatabaseMigrations from '@database/migration/server'; import {InfoModel, GlobalModel, ServersModel} from '@database/models/app'; -import {CategoryModel, CategoryChannelModel, ChannelModel, ChannelInfoModel, ChannelMembershipModel, ConfigModel, CustomEmojiModel, DraftModel, FileModel, +import {CategoryModel, CategoryChannelModel, ChannelModel, ChannelBookmarkModel, ChannelInfoModel, ChannelMembershipModel, ConfigModel, CustomEmojiModel, DraftModel, FileModel, GroupModel, GroupChannelModel, GroupTeamModel, GroupMembershipModel, MyChannelModel, MyChannelSettingsModel, MyTeamModel, PostModel, PostsInChannelModel, PostsInThreadModel, PreferenceModel, ReactionModel, RoleModel, SystemModel, TeamModel, TeamChannelHistoryModel, TeamMembershipModel, TeamSearchHistoryModel, @@ -47,7 +47,7 @@ class DatabaseManager { constructor() { this.appModels = [InfoModel, GlobalModel, ServersModel]; this.serverModels = [ - CategoryModel, CategoryChannelModel, ChannelModel, ChannelInfoModel, ChannelMembershipModel, ConfigModel, CustomEmojiModel, DraftModel, FileModel, + CategoryModel, CategoryChannelModel, ChannelModel, ChannelBookmarkModel, ChannelInfoModel, ChannelMembershipModel, ConfigModel, CustomEmojiModel, DraftModel, FileModel, GroupModel, GroupChannelModel, GroupTeamModel, GroupMembershipModel, MyChannelModel, MyChannelSettingsModel, MyTeamModel, PostModel, PostsInChannelModel, PostsInThreadModel, PreferenceModel, ReactionModel, RoleModel, SystemModel, TeamModel, TeamChannelHistoryModel, TeamMembershipModel, TeamSearchHistoryModel, diff --git a/app/database/manager/index.ts b/app/database/manager/index.ts index 8d029910f80..e5edf058b0f 100644 --- a/app/database/manager/index.ts +++ b/app/database/manager/index.ts @@ -12,7 +12,7 @@ import {DatabaseType, MIGRATION_EVENTS, MM_TABLES} from '@constants/database'; import AppDatabaseMigrations from '@database/migration/app'; import ServerDatabaseMigrations from '@database/migration/server'; import {InfoModel, GlobalModel, ServersModel} from '@database/models/app'; -import {CategoryModel, CategoryChannelModel, ChannelModel, ChannelInfoModel, ChannelMembershipModel, CustomEmojiModel, DraftModel, FileModel, +import {CategoryModel, CategoryChannelModel, ChannelModel, ChannelBookmarkModel, ChannelInfoModel, ChannelMembershipModel, CustomEmojiModel, DraftModel, FileModel, GroupModel, GroupChannelModel, GroupTeamModel, GroupMembershipModel, MyChannelModel, MyChannelSettingsModel, MyTeamModel, PostModel, PostsInChannelModel, PostsInThreadModel, PreferenceModel, ReactionModel, RoleModel, SystemModel, TeamModel, TeamChannelHistoryModel, TeamMembershipModel, TeamSearchHistoryModel, @@ -44,7 +44,7 @@ class DatabaseManager { constructor() { this.appModels = [InfoModel, GlobalModel, ServersModel]; this.serverModels = [ - CategoryModel, CategoryChannelModel, ChannelModel, ChannelInfoModel, ChannelMembershipModel, ConfigModel, CustomEmojiModel, DraftModel, FileModel, + CategoryModel, CategoryChannelModel, ChannelModel, ChannelBookmarkModel, ChannelInfoModel, ChannelMembershipModel, ConfigModel, CustomEmojiModel, DraftModel, FileModel, GroupModel, GroupChannelModel, GroupTeamModel, GroupMembershipModel, MyChannelModel, MyChannelSettingsModel, MyTeamModel, PostModel, PostsInChannelModel, PostsInThreadModel, PreferenceModel, ReactionModel, RoleModel, SystemModel, TeamModel, TeamChannelHistoryModel, TeamMembershipModel, TeamSearchHistoryModel, diff --git a/app/database/migration/server/index.ts b/app/database/migration/server/index.ts index 6fc926695c0..73a2a34cd49 100644 --- a/app/database/migration/server/index.ts +++ b/app/database/migration/server/index.ts @@ -4,13 +4,37 @@ // NOTE : To implement migration, please follow this document // https://nozbe.github.io/WatermelonDB/Advanced/Migrations.html -import {addColumns, schemaMigrations} from '@nozbe/watermelondb/Schema/migrations'; +import {addColumns, createTable, schemaMigrations} from '@nozbe/watermelondb/Schema/migrations'; import {MM_TABLES} from '@constants/database'; -const {CHANNEL_INFO, DRAFT, POST} = MM_TABLES.SERVER; +const {CHANNEL_BOOKMARK, CHANNEL_INFO, DRAFT, POST} = MM_TABLES.SERVER; export default schemaMigrations({migrations: [ + { + toVersion: 4, + steps: [ + createTable({ + name: CHANNEL_BOOKMARK, + columns: [ + {name: 'create_at', type: 'number'}, + {name: 'update_at', type: 'number'}, + {name: 'delete_at', type: 'number'}, + {name: 'channel_id', type: 'string', isIndexed: true}, + {name: 'owner_id', type: 'string'}, + {name: 'file_id', type: 'string', isOptional: true}, + {name: 'display_name', type: 'string'}, + {name: 'sort_order', type: 'number'}, + {name: 'link_url', type: 'string', isOptional: true}, + {name: 'image_url', type: 'string', isOptional: true}, + {name: 'emoji', type: 'string', isOptional: true}, + {name: 'type', type: 'string'}, + {name: 'original_id', type: 'string', isOptional: true}, + {name: 'parent_id', type: 'string', isOptional: true}, + ], + }), + ], + }, { toVersion: 3, steps: [ diff --git a/app/database/models/server/channel.ts b/app/database/models/server/channel.ts index 80c40fcfd4d..420e19b5a0b 100644 --- a/app/database/models/server/channel.ts +++ b/app/database/models/server/channel.ts @@ -9,6 +9,7 @@ import {MM_TABLES} from '@constants/database'; import type {Query, Relation} from '@nozbe/watermelondb'; import type CategoryChannelModel from '@typings/database/models/servers/category_channel'; import type ChannelModelInterface from '@typings/database/models/servers/channel'; +import type ChannelBookmarkModel from '@typings/database/models/servers/channel_bookmark'; import type ChannelInfoModel from '@typings/database/models/servers/channel_info'; import type ChannelMembershipModel from '@typings/database/models/servers/channel_membership'; import type DraftModel from '@typings/database/models/servers/draft'; @@ -21,6 +22,7 @@ import type UserModel from '@typings/database/models/servers/user'; const { CATEGORY_CHANNEL, CHANNEL, + CHANNEL_BOOKMARK, CHANNEL_INFO, CHANNEL_MEMBERSHIP, DRAFT, @@ -41,6 +43,9 @@ export default class ChannelModel extends Model implements ChannelModelInterface /** associations : Describes every relationship to this table. */ static associations: Associations = { + /** A CHANNEL can be associated with multiple CHANNEL_BOOKMARK (relationship is 1:N) */ + [CHANNEL_BOOKMARK]: {type: 'has_many', foreignKey: 'channel_id'}, + /** A CHANNEL can be associated with multiple CHANNEL_MEMBERSHIP (relationship is 1:N) */ [CHANNEL_MEMBERSHIP]: {type: 'has_many', foreignKey: 'channel_id'}, @@ -109,6 +114,9 @@ export default class ChannelModel extends Model implements ChannelModelInterface /** drafts : All drafts for this channel */ @children(DRAFT) drafts!: Query; + /** bookmarks : All bookmarks for this channel */ + @children(CHANNEL_BOOKMARK) bookmarks!: Query; + /** posts : All posts made in that channel */ @children(POST) posts!: Query; diff --git a/app/database/models/server/channel_bookmark.ts b/app/database/models/server/channel_bookmark.ts new file mode 100644 index 00000000000..084b23dae23 --- /dev/null +++ b/app/database/models/server/channel_bookmark.ts @@ -0,0 +1,115 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import {field, immutableRelation} from '@nozbe/watermelondb/decorators'; +import Model, {type Associations} from '@nozbe/watermelondb/Model'; + +import {MM_TABLES} from '@constants/database'; + +import type {Relation} from '@nozbe/watermelondb'; +import type ChannelModel from '@typings/database/models/servers/channel'; +import type ChannelBookmarkInterface from '@typings/database/models/servers/channel_bookmark'; +import type FileModel from '@typings/database/models/servers/file'; +import type UserModel from '@typings/database/models/servers/user'; + +const { + CHANNEL, + CHANNEL_BOOKMARK, + FILE, + USER, +} = MM_TABLES.SERVER; + +/** + * The Channel model represents a channel in the Mattermost app. + */ +export default class ChannelBookmarkModel extends Model implements ChannelBookmarkInterface { + /** table (name) : Channel */ + static table = CHANNEL_BOOKMARK; + + /** associations : Describes every relationship to this table. */ + static associations: Associations = { + + /** A CHANNEL can be associated to CHANNEL_BOOKMARK (relationship is 1:N) */ + [CHANNEL]: {type: 'belongs_to', key: 'channel_id'}, + + /** A USER can create multiple CHANNEL_BOOKMARK (relationship is 1:N) */ + [USER]: {type: 'belongs_to', key: 'owner_id'}, + + /** A FILE is associated with one CHANNEL_BOOKMARK**/ + [FILE]: {type: 'has_many', foreignKey: 'file_id'}, + + }; + + /** create_at : The creation date for this channel bookmark */ + @field('create_at') createAt!: number; + + /** update_at : The timestamp to when this channel bookmark was last updated on the server */ + @field('update_at') updateAt!: number; + + /** delete_at : The deletion/archived date of this channel bookmark */ + @field('delete_at') deleteAt!: number; + + /** channel_id : The channel to which this bookmarks belongs */ + @field('channel_id') channelId!: string; + + /** owner_id : The user who created this channel bookmark */ + @field('owner_id') ownerId!: string; + + /** file_id : The file attached this channel bookmark */ + @field('file_id') fileId?: string; + + /** display_name : The channel bookmark display name (e.g. Important document ) */ + @field('display_name') displayName!: string; + + /** sort_order : the order in which the bookmark is displayed in the UI. */ + @field('sort_order') sortOrder!: number; + + /** link_url : The channel bookmark url if of type link */ + @field('link_url') linkUrl?: string; + + /** image_url : The channel bookmark image url if of type link (optional) */ + @field('image_url') imageUrl?: string; + + /** emoji : The channel bookmark emoji (optional) */ + @field('emoji') emoji?: string; + + /** type : The channel bookmark type it can be link or file */ + @field('type') type!: ChannelBookmarkType; + + /** original_id : The channel bookmark original identifier before it was edited */ + @field('original_id') originalId?: string; + + /** parent_id : The channel bookmark parent in case is nested */ + @field('parent_id') parentId?: string; + + /** channel : The CHANNEL to which this CHANNEL_BOOKMARK belongs */ + @immutableRelation(CHANNEL, 'channel_id') channel!: Relation; + + /** creator : The USER who created this CHANNEL_BOOKMARK */ + @immutableRelation(USER, 'owner_id') owner!: Relation; + + /** file : The FILE attached to this CHANNEL_BOOKMARK */ + @immutableRelation(FILE, 'file_id') file!: Relation; + + toApi = () => { + const b: ChannelBookmark = { + id: this.id, + create_at: this.createAt, + update_at: this.updateAt, + delete_at: this.deleteAt, + channel_id: this.channelId, + owner_id: this.ownerId, + file_id: this.fileId, + display_name: this.displayName, + sort_order: this.sortOrder, + link_url: this.linkUrl, + image_url: this.imageUrl, + emoji: this.emoji, + type: this.type, + original_id: this.originalId, + parent_id: this.parentId, + }; + + return b; + }; +} diff --git a/app/database/models/server/file.ts b/app/database/models/server/file.ts index 45c62e49707..96ba7fc2aef 100644 --- a/app/database/models/server/file.ts +++ b/app/database/models/server/file.ts @@ -10,7 +10,7 @@ import type {Relation} from '@nozbe/watermelondb'; import type FileModelInterface from '@typings/database/models/servers/file'; import type PostModel from '@typings/database/models/servers/post'; -const {FILE, POST} = MM_TABLES.SERVER; +const {CHANNEL_BOOKMARK, FILE, POST} = MM_TABLES.SERVER; /** * The File model works in pair with the Post model. It hosts information about the files attached to a Post @@ -22,6 +22,9 @@ export default class FileModel extends Model implements FileModelInterface { /** associations : Describes every relationship to this table. */ static associations: Associations = { + /** A CHANNEL_BOOKMARK has a 1:1 relationship with FILE. */ + [CHANNEL_BOOKMARK]: {type: 'has_many', foreignKey: 'file_id'}, + /** A POST has a 1:N relationship with FILE. */ [POST]: {type: 'belongs_to', key: 'post_id'}, }; diff --git a/app/database/models/server/index.ts b/app/database/models/server/index.ts index 8368b53dad2..6ea0df9062e 100644 --- a/app/database/models/server/index.ts +++ b/app/database/models/server/index.ts @@ -3,9 +3,10 @@ export {default as CategoryModel} from './category'; export {default as CategoryChannelModel} from './category_channel'; +export {default as ChannelModel} from './channel'; +export {default as ChannelBookmarkModel} from './channel_bookmark'; export {default as ChannelInfoModel} from './channel_info'; export {default as ChannelMembershipModel} from './channel_membership'; -export {default as ChannelModel} from './channel'; export {default as ConfigModel} from './config'; export {default as CustomEmojiModel} from './custom_emoji'; export {default as DraftModel} from './draft'; diff --git a/app/database/models/server/user.ts b/app/database/models/server/user.ts index c9c22154e5d..1edbaba54a6 100644 --- a/app/database/models/server/user.ts +++ b/app/database/models/server/user.ts @@ -20,6 +20,7 @@ import type {UserMentionKey, HighlightWithoutNotificationKey} from '@typings/glo const { CHANNEL, + CHANNEL_BOOKMARK, CHANNEL_MEMBERSHIP, POST, PREFERENCE, @@ -43,6 +44,9 @@ export default class UserModel extends Model implements UserModelInterface { /** USER has a 1:N relationship with CHANNEL. A user can create multiple channels */ [CHANNEL]: {type: 'has_many', foreignKey: 'creator_id'}, + /** USER has a 1:N relationship with CHANNEL_BOOKMARK. A user can create multiple channels */ + [CHANNEL_BOOKMARK]: {type: 'has_many', foreignKey: 'owner_id'}, + /** USER has a 1:N relationship with CHANNEL_MEMBERSHIP. A user can be part of multiple channels */ [CHANNEL_MEMBERSHIP]: {type: 'has_many', foreignKey: 'user_id'}, diff --git a/app/database/operator/base_data_operator/index.ts b/app/database/operator/base_data_operator/index.ts index aace2f2aa26..1b77b665f53 100644 --- a/app/database/operator/base_data_operator/index.ts +++ b/app/database/operator/base_data_operator/index.ts @@ -1,7 +1,7 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {Database, Q} from '@nozbe/watermelondb'; +import {Database, Model, Q} from '@nozbe/watermelondb'; import {OperationType} from '@constants/database'; import { @@ -11,7 +11,6 @@ import { } from '@database/operator/utils/general'; import {logWarning} from '@utils/log'; -import type Model from '@nozbe/watermelondb/Model'; import type { HandleRecordsArgs, OperationArgs, @@ -40,10 +39,11 @@ export default class BaseDataOperator { * the same value. Hence, prior to that we query the database and pick only those values that are 'new' from the 'Raw' array. * @param {ProcessRecordsArgs} inputsArg * @param {RawValue[]} inputsArg.createOrUpdateRawValues + * @param {RawValue[]} inputsArg.deleteRawValues * @param {string} inputsArg.tableName * @param {string} inputsArg.fieldName * @param {(existing: Model, newElement: RawValue) => boolean} inputsArg.buildKeyRecordBy - * @returns {Promise<{ProcessRecordResults}>} + * @returns {Promise<{ProcessRecordResults}>} */ processRecords = async ({createOrUpdateRawValues = [], deleteRawValues = [], tableName, buildKeyRecordBy, fieldName, shouldUpdate}: ProcessRecordsArgs): Promise> => { const getRecords = async (rawValues: RawValue[]) => { @@ -77,7 +77,7 @@ export default class BaseDataOperator { // for create or update flow const createOrUpdateRaws = await getRecords(createOrUpdateRawValues); - const recordsByKeys = createOrUpdateRaws.reduce((result: Record, record) => { + const recordsByKeys = createOrUpdateRaws.reduce((result: Record, record) => { // @ts-expect-error object with string key const key = buildKeyRecordBy?.(record) || record[fieldName]; result[key] = record; @@ -125,9 +125,9 @@ export default class BaseDataOperator { * @param {string} prepareRecord.tableName * @param {RawValue[]} prepareRecord.createRaws * @param {RawValue[]} prepareRecord.updateRaws - * @param {Model[]} prepareRecord.deleteRaws - * @param {(TransformerArgs) => Promise;} transformer - * @returns {Promise} + * @param {T extends Model[]} prepareRecord.deleteRaws + * @param {(TransformerArgs) => Promise;} transformer + * @returns {Promise} */ prepareRecords = async ({tableName, createRaws, deleteRaws, updateRaws, transformer}: OperationArgs): Promise => { if (!this.database) { @@ -210,7 +210,7 @@ export default class BaseDataOperator { * @returns {Promise} */ async handleRecords({buildKeyRecordBy, fieldName, transformer, createOrUpdateRawValues, deleteRawValues = [], tableName, prepareRecordsOnly = true, shouldUpdate}: HandleRecordsArgs, description: string): Promise { - if (!createOrUpdateRawValues.length) { + if (!createOrUpdateRawValues.length && !deleteRawValues.length) { logWarning( `An empty "rawValues" array has been passed to the handleRecords method for tableName ${tableName}`, ); diff --git a/app/database/operator/server_data_operator/handlers/channel.test.ts b/app/database/operator/server_data_operator/handlers/channel.test.ts index d5c3b5df8b9..eccb9fc4724 100644 --- a/app/database/operator/server_data_operator/handlers/channel.test.ts +++ b/app/database/operator/server_data_operator/handlers/channel.test.ts @@ -184,7 +184,7 @@ describe('*** Operator: Channel Handlers tests ***', () => { await operator.handleMyChannel({ channels, myChannels, - prepareRecordsOnly: false, + prepareRecordsOnly: true, }); expect(spyOnHandleRecords).toHaveBeenCalledTimes(1); @@ -192,7 +192,7 @@ describe('*** Operator: Channel Handlers tests ***', () => { fieldName: 'id', createOrUpdateRawValues: myChannels, tableName: 'MyChannel', - prepareRecordsOnly: false, + prepareRecordsOnly: true, buildKeyRecordBy: buildMyChannelKey, transformer: transformMyChannelRecord, }, 'handleMyChannel'); @@ -253,7 +253,7 @@ describe('*** Operator: Channel Handlers tests ***', () => { prepareRecordsOnly: false, }); - expect(updated[0].lastFetchedAt).toBe(123456789); + expect(updated[0]).toHaveProperty('lastFetchedAt', 123456789); }); it('=> HandleChannelMembership: should write to the CHANNEL_MEMBERSHIP table', async () => { diff --git a/app/database/operator/server_data_operator/handlers/channel.ts b/app/database/operator/server_data_operator/handlers/channel.ts index c5b750721f7..b5f7f3cbe4d 100644 --- a/app/database/operator/server_data_operator/handlers/channel.ts +++ b/app/database/operator/server_data_operator/handlers/channel.ts @@ -1,7 +1,7 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {Database, Q} from '@nozbe/watermelondb'; +import {Database, Model, Q} from '@nozbe/watermelondb'; import {MM_TABLES} from '@constants/database'; import { @@ -9,6 +9,7 @@ import { buildChannelMembershipKey, } from '@database/operator/server_data_operator/comparators'; import { + transformChannelBookmarkRecord, transformChannelInfoRecord, transformChannelMembershipRecord, transformChannelRecord, @@ -17,10 +18,11 @@ import { } from '@database/operator/server_data_operator/transformers/channel'; import {getUniqueRawsBy} from '@database/operator/utils/general'; import {getIsCRTEnabled} from '@queries/servers/thread'; +import ChannelBookmarkModel from '@typings/database/models/servers/channel_bookmark'; import {logWarning} from '@utils/log'; import type ServerDataOperatorBase from '.'; -import type {HandleChannelArgs, HandleChannelInfoArgs, HandleChannelMembershipArgs, HandleMyChannelArgs, HandleMyChannelSettingsArgs} from '@typings/database/database'; +import type {HandleChannelArgs, HandleChannelBookmarkArgs, HandleChannelInfoArgs, HandleChannelMembershipArgs, HandleMyChannelArgs, HandleMyChannelSettingsArgs} from '@typings/database/database'; import type ChannelModel from '@typings/database/models/servers/channel'; import type ChannelInfoModel from '@typings/database/models/servers/channel_info'; import type ChannelMembershipModel from '@typings/database/models/servers/channel_membership'; @@ -29,6 +31,7 @@ import type MyChannelSettingsModel from '@typings/database/models/servers/my_cha const { CHANNEL, + CHANNEL_BOOKMARK, CHANNEL_INFO, CHANNEL_MEMBERSHIP, MY_CHANNEL, @@ -37,10 +40,11 @@ const { export interface ChannelHandlerMix { handleChannel: ({channels, prepareRecordsOnly}: HandleChannelArgs) => Promise; + handleChannelBookmark: ({bookmarks, prepareRecordsOnly}: HandleChannelBookmarkArgs) => Promise; handleChannelMembership: ({channelMemberships, prepareRecordsOnly}: HandleChannelMembershipArgs) => Promise; handleMyChannelSettings: ({settings, prepareRecordsOnly}: HandleMyChannelSettingsArgs) => Promise; handleChannelInfo: ({channelInfos, prepareRecordsOnly}: HandleChannelInfoArgs) => Promise; - handleMyChannel: ({channels, myChannels, isCRTEnabled, prepareRecordsOnly}: HandleMyChannelArgs) => Promise; + handleMyChannel: ({channels, myChannels, isCRTEnabled, prepareRecordsOnly}: HandleMyChannelArgs) => Promise; } const ChannelHandler = >(superclass: TBase) => class extends superclass { @@ -217,7 +221,7 @@ const ChannelHandler = >(super * @param {boolean} myChannelsArgs.prepareRecordsOnly * @returns {Promise} */ - handleMyChannel = async ({channels, myChannels, isCRTEnabled, prepareRecordsOnly = true}: HandleMyChannelArgs): Promise => { + handleMyChannel = async ({channels, myChannels, isCRTEnabled, prepareRecordsOnly = true}: HandleMyChannelArgs): Promise => { if (!myChannels?.length) { logWarning( 'An empty or undefined "myChannels" array has been passed to the handleMyChannel method', @@ -235,7 +239,6 @@ const ChannelHandler = >(super } const isCRT = isCRTEnabled ?? await getIsCRTEnabled(this.database); - const channelMap = channels.reduce((result: Record, channel) => { result[channel.id] = channel; return result; @@ -353,6 +356,87 @@ const ChannelHandler = >(super tableName: CHANNEL_MEMBERSHIP, }, 'handleChannelMembership'); }; + + /** + * handleChannelMembership: Handler responsible for the Create/Update/Delete operations occurring on the CHANNEL_BOOKMARK table from the 'Server' schema + * @param {HandleChannelBookmarkArgs} channelBookmarkArgs + * @param {ChannelBookmark[]} channelBookmarkArgs.bookmarks + * @param {boolean} channelBookmarkArgs.prepareRecordsOnly + * @returns {Promise} + */ + handleChannelBookmark = async ({bookmarks, prepareRecordsOnly}: HandleChannelBookmarkArgs): Promise => { + if (!bookmarks?.length) { + logWarning( + 'An empty or undefined "bookmarks" array has been passed to the handleChannelBookmark method', + ); + return []; + } + + const uniqueRaws = getUniqueRawsBy({raws: bookmarks, key: 'id'}) as ChannelBookmarkWithFileInfo[]; + const keys = uniqueRaws.map((c) => c.id); + const db: Database = this.database; + const existing = await db.get(CHANNEL_BOOKMARK).query( + Q.where('id', Q.oneOf(keys)), + ).fetch(); + const bookmarkMap = new Map(existing.map((b) => [b.id, b])); + const files: FileInfo[] = []; + const raws = uniqueRaws.reduce<{createOrUpdateRaws: ChannelBookmarkWithFileInfo[]; deleteRaws: ChannelBookmarkWithFileInfo[]}>((res, b) => { + const e = bookmarkMap.get(b.id); + if (!e) { + if (!b.delete_at) { + res.createOrUpdateRaws.push(b); + if (b.file) { + files.push(b.file); + } + } + return res; + } + + if (e.updateAt !== b.update_at) { + res.createOrUpdateRaws.push(b); + if (b.file) { + files.push(b.file); + } + } + + if (b.delete_at) { + res.deleteRaws.push(b); + if (b.file) { + b.file.delete_at = b.delete_at; + files.push(b.file); + } + } + + return res; + }, {createOrUpdateRaws: [], deleteRaws: []}); + + if (!raws.createOrUpdateRaws.length && !raws.deleteRaws.length) { + return []; + } + + const preparedBookmarks = await this.handleRecords({ + fieldName: 'id', + transformer: transformChannelBookmarkRecord, + createOrUpdateRawValues: raws.createOrUpdateRaws, + deleteRawValues: raws.deleteRaws, + tableName: CHANNEL_BOOKMARK, + prepareRecordsOnly: true, + }, 'handleChannelBookmark'); + + const batch: Model[] = [...preparedBookmarks]; + if (files.length) { + const bookmarkFiles = await this.handleFiles({files, prepareRecordsOnly: true}); + if (bookmarkFiles.length) { + batch.push(...bookmarkFiles); + } + } + + if (batch.length && !prepareRecordsOnly) { + await this.batchRecords(batch, 'handleChannelBookmark'); + } + + return batch; + }; }; export default ChannelHandler; diff --git a/app/database/operator/server_data_operator/handlers/index.test.ts b/app/database/operator/server_data_operator/handlers/index.test.ts index 9ce3d4744b9..09b02c8a3e6 100644 --- a/app/database/operator/server_data_operator/handlers/index.test.ts +++ b/app/database/operator/server_data_operator/handlers/index.test.ts @@ -2,6 +2,7 @@ // See LICENSE.txt for license information. import DatabaseManager from '@database/manager'; +import {shouldUpdateFileRecord} from '@database/operator/server_data_operator/comparators/files'; import { transformConfigRecord, transformCustomEmojiRecord, @@ -9,7 +10,7 @@ import { transformSystemRecord, } from '@database/operator/server_data_operator/transformers/general'; -import type ServerDataOperator from '..'; +import type ServerDataOperator from '@database/operator/server_data_operator/index'; describe('*** DataOperator: Base Handlers tests ***', () => { let operator: ServerDataOperator; @@ -120,6 +121,59 @@ describe('*** DataOperator: Base Handlers tests ***', () => { }, 'handleConfigs'); }); + it('=> HandleFiles: should write to the FILE table', async () => { + expect.assertions(1); + + const spyOnprocessRecords = jest.spyOn(operator, 'processRecords'); + + const files = [{ + id: 'f1oxe5rtepfs7n3zifb4sso7po', + user_id: '89ertha8xpfsumpucqppy5knao', + post_id: 'a7ebyw883trm884p1qcgt8yw4a', + create_at: 1608270920357, + update_at: 1608270920357, + delete_at: 0, + name: '4qtwrg.jpg', + extension: 'jpg', + size: 89208, + mime_type: 'image/jpeg', + width: 500, + height: 656, + has_preview_image: true, + mini_preview: + '/9j/2wCEAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRQBAwQEBQQFCQUFCRQNCw0UFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFP/AABEIABAAEAMBIgACEQEDEQH/xAGiAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgsQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5+gEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoLEQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4+Tl5ufo6ery8/T19vf4+fr/2gAMAwEAAhEDEQA/AN/T/iZp+pX15FpUmnwLbXtpJpyy2sQLw8CcBXA+bksCDnHGOaf4W+P3xIshbQ6loB8RrbK11f3FpbBFW3ZwiFGHB2kr25BIOeCPPbX4S3407T7rTdDfxFNIpDyRaw9lsB4OECHGR15yO4GK6fRPhR4sGmSnxAs8NgchNOjvDPsjz8qSHA37cDk5JPPFdlOpTdPlcVt/Ku1lrvr17b67EPnjrH8/626H/9k=', + }, { + id: 'f1oxe5rtepfs7n3zifb4sso7po', + user_id: 'bookmark', + create_at: 1608270920357, + update_at: 1608270920357, + delete_at: 1608270920357, + name: '4qtwrg.jpg', + extension: 'jpg', + size: 89208, + mime_type: 'image/jpeg', + width: 500, + height: 656, + has_preview_image: true, + mini_preview: + '/9j/2wCEAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRQBAwQEBQQFCQUFCRQNCw0UFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFP/AABEIABAAEAMBIgACEQEDEQH/xAGiAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgsQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5+gEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoLEQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4+Tl5ufo6ery8/T19vf4+fr/2gAMAwEAAhEDEQA/AN/T/iZp+pX15FpUmnwLbXtpJpyy2sQLw8CcBXA+bksCDnHGOaf4W+P3xIshbQ6loB8RrbK11f3FpbBFW3ZwiFGHB2kr25BIOeCPPbX4S3407T7rTdDfxFNIpDyRaw9lsB4OECHGR15yO4GK6fRPhR4sGmSnxAs8NgchNOjvDPsjz8qSHA37cDk5JPPFdlOpTdPlcVt/Ku1lrvr17b67EPnjrH8/626H/9k=', + }, + ]; + + await operator.handleFiles({ + files, + prepareRecordsOnly: false, + }); + + expect(spyOnprocessRecords).toHaveBeenCalledWith({ + fieldName: 'id', + createOrUpdateRawValues: files.filter((f) => !f.delete_at), + deleteRawValues: files.filter((f) => f.delete_at), + tableName: 'File', + shouldUpdate: shouldUpdateFileRecord, + }); + }); + it('=> No table name: should not call execute if tableName is invalid', async () => { expect.assertions(3); diff --git a/app/database/operator/server_data_operator/handlers/index.ts b/app/database/operator/server_data_operator/handlers/index.ts index 15ce6f28259..8da5fbd39e2 100644 --- a/app/database/operator/server_data_operator/handlers/index.ts +++ b/app/database/operator/server_data_operator/handlers/index.ts @@ -3,9 +3,11 @@ import {MM_TABLES} from '@constants/database'; import BaseDataOperator from '@database/operator/base_data_operator'; +import {shouldUpdateFileRecord} from '@database/operator/server_data_operator/comparators/files'; import { transformConfigRecord, transformCustomEmojiRecord, + transformFileRecord, transformRoleRecord, transformSystemRecord, } from '@database/operator/server_data_operator/transformers/general'; @@ -16,13 +18,14 @@ import {sanitizeReactions} from '../../utils/reaction'; import {transformReactionRecord} from '../transformers/reaction'; import type {Model} from '@nozbe/watermelondb'; -import type {HandleConfigArgs, HandleCustomEmojiArgs, HandleReactionsArgs, HandleRoleArgs, HandleSystemArgs, OperationArgs} from '@typings/database/database'; +import type {HandleConfigArgs, HandleCustomEmojiArgs, HandleFilesArgs, HandleReactionsArgs, HandleRoleArgs, HandleSystemArgs, OperationArgs} from '@typings/database/database'; import type CustomEmojiModel from '@typings/database/models/servers/custom_emoji'; +import type FileModel from '@typings/database/models/servers/file'; import type ReactionModel from '@typings/database/models/servers/reaction'; import type RoleModel from '@typings/database/models/servers/role'; import type SystemModel from '@typings/database/models/servers/system'; -const {SERVER: {CONFIG, CUSTOM_EMOJI, ROLE, SYSTEM, REACTION}} = MM_TABLES; +const {SERVER: {CONFIG, CUSTOM_EMOJI, FILE, ROLE, SYSTEM, REACTION}} = MM_TABLES; export default class ServerDataOperatorBase extends BaseDataOperator { handleRole = async ({roles, prepareRecordsOnly = true}: HandleRoleArgs) => { @@ -151,6 +154,58 @@ export default class ServerDataOperatorBase extends BaseDataOperator { return batchRecords; }; + /** + * handleFiles: Handler responsible for the Create/Update operations occurring on the File table from the 'Server' schema + * @param {HandleFilesArgs} handleFiles + * @param {RawFile[]} handleFiles.files + * @param {boolean} handleFiles.prepareRecordsOnly + * @returns {Promise} + */ + handleFiles = async ({files, prepareRecordsOnly}: HandleFilesArgs): Promise => { + if (!files?.length) { + logWarning( + 'An empty or undefined "files" array has been passed to the handleFiles method', + ); + return []; + } + + const raws = files.reduce<{createOrUpdateFiles: FileInfo[]; deleteFiles: FileInfo[]}>((res, f) => { + if (f.delete_at) { + res.deleteFiles.push(f); + } else { + res.createOrUpdateFiles.push(f); + } + + return res; + }, {createOrUpdateFiles: [], deleteFiles: []}); + + const processedFiles = (await this.processRecords({ + createOrUpdateRawValues: raws.createOrUpdateFiles, + tableName: FILE, + fieldName: 'id', + deleteRawValues: raws.deleteFiles, + shouldUpdate: shouldUpdateFileRecord, + })); + + const preparedFiles = await this.prepareRecords({ + createRaws: processedFiles.createRaws, + updateRaws: processedFiles.updateRaws, + deleteRaws: processedFiles.deleteRaws, + transformer: transformFileRecord, + tableName: FILE, + }); + + if (prepareRecordsOnly) { + return preparedFiles; + } + + if (preparedFiles?.length) { + await this.batchRecords(preparedFiles, 'handleFiles'); + } + + return preparedFiles; + }; + /** * execute: Handles the Create/Update operations on an table. * @param {OperationArgs} execute diff --git a/app/database/operator/server_data_operator/handlers/post.ts b/app/database/operator/server_data_operator/handlers/post.ts index 2b0d92e3e09..56c28571ab8 100644 --- a/app/database/operator/server_data_operator/handlers/post.ts +++ b/app/database/operator/server_data_operator/handlers/post.ts @@ -6,16 +6,15 @@ import {Q} from '@nozbe/watermelondb'; import {ActionType} from '@constants'; import {MM_TABLES} from '@constants/database'; import {buildDraftKey} from '@database/operator/server_data_operator/comparators'; -import {shouldUpdateFileRecord} from '@database/operator/server_data_operator/comparators/files'; import { transformDraftRecord, - transformFileRecord, transformPostInThreadRecord, transformPostRecord, transformPostsInChannelRecord, } from '@database/operator/server_data_operator/transformers/post'; import {getRawRecordPairs, getUniqueRawsBy, getValidRecordsForUpdate} from '@database/operator/utils/general'; import {createPostsChain, getPostListEdges} from '@database/operator/utils/post'; +import FileModel from '@typings/database/models/servers/file'; import {logWarning} from '@utils/log'; import type ServerDataOperatorBase from '.'; @@ -23,7 +22,6 @@ import type Database from '@nozbe/watermelondb/Database'; import type Model from '@nozbe/watermelondb/Model'; import type {HandleDraftArgs, HandleFilesArgs, HandlePostsArgs, RecordPair} from '@typings/database/database'; import type DraftModel from '@typings/database/models/servers/draft'; -import type FileModel from '@typings/database/models/servers/file'; import type PostModel from '@typings/database/models/servers/post'; import type PostsInChannelModel from '@typings/database/models/servers/posts_in_channel'; import type PostsInThreadModel from '@typings/database/models/servers/posts_in_thread'; @@ -31,7 +29,6 @@ import type ReactionModel from '@typings/database/models/servers/reaction'; const { DRAFT, - FILE, POST, POSTS_IN_CHANNEL, POSTS_IN_THREAD, @@ -291,47 +288,6 @@ const PostHandler = >(supercla return batch; }; - /** - * handleFiles: Handler responsible for the Create/Update operations occurring on the File table from the 'Server' schema - * @param {HandleFilesArgs} handleFiles - * @param {RawFile[]} handleFiles.files - * @param {boolean} handleFiles.prepareRecordsOnly - * @returns {Promise} - */ - handleFiles = async ({files, prepareRecordsOnly}: HandleFilesArgs): Promise => { - if (!files?.length) { - logWarning( - 'An empty or undefined "files" array has been passed to the handleFiles method', - ); - return []; - } - - const processedFiles = (await this.processRecords({ - createOrUpdateRawValues: files, - tableName: FILE, - fieldName: 'id', - deleteRawValues: [], - shouldUpdate: shouldUpdateFileRecord, - })); - - const postFiles = await this.prepareRecords({ - createRaws: processedFiles.createRaws, - updateRaws: processedFiles.updateRaws, - transformer: transformFileRecord, - tableName: FILE, - }); - - if (prepareRecordsOnly) { - return postFiles; - } - - if (postFiles?.length) { - await this.batchRecords(postFiles, 'handleFiles'); - } - - return postFiles; - }; - /** * handlePostsInThread: Handler responsible for the Create/Update operations occurring on the PostsInThread table from the 'Server' schema * @param {Record} postsMap diff --git a/app/database/operator/server_data_operator/transformers/channel.ts b/app/database/operator/server_data_operator/transformers/channel.ts index 4c124c69138..671d0f42c30 100644 --- a/app/database/operator/server_data_operator/transformers/channel.ts +++ b/app/database/operator/server_data_operator/transformers/channel.ts @@ -7,6 +7,7 @@ import {extractChannelDisplayName} from '@helpers/database'; import type {TransformerArgs} from '@typings/database/database'; import type ChannelModel from '@typings/database/models/servers/channel'; +import type ChannelBookmarkModel from '@typings/database/models/servers/channel_bookmark'; import type ChannelInfoModel from '@typings/database/models/servers/channel_info'; import type ChannelMembershipModel from '@typings/database/models/servers/channel_membership'; import type MyChannelModel from '@typings/database/models/servers/my_channel'; @@ -14,6 +15,7 @@ import type MyChannelSettingsModel from '@typings/database/models/servers/my_cha const { CHANNEL, + CHANNEL_BOOKMARK, CHANNEL_INFO, CHANNEL_MEMBERSHIP, MY_CHANNEL, @@ -178,3 +180,44 @@ export const transformChannelMembershipRecord = ({action, database, value}: Tran fieldsMapper, }) as Promise; }; + +/** + * transformChannelBookmarkRecord: Prepares a record of the SERVER database 'Channel' table for update or create actions. + * @param {DataFactory} operator + * @param {Database} operator.database + * @param {RecordPair} operator.value + * @returns {Promise} + */ +export const transformChannelBookmarkRecord = ({action, database, value}: TransformerArgs): Promise => { + const raw = value.raw as ChannelBookmark; + const record = value.record as ChannelBookmarkModel; + const isCreateAction = action === OperationType.CREATE; + + // If isCreateAction is true, we will use the id (API response) from the RAW, else we shall use the existing record id from the database + const fieldsMapper = (bookmark: ChannelBookmarkModel) => { + bookmark._raw.id = isCreateAction ? (raw?.id ?? bookmark.id) : record.id; + bookmark.createAt = raw.create_at; + bookmark.deleteAt = raw.delete_at; + bookmark.updateAt = raw.update_at; + bookmark.channelId = raw.channel_id; + bookmark.ownerId = raw.owner_id; + bookmark.fileId = raw.file_id; + + bookmark.displayName = raw.display_name; + bookmark.sortOrder = raw.sort_order; + bookmark.linkUrl = raw.link_url; + bookmark.imageUrl = raw.image_url; + bookmark.emoji = raw.emoji; + bookmark.type = raw.type; + bookmark.originalId = raw.original_id; + bookmark.parentId = raw.parent_id; + }; + + return prepareBaseRecord({ + action, + database, + tableName: CHANNEL_BOOKMARK, + value, + fieldsMapper, + }) as Promise; +}; diff --git a/app/database/operator/server_data_operator/transformers/general.test.ts b/app/database/operator/server_data_operator/transformers/general.test.ts index 12e25c6e1c9..6121db4f43c 100644 --- a/app/database/operator/server_data_operator/transformers/general.test.ts +++ b/app/database/operator/server_data_operator/transformers/general.test.ts @@ -4,6 +4,7 @@ import {OperationType} from '@constants/database'; import { transformCustomEmojiRecord, + transformFileRecord, transformRoleRecord, transformSystemRecord, } from '@database/operator/server_data_operator/transformers/general'; @@ -83,3 +84,37 @@ describe('*** CustomEmoj Prepare Records Test ***', () => { }); }); +describe('*** Files Prepare Records Test ***', () => { + it('=> transformFileRecord: should return an array of type File', async () => { + expect.assertions(3); + + const database = await createTestConnection({databaseName: 'post_prepare_records', setActive: true}); + expect(database).toBeTruthy(); + + const preparedRecords = await transformFileRecord({ + action: OperationType.CREATE, + database: database!, + value: { + record: undefined, + raw: { + id: 'file-id', + post_id: 'ps81iqbddesfby8jayz7owg4yypoo', + name: 'test_file', + extension: '.jpg', + has_preview_image: true, + mime_type: 'image/jpeg', + size: 1000, + create_at: 1609253011321, + delete_at: 1609253011321, + height: 20, + width: 20, + update_at: 1609253011321, + user_id: 'wqyby5r5pinxxdqhoaomtacdhc', + }, + }, + }); + + expect(preparedRecords).toBeTruthy(); + expect(preparedRecords!.collection.table).toBe('File'); + }); +}); diff --git a/app/database/operator/server_data_operator/transformers/general.ts b/app/database/operator/server_data_operator/transformers/general.ts index d157780762c..8264739b797 100644 --- a/app/database/operator/server_data_operator/transformers/general.ts +++ b/app/database/operator/server_data_operator/transformers/general.ts @@ -7,11 +7,13 @@ import {prepareBaseRecord} from '@database/operator/server_data_operator/transfo import type {TransformerArgs} from '@typings/database/database'; import type ConfigModel from '@typings/database/models/servers/config'; import type CustomEmojiModel from '@typings/database/models/servers/custom_emoji'; +import type FileModel from '@typings/database/models/servers/file'; import type RoleModel from '@typings/database/models/servers/role'; import type SystemModel from '@typings/database/models/servers/system'; const { CUSTOM_EMOJI, + FILE, ROLE, SYSTEM, CONFIG, @@ -121,3 +123,38 @@ export const transformConfigRecord = ({action, database, value}: TransformerArgs fieldsMapper, }) as Promise; }; + +/** + * transformFileRecord: Prepares a record of the SERVER database 'Files' table for update or create actions. + * @param {TransformerArgs} operator + * @param {Database} operator.database + * @param {RecordPair} operator.value + * @returns {Promise} + */ +export const transformFileRecord = ({action, database, value}: TransformerArgs): Promise => { + const raw = value.raw as FileInfo; + const record = value.record as FileModel; + const isCreateAction = action === OperationType.CREATE; + + // If isCreateAction is true, we will use the id (API response) from the RAW, else we shall use the existing record id from the database + const fieldsMapper = (file: FileModel) => { + file._raw.id = isCreateAction ? (raw.id || file.id) : record.id; + file.postId = raw.post_id || ''; + file.name = raw.name; + file.extension = raw.extension; + file.size = raw.size; + file.mimeType = raw?.mime_type ?? ''; + file.width = raw?.width || record?.width || 0; + file.height = raw?.height || record?.height || 0; + file.imageThumbnail = raw?.mini_preview || record?.imageThumbnail || ''; + file.localPath = raw?.localPath || record?.localPath || null; + }; + + return prepareBaseRecord({ + action, + database, + tableName: FILE, + value, + fieldsMapper, + }) as Promise; +}; diff --git a/app/database/operator/server_data_operator/transformers/post.test.ts b/app/database/operator/server_data_operator/transformers/post.test.ts index 53bea8c62e8..6f018d2fb39 100644 --- a/app/database/operator/server_data_operator/transformers/post.test.ts +++ b/app/database/operator/server_data_operator/transformers/post.test.ts @@ -4,7 +4,6 @@ import {OperationType} from '@constants/database'; import { transformDraftRecord, - transformFileRecord, transformPostInThreadRecord, transformPostRecord, transformPostsInChannelRecord, @@ -75,39 +74,6 @@ describe('*** POST Prepare Records Test ***', () => { expect(preparedRecords!.collection.table).toBe('PostsInThread'); }); - it('=> transformFileRecord: should return an array of type File', async () => { - expect.assertions(3); - - const database = await createTestConnection({databaseName: 'post_prepare_records', setActive: true}); - expect(database).toBeTruthy(); - - const preparedRecords = await transformFileRecord({ - action: OperationType.CREATE, - database: database!, - value: { - record: undefined, - raw: { - id: 'file-id', - post_id: 'ps81iqbddesfby8jayz7owg4yypoo', - name: 'test_file', - extension: '.jpg', - has_preview_image: true, - mime_type: 'image/jpeg', - size: 1000, - create_at: 1609253011321, - delete_at: 1609253011321, - height: 20, - width: 20, - update_at: 1609253011321, - user_id: 'wqyby5r5pinxxdqhoaomtacdhc', - }, - }, - }); - - expect(preparedRecords).toBeTruthy(); - expect(preparedRecords!.collection.table).toBe('File'); - }); - it('=> transformDraftRecord: should return an array of type Draft', async () => { expect.assertions(3); diff --git a/app/database/operator/server_data_operator/transformers/post.ts b/app/database/operator/server_data_operator/transformers/post.ts index 0b42b8b6a6c..7bd63609d4f 100644 --- a/app/database/operator/server_data_operator/transformers/post.ts +++ b/app/database/operator/server_data_operator/transformers/post.ts @@ -6,14 +6,12 @@ import {prepareBaseRecord} from '@database/operator/server_data_operator/transfo import type{TransformerArgs} from '@typings/database/database'; import type DraftModel from '@typings/database/models/servers/draft'; -import type FileModel from '@typings/database/models/servers/file'; import type PostModel from '@typings/database/models/servers/post'; import type PostsInChannelModel from '@typings/database/models/servers/posts_in_channel'; import type PostsInThreadModel from '@typings/database/models/servers/posts_in_thread'; const { DRAFT, - FILE, POST, POSTS_IN_CHANNEL, POSTS_IN_THREAD, @@ -94,41 +92,6 @@ export const transformPostInThreadRecord = ({action, database, value}: Transform }) as Promise; }; -/** - * transformFileRecord: Prepares a record of the SERVER database 'Files' table for update or create actions. - * @param {TransformerArgs} operator - * @param {Database} operator.database - * @param {RecordPair} operator.value - * @returns {Promise} - */ -export const transformFileRecord = ({action, database, value}: TransformerArgs): Promise => { - const raw = value.raw as FileInfo; - const record = value.record as FileModel; - const isCreateAction = action === OperationType.CREATE; - - // If isCreateAction is true, we will use the id (API response) from the RAW, else we shall use the existing record id from the database - const fieldsMapper = (file: FileModel) => { - file._raw.id = isCreateAction ? (raw.id || file.id) : record.id; - file.postId = raw.post_id; - file.name = raw.name; - file.extension = raw.extension; - file.size = raw.size; - file.mimeType = raw?.mime_type ?? ''; - file.width = raw?.width || record?.width || 0; - file.height = raw?.height || record?.height || 0; - file.imageThumbnail = raw?.mini_preview || record?.imageThumbnail || ''; - file.localPath = raw?.localPath || record?.localPath || null; - }; - - return prepareBaseRecord({ - action, - database, - tableName: FILE, - value, - fieldsMapper, - }) as Promise; -}; - /** * transformDraftRecord: Prepares a record of the SERVER database 'Draft' table for update or create actions. * @param {TransformerArgs} operator diff --git a/app/database/schema/server/index.ts b/app/database/schema/server/index.ts index 8220f092e07..7f0203c1676 100644 --- a/app/database/schema/server/index.ts +++ b/app/database/schema/server/index.ts @@ -6,9 +6,10 @@ import {type AppSchema, appSchema} from '@nozbe/watermelondb'; import { CategorySchema, CategoryChannelSchema, + ChannelSchema, + ChannelBookmarkSchema, ChannelInfoSchema, ChannelMembershipSchema, - ChannelSchema, ConfigSchema, CustomEmojiSchema, DraftSchema, @@ -39,13 +40,14 @@ import { } from './table_schemas'; export const serverSchema: AppSchema = appSchema({ - version: 3, + version: 4, tables: [ CategorySchema, CategoryChannelSchema, + ChannelSchema, + ChannelBookmarkSchema, ChannelInfoSchema, ChannelMembershipSchema, - ChannelSchema, ConfigSchema, CustomEmojiSchema, DraftSchema, diff --git a/app/database/schema/server/table_schemas/channel_bookmark.ts b/app/database/schema/server/table_schemas/channel_bookmark.ts new file mode 100644 index 00000000000..c7d62188d42 --- /dev/null +++ b/app/database/schema/server/table_schemas/channel_bookmark.ts @@ -0,0 +1,28 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import {tableSchema} from '@nozbe/watermelondb'; + +import {MM_TABLES} from '@constants/database'; + +const {CHANNEL_BOOKMARK} = MM_TABLES.SERVER; + +export default tableSchema({ + name: CHANNEL_BOOKMARK, + columns: [ + {name: 'create_at', type: 'number'}, + {name: 'update_at', type: 'number'}, + {name: 'delete_at', type: 'number'}, + {name: 'channel_id', type: 'string', isIndexed: true}, + {name: 'owner_id', type: 'string'}, + {name: 'file_id', type: 'string', isOptional: true}, + {name: 'display_name', type: 'string'}, + {name: 'sort_order', type: 'number'}, + {name: 'link_url', type: 'string', isOptional: true}, + {name: 'image_url', type: 'string', isOptional: true}, + {name: 'emoji', type: 'string', isOptional: true}, + {name: 'type', type: 'string'}, + {name: 'original_id', type: 'string', isOptional: true}, + {name: 'parent_id', type: 'string', isOptional: true}, + ], +}); diff --git a/app/database/schema/server/table_schemas/index.ts b/app/database/schema/server/table_schemas/index.ts index 27676546eae..ae6337034fb 100644 --- a/app/database/schema/server/table_schemas/index.ts +++ b/app/database/schema/server/table_schemas/index.ts @@ -3,9 +3,10 @@ export {default as CategorySchema} from './category'; export {default as CategoryChannelSchema} from './category_channel'; +export {default as ChannelSchema} from './channel'; +export {default as ChannelBookmarkSchema} from './channel_bookmark'; export {default as ChannelInfoSchema} from './channel_info'; export {default as ChannelMembershipSchema} from './channel_membership'; -export {default as ChannelSchema} from './channel'; export {default as CustomEmojiSchema} from './custom_emoji'; export {default as DraftSchema} from './draft'; export {default as FileSchema} from './file'; diff --git a/app/database/schema/server/test.ts b/app/database/schema/server/test.ts index e64685048fd..da7cc1fc65f 100644 --- a/app/database/schema/server/test.ts +++ b/app/database/schema/server/test.ts @@ -11,6 +11,7 @@ const { CATEGORY, CATEGORY_CHANNEL, CHANNEL, + CHANNEL_BOOKMARK, CHANNEL_INFO, CHANNEL_MEMBERSHIP, CONFIG, @@ -45,7 +46,7 @@ const { describe('*** Test schema for SERVER database ***', () => { it('=> The SERVER SCHEMA should strictly match', () => { expect(serverSchema).toEqual({ - version: 3, + version: 4, unsafeSql: undefined, tables: { [CATEGORY]: { @@ -136,6 +137,43 @@ describe('*** Test schema for SERVER database ***', () => { {name: 'update_at', type: 'number'}, ], }, + [CHANNEL_BOOKMARK]: { + name: CHANNEL_BOOKMARK, + unsafeSql: undefined, + columns: { + create_at: {name: 'create_at', type: 'number'}, + update_at: {name: 'update_at', type: 'number'}, + delete_at: {name: 'delete_at', type: 'number'}, + channel_id: {name: 'channel_id', type: 'string', isIndexed: true}, + owner_id: {name: 'owner_id', type: 'string'}, + file_id: {name: 'file_id', type: 'string', isOptional: true}, + display_name: {name: 'display_name', type: 'string'}, + sort_order: {name: 'sort_order', type: 'number'}, + link_url: {name: 'link_url', type: 'string', isOptional: true}, + image_url: {name: 'image_url', type: 'string', isOptional: true}, + emoji: {name: 'emoji', type: 'string', isOptional: true}, + type: {name: 'type', type: 'string'}, + original_id: {name: 'original_id', type: 'string', isOptional: true}, + parent_id: {name: 'parent_id', type: 'string', isOptional: true}, + + }, + columnArray: [ + {name: 'create_at', type: 'number'}, + {name: 'update_at', type: 'number'}, + {name: 'delete_at', type: 'number'}, + {name: 'channel_id', type: 'string', isIndexed: true}, + {name: 'owner_id', type: 'string'}, + {name: 'file_id', type: 'string', isOptional: true}, + {name: 'display_name', type: 'string'}, + {name: 'sort_order', type: 'number'}, + {name: 'link_url', type: 'string', isOptional: true}, + {name: 'image_url', type: 'string', isOptional: true}, + {name: 'emoji', type: 'string', isOptional: true}, + {name: 'type', type: 'string'}, + {name: 'original_id', type: 'string', isOptional: true}, + {name: 'parent_id', type: 'string', isOptional: true}, + ], + }, [CHANNEL_MEMBERSHIP]: { name: CHANNEL_MEMBERSHIP, unsafeSql: undefined, diff --git a/app/hooks/files.ts b/app/hooks/files.ts index 6daa81947a7..49da59e5122 100644 --- a/app/hooks/files.ts +++ b/app/hooks/files.ts @@ -1,11 +1,53 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {useMemo} from 'react'; +import {useEffect, useMemo, useState} from 'react'; +import {getLocalFileInfo} from '@actions/local/file'; import {buildFilePreviewUrl, buildFileUrl} from '@actions/remote/file'; import {useServerUrl} from '@context/server'; import {isGif, isImage, isVideo} from '@utils/file'; +import {getImageSize} from '@utils/gallery'; + +import type ChannelBookmarkModel from '@typings/database/models/servers/channel_bookmark'; + +const getFileInfo = async (serverUrl: string, bookmarks: ChannelBookmarkModel[], publicLinkEnabled: boolean, cb: (files: FileInfo[]) => void) => { + const fileInfos: FileInfo[] = []; + for await (const b of bookmarks) { + if (b.fileId) { + const res = await getLocalFileInfo(serverUrl, b.fileId); + if (res.file) { + const fileInfo = res.file.toFileInfo(b.ownerId); + const imageFile = isImage(fileInfo); + const videoFile = isVideo(fileInfo); + + let uri; + if (imageFile || (videoFile && publicLinkEnabled)) { + if (fileInfo.localPath) { + uri = fileInfo.localPath; + } else { + uri = (isGif(fileInfo) || (imageFile && !fileInfo.has_preview_image) || videoFile) ? buildFileUrl(serverUrl, fileInfo.id!) : buildFilePreviewUrl(serverUrl, fileInfo.id!); + } + } else { + uri = fileInfo.localPath || buildFileUrl(serverUrl, fileInfo.id!); + } + + let {width, height} = fileInfo; + if (imageFile && !width) { + const size = await getImageSize(uri); + width = size.width; + height = size.height; + } + + fileInfos.push({...fileInfo, uri, width, height}); + } + } + } + + if (fileInfos.length) { + cb(fileInfos); + } +}; export const useImageAttachments = (filesInfo: FileInfo[], publicLinkEnabled: boolean) => { const serverUrl = useServerUrl(); @@ -35,3 +77,13 @@ export const useImageAttachments = (filesInfo: FileInfo[], publicLinkEnabled: bo }, [filesInfo, publicLinkEnabled]); }; +export const useChannelBookmarkFiles = (bookmarks: ChannelBookmarkModel[], publicLinkEnabled: boolean) => { + const serverUrl = useServerUrl(); + const [files, setFiles] = useState([]); + + useEffect(() => { + getFileInfo(serverUrl, bookmarks, publicLinkEnabled, setFiles); + }, [serverUrl, bookmarks, publicLinkEnabled]); + + return files; +}; diff --git a/app/init/launch.ts b/app/init/launch.ts index c9cfb365c75..9db93c37a94 100644 --- a/app/init/launch.ts +++ b/app/init/launch.ts @@ -22,6 +22,7 @@ import EphemeralStore from '@store/ephemeral_store'; import {getLaunchPropsFromDeepLink} from '@utils/deep_link'; import {logInfo} from '@utils/log'; import {convertToNotificationData} from '@utils/notification'; +import {removeProtocol} from '@utils/url'; import type {DeepLinkWithData, LaunchProps} from '@typings/launch'; @@ -80,9 +81,17 @@ const launchApp = async (props: LaunchProps) => { const existingServer = DatabaseManager.searchUrl(extra.data!.serverUrl); serverUrl = existingServer; props.serverUrl = serverUrl || extra.data?.serverUrl; - if (!serverUrl) { + if (!serverUrl && extra.type !== DeepLink.Server) { props.launchError = true; } + if (extra.type === DeepLink.Server) { + if (removeProtocol(serverUrl) === extra.data?.serverUrl) { + props.extra = undefined; + props.launchType = Launch.Normal; + } else { + serverUrl = await getActiveServerUrl(); + } + } } break; case Launch.Notification: { diff --git a/app/managers/global_event_handler.ts b/app/managers/global_event_handler.ts index f9daa8dec15..5a69d5b7279 100644 --- a/app/managers/global_event_handler.ts +++ b/app/managers/global_event_handler.ts @@ -43,7 +43,7 @@ class GlobalEventHandler { } if (event.url) { - const {error} = await handleDeepLink(event.url); + const {error} = await handleDeepLink(event.url, undefined, undefined, true); if (error) { alertInvalidDeepLink(getIntlShape(DEFAULT_LOCALE)); } diff --git a/app/managers/network_manager.ts b/app/managers/network_manager.ts index 74cdbde80b8..4d6afb81d72 100644 --- a/app/managers/network_manager.ts +++ b/app/managers/network_manager.ts @@ -11,6 +11,7 @@ import { } from '@mattermost/react-native-network-client'; import {nativeApplicationVersion, nativeBuildVersion} from 'expo-application'; import {modelName, osName, osVersion} from 'expo-device'; +import {defineMessages} from 'react-intl'; import {Alert, DeviceEventEmitter} from 'react-native'; import urlParse from 'url-parse'; @@ -19,18 +20,38 @@ import {Client} from '@client/rest'; import * as ClientConstants from '@client/rest/constants'; import ClientError from '@client/rest/error'; import {CERTIFICATE_ERRORS} from '@constants/network'; -import {DEFAULT_LOCALE, getLocalizedMessage, t} from '@i18n'; import ManagedApp from '@init/managed_app'; +import {toMilliseconds} from '@utils/datetime'; +import {getIntlShape} from '@utils/general'; import {logDebug, logError} from '@utils/log'; import {getCSRFFromCookie} from '@utils/security'; const CLIENT_CERTIFICATE_IMPORT_ERROR_CODES = [-103, -104, -105, -108]; const CLIENT_CERTIFICATE_MISSING_ERROR_CODE = -200; const SERVER_CERTIFICATE_INVALID = -299; +const SERVER_TRUST_EVALUATION_FAILED = -298; +let showingServerTrustAlert = false; + +const messages = defineMessages({ + invalidSslTitle: { + id: 'server.invalid.certificate.title', + defaultMessage: 'Invalid SSL certificate', + }, + invalidSslDescription: { + id: 'server.invalid.certificate.description', + defaultMessage: 'The certificate for this server is invalid.\nYou might be connecting to a server that is pretending to be “{hostname}” which could put your confidential information at risk.', + }, + invalidPinningTitle: { + id: 'server.invalid.pinning.title', + defaultMessage: 'Invalid pinned SSL certificate', + }, +}); class NetworkManager { private clients: Record = {}; + private intl = getIntlShape(); + private DEFAULT_CONFIG: APIClientConfiguration = { headers: { 'X-Requested-With': 'XMLHttpRequest', @@ -129,12 +150,23 @@ class NetworkManager { logDebug('Invalid SSL certificate:', event.errorDescription); const parsed = urlParse(event.serverUrl); Alert.alert( - getLocalizedMessage(DEFAULT_LOCALE, t('server.invalid.certificate.title'), 'Invalid SSL certificate'), - getLocalizedMessage( - DEFAULT_LOCALE, - t('server.invalid.certificate.description'), - 'The certificate for this server is invalid.\nYou might be connecting to a server that is pretending to be “{hostname}” which could put your confidential information at risk.', - ).replace('{hostname}', parsed.hostname), + this.intl.formatMessage(messages.invalidSslTitle), + this.intl.formatMessage(messages.invalidSslDescription, {hostname: parsed.hostname}), + ); + } else if (SERVER_TRUST_EVALUATION_FAILED === event.errorCode && !showingServerTrustAlert) { + logDebug('Invalid SSL Pinning:', event.errorDescription); + showingServerTrustAlert = true; + Alert.alert( + this.intl.formatMessage(messages.invalidPinningTitle), + event.errorDescription, + [{ + text: this.intl.formatMessage({id: 'server_upgrade.dismiss', defaultMessage: 'Dismiss'}), + onPress: () => { + setTimeout(() => { + showingServerTrustAlert = false; + }, toMilliseconds({hours: 23})); + }, + }], ); } }; diff --git a/app/managers/performance_metrics_manager/index.ts b/app/managers/performance_metrics_manager/index.ts index a3369f6a2e6..f3dcf8298a7 100644 --- a/app/managers/performance_metrics_manager/index.ts +++ b/app/managers/performance_metrics_manager/index.ts @@ -48,6 +48,10 @@ class PerformanceMetricsManager { this.target = target; } + public skipLoadMetric() { + this.hasRegisteredLoad = true; + } + public finishLoad(location: Target, serverUrl: string) { this.finishLoadWithRetries(location, serverUrl, 0); } diff --git a/app/managers/websocket_manager.ts b/app/managers/websocket_manager.ts index b40f94db249..1a3538525c0 100644 --- a/app/managers/websocket_manager.ts +++ b/app/managers/websocket_manager.ts @@ -9,7 +9,8 @@ import {distinctUntilChanged} from 'rxjs/operators'; import {setCurrentUserStatus} from '@actions/local/user'; import {fetchStatusByIds} from '@actions/remote/user'; -import {handleEvent, handleFirstConnect, handleReconnect} from '@actions/websocket'; +import {handleFirstConnect, handleReconnect} from '@actions/websocket'; +import {handleWebSocketEvent} from '@actions/websocket/event'; import WebSocketClient from '@client/websocket'; import {General} from '@constants'; import DatabaseManager from '@database/manager'; @@ -84,7 +85,7 @@ class WebsocketManager { const client = new WebSocketClient(serverUrl, bearerToken); client.setFirstConnectCallback(() => this.onFirstConnect(serverUrl)); - client.setEventCallback((evt: any) => handleEvent(serverUrl, evt)); + client.setEventCallback((evt: WebSocketMessage) => handleWebSocketEvent(serverUrl, evt)); //client.setMissedEventsCallback(() => {}) Nothing to do on missedEvents callback client.setReconnectCallback(() => this.onReconnect(serverUrl)); diff --git a/app/products/calls/actions/calls.ts b/app/products/calls/actions/calls.ts index fb5f5b0097c..2c7d6b2947a 100644 --- a/app/products/calls/actions/calls.ts +++ b/app/products/calls/actions/calls.ts @@ -30,6 +30,7 @@ import { setSpeakerPhone, } from '@calls/state'; import {type AudioDevice, type Call, type CallSession, type CallsConnection, EndCallReturn} from '@calls/types/calls'; +import {areGroupCallsAllowed} from '@calls/utils'; import {General, Preferences} from '@constants'; import Calls from '@constants/calls'; import DatabaseManager from '@database/manager'; @@ -493,7 +494,7 @@ export const dismissIncomingCall = async (serverUrl: string, channelId: string) }; // handleCallsSlashCommand will return true if the slash command was handled -export const handleCallsSlashCommand = async (value: string, serverUrl: string, channelId: string, rootId: string, currentUserId: string, intl: IntlShape): +export const handleCallsSlashCommand = async (value: string, serverUrl: string, channelId: string, channelType: string, rootId: string, currentUserId: string, intl: IntlShape): Promise<{ handled?: boolean; error?: string }> => { const tokens = value.split(' '); if (tokens.length < 2 || tokens[0] !== '/call') { @@ -505,6 +506,15 @@ export const handleCallsSlashCommand = async (value: string, serverUrl: string, await handleEndCall(serverUrl, channelId, currentUserId, intl); return {handled: true}; case 'start': { + if (!areGroupCallsAllowed(getCallsConfig(serverUrl)) && channelType !== General.DM_CHANNEL) { + return { + error: intl.formatMessage({ + id: 'mobile.calls_group_calls_not_available', + defaultMessage: 'Calls are only available in DM channels.', + }), + }; + } + if (getChannelsWithCalls(serverUrl)[channelId]) { return { error: intl.formatMessage({ @@ -518,13 +528,22 @@ export const handleCallsSlashCommand = async (value: string, serverUrl: string, return {handled: true}; } case 'join': { + if (!areGroupCallsAllowed(getCallsConfig(serverUrl)) && channelType !== General.DM_CHANNEL) { + return { + error: intl.formatMessage({ + id: 'mobile.calls_group_calls_not_available', + defaultMessage: 'Calls are only available in DM channels.', + }), + }; + } + const title = tokens.length > 2 ? tokens.slice(2).join(' ') : undefined; await leaveAndJoinWithAlert(intl, serverUrl, channelId, title, rootId); return {handled: true}; } case 'leave': if (getCurrentCall()?.channelId === channelId) { - await leaveCall(); + leaveCall(); return {handled: true}; } return { diff --git a/app/products/calls/alerts.ts b/app/products/calls/alerts.ts index 20007e4fa9c..092e20c4028 100644 --- a/app/products/calls/alerts.ts +++ b/app/products/calls/alerts.ts @@ -310,7 +310,7 @@ export const recordingAlert = (isHost: boolean, transcriptionsEnabled: boolean, defaultMessage: 'Leave', }), onPress: async () => { - await leaveCall(); + leaveCall(); }, style: 'destructive', }, diff --git a/app/products/calls/components/captions.tsx b/app/products/calls/components/captions.tsx index 47bffee4197..dff6410d0b3 100644 --- a/app/products/calls/components/captions.tsx +++ b/app/products/calls/components/captions.tsx @@ -21,11 +21,15 @@ const styles = StyleSheet.create({ captionContainer: { display: 'flex', height: 400, - bottom: -352, // 48-400, to place the bottoms at the same place + bottom: 400 - 48, // to place the bottoms at the same place gap: 8, alignItems: 'center', flexDirection: 'column-reverse', overflow: 'hidden', + + // needed so that events (e.g., long press on participants) + // still work when the captions overlay is rendered on top + pointerEvents: 'none', }, caption: { paddingTop: 1, diff --git a/app/products/calls/components/channel_info_enable_calls/index.tsx b/app/products/calls/components/channel_info_enable_calls/index.tsx index ac3883f589f..24779c74984 100644 --- a/app/products/calls/components/channel_info_enable_calls/index.tsx +++ b/app/products/calls/components/channel_info_enable_calls/index.tsx @@ -6,6 +6,7 @@ import {useIntl} from 'react-intl'; import {enableChannelCalls} from '@calls/actions'; import {useTryCallsFunction} from '@calls/hooks'; +import {getCallsConfig} from '@calls/state'; import OptionItem from '@components/option_item'; import {useServerUrl} from '@context/server'; import {preventDoubleTap} from '@utils/tap'; @@ -28,6 +29,11 @@ const ChannelInfoEnableCalls = ({channelId, enabled}: Props) => { const disableText = formatMessage({id: 'mobile.calls_disable', defaultMessage: 'Disable calls'}); const enableText = formatMessage({id: 'mobile.calls_enable', defaultMessage: 'Enable calls'}); + const config = getCallsConfig(serverUrl); + if (!config.pluginEnabled) { + return null; + } + return ( { const serverUrl = useServerUrl(); const insets = useSafeAreaInsets(); @@ -45,7 +47,7 @@ const FloatingCallContainer = ({ const topBarForTablet = (isTablet && !threadScreen) ? TABLET_HEADER_HEIGHT : 0; const topBarChannel = (!isTablet && !threadScreen) ? DEFAULT_HEADER_HEIGHT : 0; const wrapperTop = { - top: insets.top + topBarForTablet + topBarChannel, + top: insets.top + topBarForTablet + topBarChannel + (includeBookmarkBar ? BOOKMARKS_BAR_HEIGHT : 0), }; const wrapperBottom = { bottom: 8, diff --git a/app/products/calls/connection/connection.test.ts b/app/products/calls/connection/connection.test.ts new file mode 100644 index 00000000000..1cb602d16e0 --- /dev/null +++ b/app/products/calls/connection/connection.test.ts @@ -0,0 +1,130 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. +import {RTCMonitor, RTCPeer, parseRTCStats} from '@mattermost/calls/lib'; +import {Platform} from 'react-native'; +import InCallManager from 'react-native-incall-manager'; + +import NetworkManager from '@managers/network_manager'; + +import {newConnection} from './connection'; +import {WebSocketClient} from './websocket_client'; + +jest.mock('./websocket_client'); +jest.mock('@mattermost/calls/lib'); + +describe('newConnection', () => { + const mockClient = { + getWebSocketUrl: jest.fn(() => 'ws://localhost:8065'), + getCallsConfig: jest.fn(() => ({})), + }; + + const mockRTCStats = { + iceStats: { + succeeded: [ + { + id: 'candidatePairA', + timestamp: 45, + priority: 45, + state: 'succeeded', + local: { + candidateType: 'host', + protocol: 'udp', + port: 45000, + }, + remote: { + candidateType: 'host', + protocol: 'udp', + port: 8443, + }, + }, + ], + failed: [], + }, + }; + + beforeAll(() => { + // eslint-disable-next-line + // @ts-ignore + global.navigator = {}; + + // eslint-disable-next-line + // @ts-ignore + NetworkManager.getClient = jest.fn(() => mockClient); + + // eslint-disable-next-line + // @ts-ignore + RTCPeer.mockImplementation(() => { + return { + getStats: jest.fn(), + on: jest.fn(), + }; + }); + + // eslint-disable-next-line + // @ts-ignore + RTCMonitor.mockImplementation(() => { + return { + on: jest.fn(), + }; + }); + + Platform.OS = 'web'; + + InCallManager.start = jest.fn(); + InCallManager.stopProximitySensor = jest.fn(); + }); + + afterAll(() => { + // eslint-disable-next-line + // @ts-ignore + delete global.navigator; + + jest.resetAllMocks(); + }); + + it('collectICEStats', (done) => { + const wsSend = jest.fn(); + const joinHandler = jest.fn(async (event: string, cb: () => void) => { + if (event === 'join') { + await cb(); + expect(parseRTCStats).toHaveBeenCalled(); + expect(wsSend).toHaveBeenCalledWith('metric', { + metric_name: 'client_ice_candidate_pair', + data: JSON.stringify({ + state: 'succeeded', + local: { + type: 'host', + protocol: 'udp', + }, + remote: { + type: 'host', + protocol: 'udp', + }, + }), + }, + ); + done(); + } + }); + + // eslint-disable-next-line + // @ts-ignore + WebSocketClient.mockImplementation(() => { + return { + initialize: jest.fn(), + on: joinHandler, + send: wsSend, + }; + }); + + // eslint-disable-next-line + // @ts-ignore + parseRTCStats.mockImplementation(() => mockRTCStats); + + newConnection('http://localhost:8065', 'channelID', () => {}, () => {}, false). + then((connection) => { + expect(connection).toBeDefined(); + expect(joinHandler).toHaveBeenCalled(); + }); + }); +}); diff --git a/app/products/calls/connection/connection.ts b/app/products/calls/connection/connection.ts index 2586e29cc65..677834759ce 100644 --- a/app/products/calls/connection/connection.ts +++ b/app/products/calls/connection/connection.ts @@ -1,11 +1,11 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {RTCMonitor, RTCPeer} from '@mattermost/calls/lib'; +import {RTCMonitor, RTCPeer, parseRTCStats} from '@mattermost/calls/lib'; import {deflate} from 'pako'; import {DeviceEventEmitter, type EmitterSubscription, NativeEventEmitter, NativeModules, Platform} from 'react-native'; import InCallManager from 'react-native-incall-manager'; -import {mediaDevices, MediaStream, MediaStreamTrack, RTCPeerConnection} from 'react-native-webrtc'; +import {mediaDevices, MediaStream, MediaStreamTrack, registerGlobals} from 'react-native-webrtc'; import {setPreferredAudioRoute, setSpeakerphoneOn} from '@calls/actions/calls'; import { @@ -19,7 +19,7 @@ import {getICEServersConfigs} from '@calls/utils'; import {WebsocketEvents} from '@constants'; import {getServerCredentials} from '@init/credentials'; import NetworkManager from '@managers/network_manager'; -import {getFullErrorMessage} from '@utils/errors'; +import {getErrorMessage, getFullErrorMessage} from '@utils/errors'; import {logDebug, logError, logInfo, logWarning} from '@utils/log'; import {WebSocketClient, wsReconnectionTimeoutErr} from './websocket_client'; @@ -80,10 +80,29 @@ export async function newConnection( } }; + // Registering WebRTC globals (e.g. RTCPeerConnection) + registerGlobals(); + // getClient can throw an error, which will be handled by the caller. const client = NetworkManager.getClient(serverUrl); const credentials = await getServerCredentials(serverUrl); + let config; + try { + config = await client.getCallsConfig(); + } catch (err) { + throw new Error(`calls: fetching calls config: ${getFullErrorMessage(err)}`); + } + + let av1Support = false; + if (config.EnableAV1 && !config.EnableSimulcast) { + try { + av1Support = Boolean(await RTCPeer.getVideoCodec('video/AV1')); + } catch (err) { + throw new Error(`calls: failed to check AV1 support: ${getErrorMessage(err)}`); + } + } + const ws = new WebSocketClient(serverUrl, client.getWebSocketUrl(), credentials?.token); // Throws an error, to be caught by caller. @@ -207,6 +226,64 @@ export async function newConnection( } }; + const collectICEStats = () => { + const start = Date.now(); + const seenMap: {[key: string]: string} = {}; + + const gatherStats = async () => { + if (!peer) { + return; + } + + try { + const stats = parseRTCStats(await peer.getStats()).iceStats; + for (const state of Object.keys(stats)) { + for (const pair of stats[state]) { + const seenState = seenMap[pair.id]; + seenMap[pair.id] = pair.state; + + if (seenState !== pair.state) { + logDebug('calls: ice candidate pair stats', JSON.stringify(pair)); + } + + if (seenState === 'succeeded' || state !== 'succeeded') { + continue; + } + + if (!pair.local || !pair.remote) { + continue; + } + + ws.send('metric', { + metric_name: 'client_ice_candidate_pair', + data: JSON.stringify({ + state: pair.state, + local: { + type: pair.local.candidateType, + protocol: pair.local.protocol, + }, + remote: { + type: pair.remote.candidateType, + protocol: pair.remote.protocol, + }, + }), + }); + } + } + } catch (err) { + logError('failed to parse ICE stats', err); + } + + // Repeat the check for at most 30 seconds. + if (Date.now() < start + 30000) { + // We check every two seconds. + setTimeout(gatherStats, 2000); + } + }; + + gatherStats(); + }; + ws.on('error', (err: Error) => { logDebug('calls: ws error', err); if (err === wsReconnectionTimeoutErr) { @@ -228,23 +305,18 @@ export async function newConnection( }); } else { logDebug('calls: ws open, sending join msg'); + ws.send('join', { channelID, title, threadID: rootId, + av1Support, }); } }); ws.on('join', async () => { logDebug('calls: join ack received, initializing connection'); - let config; - try { - config = await client.getCallsConfig(); - } catch (err) { - logError('calls: fetching calls config:', getFullErrorMessage(err)); - return; - } const iceConfigs = getICEServersConfigs(config); if (config.NeedsTURNCredentials) { @@ -313,12 +385,10 @@ export async function newConnection( peer = new RTCPeer({ iceServers: iceConfigs || [], logger, - webrtc: { - MediaStream, - RTCPeerConnection, - }, }); + collectICEStats(); + rtcMonitor = new RTCMonitor({ peer, logger, diff --git a/app/products/calls/connection/websocket_event_handlers.ts b/app/products/calls/connection/websocket_event_handlers.ts index 9a0693ddac5..5c1c9aac003 100644 --- a/app/products/calls/connection/websocket_event_handlers.ts +++ b/app/products/calls/connection/websocket_event_handlers.ts @@ -250,5 +250,13 @@ export const handleCallState = (serverUrl: string, msg: WebSocketMessage { const appState = useAppState(); + const [hasPermission, setHasPermission] = useState(micPermissionsGranted); useEffect(() => { const asyncFn = async () => { if (appState === 'active') { - const hasPermission = (await Permissions.check(micPermission)) === Permissions.RESULTS.GRANTED; - if (hasPermission) { + const result = (await Permissions.check(micPermission)) === Permissions.RESULTS.GRANTED; + setHasPermission(result); + if (result) { initializeVoiceTrack(); - setMicPermissionsGranted(hasPermission); + setMicPermissionsGranted(result); } } }; @@ -130,6 +132,8 @@ export const usePermissionsChecker = (micPermissionsGranted: boolean) => { asyncFn(); } }, [appState]); + + return hasPermission; }; export const useCallsAdjustment = (serverUrl: string, channelId: string): number => { @@ -139,6 +143,7 @@ export const useCallsAdjustment = (serverUrl: string, channelId: string): number const globalCallsState = useGlobalCallsState(); const currentCall = useCurrentCall(); const [numServers, setNumServers] = useState(1); + const micPermissionsGranted = usePermissionsChecker(globalCallsState.micPermissionsGranted); const dismissed = Boolean(callsState.calls[channelId]?.dismissed[callsState.myUserId]); const inCurrentCall = currentCall?.id === channelId; const joinCallBannerVisible = Boolean(channelsWithCalls[channelId]) && !dismissed && !inCurrentCall; @@ -153,7 +158,7 @@ export const useCallsAdjustment = (serverUrl: string, channelId: string): number // Do we have calls banners? const currentCallBarVisible = Boolean(currentCall); - const micPermissionsError = !globalCallsState.micPermissionsGranted && (currentCall && !currentCall.micPermissionsErrorDismissed); + const micPermissionsError = !micPermissionsGranted && (currentCall && !currentCall.micPermissionsErrorDismissed); const callQualityAlert = Boolean(currentCall?.callQualityAlert); const incomingCallsShowing = incomingCalls.filter((ic) => ic.channelID !== channelId); const notificationBarHeight = CALL_NOTIFICATION_BAR_HEIGHT + (numServers > 1 ? 8 : 0); diff --git a/app/products/calls/observers/index.ts b/app/products/calls/observers/index.ts index 038bee5309d..bb4a1b10049 100644 --- a/app/products/calls/observers/index.ts +++ b/app/products/calls/observers/index.ts @@ -29,6 +29,10 @@ export type LimitRestrictedInfo = { } export const observeIsCallsEnabledInChannel = (database: Database, serverUrl: string, channelId: Observable) => { + const callsPluginEnabled = observeCallsConfig(serverUrl).pipe( + switchMap((config) => of$(config.pluginEnabled)), + distinctUntilChanged(), + ); const callsDefaultEnabled = observeCallsConfig(serverUrl).pipe( switchMap((config) => of$(config.DefaultEnabled)), distinctUntilChanged(), @@ -40,8 +44,12 @@ export const observeIsCallsEnabledInChannel = (database: Database, serverUrl: st const callsGAServer = observeConfigValue(database, 'Version').pipe( switchMap((v) => of$(isMinimumServerVersion(v || '', 7, 6))), ); - return combineLatest([channelId, callsStateEnabledDict, callsDefaultEnabled, callsGAServer]).pipe( - switchMap(([id, enabled, defaultEnabled, gaServer]) => { + return combineLatest([callsPluginEnabled, channelId, callsStateEnabledDict, callsDefaultEnabled, callsGAServer]).pipe( + switchMap(([pluginEnabled, id, enabled, defaultEnabled, gaServer]) => { + if (!pluginEnabled) { + return of$(false); + } + const explicitlyEnabled = enabled.hasOwnProperty(id as string) && enabled[id]; const explicitlyDisabled = enabled.hasOwnProperty(id as string) && !enabled[id]; return of$(explicitlyEnabled || (!explicitlyDisabled && defaultEnabled) || (!explicitlyDisabled && gaServer)); diff --git a/app/products/calls/types/calls.ts b/app/products/calls/types/calls.ts index 1d4dad9cfcd..b8803bafbbc 100644 --- a/app/products/calls/types/calls.ts +++ b/app/products/calls/types/calls.ts @@ -1,12 +1,14 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import type { - CallJobState, - CallsConfig, - EmojiData, - UserReactionData, +import { + TranscribeAPI, + type CallJobState, + type CallsConfig, + type EmojiData, + type UserReactionData, } from '@mattermost/calls/lib/types'; + import type UserModel from '@typings/database/models/servers/user'; export type GlobalCallsState = { @@ -149,6 +151,7 @@ export type CallsConnection = { export type CallsConfigState = CallsConfig & { AllowEnableCalls: boolean; + GroupCallsAllowed: boolean; pluginEnabled: boolean; version: CallsVersion; last_retrieved_at: number; @@ -173,6 +176,9 @@ export const DefaultCallsConfig: CallsConfigState = { EnableTranscriptions: false, EnableLiveCaptions: false, HostControlsAllowed: false, + EnableAV1: false, + TranscribeAPI: TranscribeAPI.WhisperCPP, + GroupCallsAllowed: true, // Set to true to keep backward compatibility with older servers. }; export type ApiResp = { diff --git a/app/products/calls/utils.ts b/app/products/calls/utils.ts index 7f8b162afa9..086df8ec3cf 100644 --- a/app/products/calls/utils.ts +++ b/app/products/calls/utils.ts @@ -108,6 +108,10 @@ export function isHostControlsAllowed(config: CallsConfigState) { return Boolean(config.HostControlsAllowed); } +export function areGroupCallsAllowed(config: CallsConfigState) { + return Boolean(config.GroupCallsAllowed); +} + export function isCallsCustomMessage(post: PostModel | Post): boolean { return Boolean(post.type && post.type === Post.POST_TYPES.CUSTOM_CALLS); } diff --git a/app/queries/servers/channel.ts b/app/queries/servers/channel.ts index 71c9b2225aa..fd846912e5c 100644 --- a/app/queries/servers/channel.ts +++ b/app/queries/servers/channel.ts @@ -20,6 +20,7 @@ import {observeTeammateNameDisplay} from './user'; import type ServerDataOperator from '@database/operator/server_data_operator'; import type {Clause} from '@nozbe/watermelondb/QueryDescription'; import type ChannelModel from '@typings/database/models/servers/channel'; +import type ChannelBookmarkModel from '@typings/database/models/servers/channel_bookmark'; import type ChannelInfoModel from '@typings/database/models/servers/channel_info'; import type ChannelMembershipModel from '@typings/database/models/servers/channel_membership'; import type MyChannelModel from '@typings/database/models/servers/my_channel'; @@ -28,6 +29,29 @@ import type UserModel from '@typings/database/models/servers/user'; const {SERVER: {CHANNEL, MY_CHANNEL, CHANNEL_MEMBERSHIP, MY_CHANNEL_SETTINGS, CHANNEL_INFO, USER, TEAM}} = MM_TABLES; +type ChannelMembershipsExtended = Pick; + +function prepareChannels( + operator: ServerDataOperator, + channels?: Channel[], + channelInfos?: ChannelInfo[], + channelMemberships?: ChannelMembershipsExtended[], + memberships?: ChannelMembership[], + isCRTEnabled?: boolean, +): Array> { + try { + const channelRecords = operator.handleChannel({channels, prepareRecordsOnly: true}); + const channelInfoRecords = operator.handleChannelInfo({channelInfos, prepareRecordsOnly: true}); + const membershipRecords = operator.handleChannelMembership({channelMemberships, prepareRecordsOnly: true}); + const myChannelRecords = operator.handleMyChannel({channels, myChannels: memberships, prepareRecordsOnly: true, isCRTEnabled}); + const myChannelSettingsRecords = operator.handleMyChannelSettings({settings: memberships, prepareRecordsOnly: true}); + + return [channelRecords, channelInfoRecords, membershipRecords, myChannelRecords, myChannelSettingsRecords]; + } catch { + return []; + } +} + export function prepareMissingChannelsForAllTeams(operator: ServerDataOperator, channels: Channel[], channelMembers: ChannelMembership[], isCRTEnabled?: boolean): Array> { const channelInfos: ChannelInfo[] = []; const channelMap: Record = {}; @@ -54,17 +78,7 @@ export function prepareMissingChannelsForAllTeams(operator: ServerDataOperator, }; }); - try { - const channelRecords = operator.handleChannel({channels, prepareRecordsOnly: true}); - const channelInfoRecords = operator.handleChannelInfo({channelInfos, prepareRecordsOnly: true}); - const membershipRecords = operator.handleChannelMembership({channelMemberships: memberships, prepareRecordsOnly: true}); - const myChannelRecords = operator.handleMyChannel({channels, myChannels: memberships, prepareRecordsOnly: true, isCRTEnabled}); - const myChannelSettingsRecords = operator.handleMyChannelSettings({settings: memberships, prepareRecordsOnly: true}); - - return [channelRecords, channelInfoRecords, membershipRecords, myChannelRecords, myChannelSettingsRecords]; - } catch { - return []; - } + return prepareChannels(operator, channels, channelInfos, memberships, memberships, isCRTEnabled); } export const prepareMyChannelsForTeam = async (operator: ServerDataOperator, teamId: string, channels: Channel[], channelMembers: ChannelMembership[], isCRTEnabled?: boolean) => { @@ -117,17 +131,7 @@ export const prepareMyChannelsForTeam = async (operator: ServerDataOperator, tea }); } - try { - const channelRecords = operator.handleChannel({channels, prepareRecordsOnly: true}); - const channelInfoRecords = operator.handleChannelInfo({channelInfos, prepareRecordsOnly: true}); - const membershipRecords = operator.handleChannelMembership({channelMemberships: channelMembers, prepareRecordsOnly: true}); - const myChannelRecords = operator.handleMyChannel({channels, myChannels: memberships, prepareRecordsOnly: true, isCRTEnabled}); - const myChannelSettingsRecords = operator.handleMyChannelSettings({settings: memberships, prepareRecordsOnly: true}); - - return [channelRecords, channelInfoRecords, membershipRecords, myChannelRecords, myChannelSettingsRecords]; - } catch { - return []; - } + return prepareChannels(operator, channels, channelInfos, channelMembers, memberships, isCRTEnabled); }; export const prepareDeleteChannel = async (channel: ChannelModel): Promise => { @@ -169,6 +173,27 @@ export const prepareDeleteChannel = async (channel: ChannelModel): Promise { + const preparedModels: Model[] = [bookmark.prepareDestroyPermanently()]; + try { + if (bookmark.fileId) { + const file = await bookmark.file.fetch(); + preparedModels.push(file.prepareDestroyPermanently()); + } + } catch { + // Record not found, do nothing + } return preparedModels; }; diff --git a/app/queries/servers/channel_bookmark.ts b/app/queries/servers/channel_bookmark.ts new file mode 100644 index 00000000000..9baeeecdec1 --- /dev/null +++ b/app/queries/servers/channel_bookmark.ts @@ -0,0 +1,117 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import {Q, type Database} from '@nozbe/watermelondb'; +import {of as of$} from 'rxjs'; +import {distinctUntilChanged, switchMap, combineLatestWith} from 'rxjs/operators'; + +import {General, Permissions} from '@constants'; +import {MM_TABLES} from '@constants/database'; +import ChannelBookmarkModel from '@typings/database/models/servers/channel_bookmark'; +import {isDMorGM} from '@utils/channel'; +import {isMinimumServerVersion} from '@utils/helpers'; + +import {observeChannel} from './channel'; +import {observePermissionForChannel} from './role'; +import {observeConfigValue} from './system'; +import {observeCurrentUser} from './user'; + +const {CHANNEL_BOOKMARK} = MM_TABLES.SERVER; + +const observeHasPermissionToBookmarks = ( + database: Database, + channelId: string, + public_permission: string, + private_permission: string, +) => { + const serverVersion = observeConfigValue(database, 'Version'); + const currentUser = observeCurrentUser(database); + + return observeChannel(database, channelId).pipe( + combineLatestWith(currentUser, serverVersion), + switchMap(([c, user, version]) => { + if (!c || !user || c.deleteAt !== 0 || user?.isGuest || !isMinimumServerVersion(version || '', 9, 4)) { + return of$(false); + } + + if (isDMorGM(c)) { + return of$(true); + } + + const permission = c.type === General.OPEN_CHANNEL ? public_permission : private_permission; + return observePermissionForChannel(database, c, user, permission, true); + }), + distinctUntilChanged(), + ); +}; + +export const observeCanAddBookmarks = (database: Database, channelId: string) => { + return observeHasPermissionToBookmarks( + database, + channelId, + Permissions.ADD_BOOKMARK_PUBLIC_CHANNEL, + Permissions.ADD_BOOKMARK_PRIVATE_CHANNEL, + ); +}; + +export const observeCanEditBookmarks = (database: Database, channelId: string) => { + return observeHasPermissionToBookmarks( + database, + channelId, + Permissions.EDIT_BOOKMARK_PUBLIC_CHANNEL, + Permissions.EDIT_BOOKMARK_PRIVATE_CHANNEL, + ); +}; + +export const observeCanDeleteBookmarks = (database: Database, channelId: string) => { + return observeHasPermissionToBookmarks( + database, + channelId, + Permissions.DELETE_BOOKMARK_PUBLIC_CHANNEL, + Permissions.DELETE_BOOKMARK_PRIVATE_CHANNEL, + ); +}; + +export const getChannelBookmarkById = async (database: Database, bookmarkId: string) => { + try { + const bookmark = await database.get(CHANNEL_BOOKMARK).find(bookmarkId); + return bookmark; + } catch { + return undefined; + } +}; + +export const queryBookmarks = (database: Database, channelId: string) => { + return database.get(CHANNEL_BOOKMARK).query( + Q.and( + Q.where('channel_id', channelId), + Q.where('delete_at', Q.eq(0)), + ), + Q.sortBy('sort_order', Q.asc), + ); +}; + +export const getBookmarksSince = async (database: Database, channelId: string) => { + try { + const result = await database.get(CHANNEL_BOOKMARK).query( + Q.unsafeSqlQuery( + `SELECT + COALESCE( + MAX ( + MAX(COALESCE(create_at, 0)), + MAX(COALESCE(update_at, 0)), + MAX(COALESCE(delete_at, 0)) + ) + 1, 0) as mostRecent + FROM ChannelBookmark + WHERE channel_id='${channelId}'`), + ).unsafeFetchRaw(); + + return result?.[0]?.mostRecent ?? 0; + } catch { + return 0; + } +}; + +export const observeBookmarks = (database: Database, channelId: string) => { + return queryBookmarks(database, channelId).observeWithColumns(['file_id']); +}; diff --git a/app/queries/servers/entry.ts b/app/queries/servers/entry.ts index ec5149b852f..60ed5ffb0ba 100644 --- a/app/queries/servers/entry.ts +++ b/app/queries/servers/entry.ts @@ -33,6 +33,7 @@ type PrepareModelsArgs = { } const { + CHANNEL_BOOKMARK, POST, POSTS_IN_CHANNEL, POSTS_IN_THREAD, @@ -100,6 +101,7 @@ export async function truncateCrtRelatedTables(serverUrl: string): Promise<{erro [`DELETE FROM ${THREAD_PARTICIPANT}`, []], [`DELETE FROM ${TEAM_THREADS_SYNC}`, []], [`DELETE FROM ${MY_CHANNEL}`, []], + [`DELETE FROM ${CHANNEL_BOOKMARK}`, []], ], }); }); diff --git a/app/queries/servers/file.ts b/app/queries/servers/file.ts index 7226058d22b..501181954ff 100644 --- a/app/queries/servers/file.ts +++ b/app/queries/servers/file.ts @@ -2,6 +2,8 @@ // See LICENSE.txt for license information. import {Database, Q} from '@nozbe/watermelondb'; +import {of as of$} from 'rxjs'; +import {switchMap} from 'rxjs/operators'; import {MM_TABLES} from '@constants/database'; @@ -18,6 +20,12 @@ export const getFileById = async (database: Database, fileId: string) => { } }; +export const observeFileById = (database: Database, id: string) => { + return database.get(FILE).query(Q.where('id', id), Q.take(1)).observe().pipe( + switchMap((result) => (result.length ? result[0].observe() : of$(undefined))), + ); +}; + export const queryFilesForPost = (database: Database, postId: string) => { return database.get(FILE).query( Q.where('post_id', postId), diff --git a/app/screens/channel/channel.tsx b/app/screens/channel/channel.tsx index 2d0909c9398..845d20e6bbf 100644 --- a/app/screens/channel/channel.tsx +++ b/app/screens/channel/channel.tsx @@ -34,12 +34,14 @@ type ChannelProps = { showJoinCallBanner: boolean; isInACall: boolean; isCallsEnabledInChannel: boolean; + groupCallsAllowed: boolean; showIncomingCalls: boolean; isTabletView?: boolean; dismissedGMasDMNotice: PreferenceModel[]; currentUserId: string; channelType: ChannelType; hasGMasDMFeature: boolean; + includeBookmarkBar?: boolean; }; const edges: Edge[] = ['left', 'right']; @@ -57,12 +59,14 @@ const Channel = ({ showJoinCallBanner, isInACall, isCallsEnabledInChannel, + groupCallsAllowed, showIncomingCalls, isTabletView, dismissedGMasDMNotice, channelType, currentUserId, hasGMasDMFeature, + includeBookmarkBar, }: ChannelProps) => { useGMasDMNotice(currentUserId, channelType, dismissedGMasDMNotice, hasGMasDMFeature); const isTablet = useIsTablet(); @@ -123,7 +127,9 @@ const Channel = ({ channelId={channelId} componentId={componentId} callsEnabledInChannel={isCallsEnabledInChannel} + groupCallsAllowed={groupCallsAllowed} isTabletView={isTabletView} + shouldRenderBookmarks={shouldRender} /> {shouldRender && <> @@ -145,12 +151,13 @@ const Channel = ({ /> } - {showFloatingCallContainer && + {showFloatingCallContainer && shouldRender && } diff --git a/app/screens/channel/channel_post_list/intro/illustration/private.tsx b/app/screens/channel/channel_post_list/intro/illustration/private.tsx index 96708e99b94..79fcc142074 100644 --- a/app/screens/channel/channel_post_list/intro/illustration/private.tsx +++ b/app/screens/channel/channel_post_list/intro/illustration/private.tsx @@ -87,8 +87,6 @@ const PrivateChannelIllustration = ({theme}: Props) => ( /> ( /> ( /> ( /> ( /> ( /> ({ + container: { + backgroundColor: theme.sidebarBg, + width: '100%', + position: 'absolute', + }, + content: { + backgroundColor: theme.centerChannelBg, + borderTopLeftRadius: 12, + borderTopRightRadius: 12, + }, + separator: { + height: 1, + backgroundColor: changeOpacity(theme.centerChannelColor, 0.08), + }, + separatorContainer: { + backgroundColor: theme.centerChannelBg, + zIndex: 1, + }, + padding: { + paddingTop: 2, + }, + paddingHorizontal: { + paddingHorizontal: 10, + }, +})); + +const ChannelHeaderBookmarks = ({canAddBookmarks, channelId}: Props) => { + const theme = useTheme(); + const defaultHeight = useDefaultHeaderHeight(); + const styles = getStyleSheet(theme); + + const containerStyle = useMemo(() => ({ + ...styles.content, + top: defaultHeight, + zIndex: 1, + }), [defaultHeight]); + + return ( + + + + + + + + + + + ); +}; + +export default ChannelHeaderBookmarks; diff --git a/app/screens/channel/header/header.tsx b/app/screens/channel/header/header.tsx index 7d83a064ae1..0b93644c71b 100644 --- a/app/screens/channel/header/header.tsx +++ b/app/screens/channel/header/header.tsx @@ -6,6 +6,7 @@ import {useIntl} from 'react-intl'; import {Keyboard, Platform, Text, View} from 'react-native'; import {useSafeAreaInsets} from 'react-native-safe-area-context'; +import {getCallsConfig} from '@calls/state'; import {CHANNEL_ACTIONS_OPTIONS_HEIGHT} from '@components/channel_actions/channel_actions'; import CompassIcon from '@components/compass_icon'; import CustomStatusEmoji from '@components/custom_status/custom_status_emoji'; @@ -14,6 +15,7 @@ import {ITEM_HEIGHT} from '@components/option_item'; import OtherMentionsBadge from '@components/other_mentions_badge'; import RoundedHeaderContext from '@components/rounded_header_context'; import {General, Screens} from '@constants'; +import {useServerUrl} from '@context/server'; import {useTheme} from '@context/theme'; import {useIsTablet} from '@hooks/device'; import {useDefaultHeaderHeight} from '@hooks/header'; @@ -24,17 +26,21 @@ import {preventDoubleTap} from '@utils/tap'; import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme'; import {typography} from '@utils/typography'; +import ChannelHeaderBookmarks from './bookmarks'; import QuickActions, {MARGIN, SEPARATOR_HEIGHT} from './quick_actions'; import type {HeaderRightButton} from '@components/navigation_header/header'; import type {AvailableScreens} from '@typings/screens/navigation'; type ChannelProps = { + canAddBookmarks: boolean; channelId: string; channelType: ChannelType; customStatus?: UserCustomStatus; + isBookmarksEnabled: boolean; isCustomStatusEnabled: boolean; isCustomStatusExpired: boolean; + hasBookmarks: boolean; componentId?: AvailableScreens; displayName: string; isOwnDirectMessage: boolean; @@ -42,7 +48,9 @@ type ChannelProps = { searchTerm: string; teamId: string; callsEnabledInChannel: boolean; + groupCallsAllowed: boolean; isTabletView?: boolean; + shouldRenderBookmarks: boolean; }; const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({ @@ -71,9 +79,9 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({ })); const ChannelHeader = ({ - channelId, channelType, componentId, customStatus, displayName, - isCustomStatusEnabled, isCustomStatusExpired, isOwnDirectMessage, memberCount, - searchTerm, teamId, callsEnabledInChannel, isTabletView, + canAddBookmarks, channelId, channelType, componentId, customStatus, displayName, hasBookmarks, + isBookmarksEnabled, isCustomStatusEnabled, isCustomStatusExpired, isOwnDirectMessage, memberCount, + searchTerm, teamId, callsEnabledInChannel, groupCallsAllowed, isTabletView, shouldRenderBookmarks, }: ChannelProps) => { const intl = useIntl(); const isTablet = useIsTablet(); @@ -81,10 +89,16 @@ const ChannelHeader = ({ const theme = useTheme(); const styles = getStyleSheet(theme); const defaultHeight = useDefaultHeaderHeight(); + const serverUrl = useServerUrl(); + + const callsConfig = getCallsConfig(serverUrl); // NOTE: callsEnabledInChannel will be true/false (not undefined) based on explicit state + the DefaultEnabled system setting // which ultimately comes from channel/index.tsx, and observeIsCallsEnabledInChannel - const callsAvailable = callsEnabledInChannel; + let callsAvailable = callsConfig.pluginEnabled && callsEnabledInChannel; + if (!groupCallsAllowed && channelType !== General.DM_CHANNEL) { + callsAvailable = false; + } const isDMorGM = isTypeDMorGM(channelType); const contextStyle = useMemo(() => ({ @@ -244,6 +258,12 @@ const ChannelHeader = ({ + {isBookmarksEnabled && hasBookmarks && shouldRenderBookmarks && + + } ); }; diff --git a/app/screens/channel/header/index.ts b/app/screens/channel/header/index.ts index e59210e9682..64ee1767c3b 100644 --- a/app/screens/channel/header/index.ts +++ b/app/screens/channel/header/index.ts @@ -4,10 +4,11 @@ import {withDatabase, withObservables} from '@nozbe/watermelondb/react'; import React from 'react'; import {of as of$} from 'rxjs'; -import {combineLatestWith, switchMap} from 'rxjs/operators'; +import {combineLatestWith, distinctUntilChanged, switchMap} from 'rxjs/operators'; import {General} from '@constants'; import {observeChannel, observeChannelInfo} from '@queries/servers/channel'; +import {observeCanAddBookmarks, queryBookmarks} from '@queries/servers/channel_bookmark'; import {observeConfigBooleanValue, observeCurrentTeamId, observeCurrentUserId} from '@queries/servers/system'; import {observeUser} from '@queries/servers/user'; import { @@ -77,11 +78,21 @@ const enhanced = withObservables(['channelId'], ({channelId, database}: OwnProps const memberCount = channelInfo.pipe( combineLatestWith(dmUser), switchMap(([ci, dm]) => of$(dm ? undefined : ci?.memberCount))); + const hasBookmarks = queryBookmarks(database, channelId).observeCount(false).pipe( + switchMap((count) => of$(count > 0)), + distinctUntilChanged(), + ); + + const isBookmarksEnabled = observeConfigBooleanValue(database, 'FeatureFlagChannelBookmarks'); + const canAddBookmarks = observeCanAddBookmarks(database, channelId); return { + canAddBookmarks, channelType, customStatus, displayName, + hasBookmarks, + isBookmarksEnabled, isCustomStatusEnabled, isCustomStatusExpired, isOwnDirectMessage, diff --git a/app/screens/channel/index.tsx b/app/screens/channel/index.tsx index 094659b1bc2..f7b20dc1d99 100644 --- a/app/screens/channel/index.tsx +++ b/app/screens/channel/index.tsx @@ -2,15 +2,17 @@ // See LICENSE.txt for license information. import {withDatabase, withObservables} from '@nozbe/watermelondb/react'; -import {of as of$, switchMap} from 'rxjs'; +import {combineLatestWith, distinctUntilChanged, of as of$, switchMap} from 'rxjs'; import {observeCallStateInChannel, observeIsCallsEnabledInChannel} from '@calls/observers'; +import {observeCallsConfig} from '@calls/state'; import {Preferences} from '@constants'; import {withServerUrl} from '@context/server'; import {observeCurrentChannel} from '@queries/servers/channel'; +import {queryBookmarks} from '@queries/servers/channel_bookmark'; import {observeHasGMasDMFeature} from '@queries/servers/features'; import {queryPreferencesByCategoryAndName} from '@queries/servers/preference'; -import {observeCurrentChannelId, observeCurrentUserId} from '@queries/servers/system'; +import {observeConfigBooleanValue, observeCurrentChannelId, observeCurrentUserId} from '@queries/servers/system'; import Channel from './channel'; @@ -26,15 +28,37 @@ const enhanced = withObservables([], ({database, serverUrl}: EnhanceProps) => { const channelType = observeCurrentChannel(database).pipe(switchMap((c) => of$(c?.type))); const currentUserId = observeCurrentUserId(database); const hasGMasDMFeature = observeHasGMasDMFeature(database); + const isBookmarksEnabled = observeConfigBooleanValue(database, 'FeatureFlagChannelBookmarks'); + const hasBookmarks = (count: number) => of$(count > 0); + const includeBookmarkBar = channelId.pipe( + combineLatestWith(isBookmarksEnabled), + switchMap(([cId, enabled]) => { + if (!enabled) { + return of$(false); + } + + return queryBookmarks(database, cId).observeCount(false).pipe( + switchMap(hasBookmarks), + distinctUntilChanged(), + ); + }), + ); + + const groupCallsAllowed = observeCallsConfig(serverUrl).pipe( + switchMap((config) => of$(config.GroupCallsAllowed)), + distinctUntilChanged(), + ); return { channelId, ...observeCallStateInChannel(serverUrl, database, channelId), isCallsEnabledInChannel: observeIsCallsEnabledInChannel(database, serverUrl, channelId), + groupCallsAllowed, dismissedGMasDMNotice, channelType, currentUserId, hasGMasDMFeature, + includeBookmarkBar, }; }); diff --git a/app/screens/channel_bookmark/components/bookmark_detail.tsx b/app/screens/channel_bookmark/components/bookmark_detail.tsx new file mode 100644 index 00000000000..3f68a4c5e5d --- /dev/null +++ b/app/screens/channel_bookmark/components/bookmark_detail.tsx @@ -0,0 +1,140 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import {Button} from '@rneui/base'; +import React, {useCallback, useMemo} from 'react'; +import {useIntl} from 'react-intl'; +import {TextInput, View} from 'react-native'; + +import BookmarkIcon from '@components/channel_bookmarks/channel_bookmark/bookmark_icon'; +import CompassIcon from '@components/compass_icon'; +import FormattedText from '@components/formatted_text'; +import {Screens} from '@constants'; +import {useTheme} from '@context/theme'; +import {useIsTablet} from '@hooks/device'; +import {openAsBottomSheet} from '@screens/navigation'; +import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme'; +import {typography} from '@utils/typography'; + +type Props = { + disabled: boolean; + emoji?: string; + file?: ExtractedFileInfo; + imageUrl?: string; + setBookmarkDisplayName: (displayName: string) => void; + setBookmarkEmoji: (emoji?: string) => void; + title: string; +}; + +const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({ + title: { + color: theme.centerChannelColor, + marginBottom: 8, + ...typography('Heading', 100, 'SemiBold'), + }, + container: { + flexDirection: 'row', + }, + disabled: { + backgroundColor: changeOpacity(theme.centerChannelColor, 0.16), + }, + iconContainer: { + borderWidth: 1, + paddingLeft: 16, + paddingRight: 8, + borderColor: changeOpacity(theme.centerChannelColor, 0.16), + borderRightWidth: 0, + borderTopLeftRadius: 4, + borderBottomLeftRadius: 4, + alignItems: 'center', + justifyContent: 'center', + flexDirection: 'row', + }, + iconButton: { + backgroundColor: 'transparent', + alignItems: 'center', + justifyContent: 'center', + }, + imageContainer: {width: 28, height: 28, marginRight: 2}, + image: {width: 24, height: 24}, + input: { + borderBottomRightRadius: 4, + borderTopRightRadius: 4, + borderWidth: 1, + borderColor: changeOpacity(theme.centerChannelColor, 0.16), + paddingVertical: 12, + paddingHorizontal: 16, + flex: 1, + color: theme.centerChannelColor, + ...typography('Body', 200), + lineHeight: undefined, + }, + genericBookmark: { + alignSelf: 'center', + top: 2, + }, +})); + +const BookmarkDetail = ({disabled, emoji, file, imageUrl, setBookmarkDisplayName, setBookmarkEmoji, title}: Props) => { + const intl = useIntl(); + const theme = useTheme(); + const isTablet = useIsTablet(); + const paddingStyle = useMemo(() => ({paddingHorizontal: isTablet ? 42 : 0}), [isTablet]); + const styles = getStyleSheet(theme); + + const openEmojiPicker = useCallback(() => { + openAsBottomSheet({ + closeButtonId: 'close-add-emoji', + screen: Screens.EMOJI_PICKER, + theme, + title: intl.formatMessage({id: 'channel_bookmark.add.emoji', defaultMessage: 'Add emoji'}), + props: { + onEmojiPress: setBookmarkEmoji, + imageUrl, + file, + }, + }); + }, [imageUrl, file, theme, setBookmarkEmoji]); + + return ( + + + + + + + + ); +}; + +export default BookmarkDetail; diff --git a/app/screens/channel_bookmark/components/bookmark_file/bookmark_file.tsx b/app/screens/channel_bookmark/components/bookmark_file/bookmark_file.tsx new file mode 100644 index 00000000000..e77550b09d8 --- /dev/null +++ b/app/screens/channel_bookmark/components/bookmark_file/bookmark_file.tsx @@ -0,0 +1,360 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import {Button} from '@rneui/base'; +import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react'; +import {useIntl} from 'react-intl'; +import {View, Text, Platform, type Insets} from 'react-native'; +import {Shadow} from 'react-native-shadow-2'; + +import {uploadFile} from '@actions/remote/file'; +import CompassIcon from '@app/components/compass_icon'; +import FileIcon from '@app/components/files/file_icon'; +import ProgressBar from '@app/components/progress_bar'; +import FormattedText from '@components/formatted_text'; +import TouchableWithFeedback from '@components/touchable_with_feedback'; +import {useServerUrl} from '@context/server'; +import {useTheme} from '@context/theme'; +import {useIsTablet} from '@hooks/device'; +import {fileSizeWarning, getExtensionFromMime, getFormattedFileSize} from '@utils/file'; +import PickerUtil from '@utils/file/file_picker'; +import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme'; +import {typography} from '@utils/typography'; + +import type {ClientResponse} from '@mattermost/react-native-network-client'; + +type Props = { + channelId: string; + close: () => void; + disabled: boolean; + initialFile?: FileInfo; + maxFileSize: number; + setBookmark: (file: ExtractedFileInfo) => void; +} + +const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({ + viewContainer: { + marginTop: 32, + marginBottom: 24, + width: '100%', + flex: 0, + }, + title: { + color: theme.centerChannelColor, + marginBottom: 8, + ...typography('Heading', 100, 'SemiBold'), + }, + shadowContainer: { + alignItems: 'center', + borderColor: changeOpacity(theme.centerChannelColor, 0.16), + borderWidth: 1, + borderRadius: 4, + }, + fileContainer: { + height: 64, + flexDirection: 'row', + paddingLeft: 12, + alignItems: 'center', + }, + fileInfoContainer: { + paddingHorizontal: 16, + flex: 1, + justifyContent: 'center', + }, + filename: { + color: theme.centerChannelColor, + ...typography('Body', 200, 'SemiBold'), + }, + fileInfo: { + color: changeOpacity(theme.centerChannelColor, 0.64), + textTransform: 'uppercase', + ...typography('Body', 75), + }, + uploadError: { + color: theme.errorTextColor, + ...typography('Body', 75), + }, + retry: { + paddingRight: 20, + height: 40, + justifyContent: 'center', + }, + removeContainer: { + position: 'absolute', + elevation: 11, + top: -18, + right: -12, + width: 24, + height: 24, + }, + removeButton: { + borderRadius: 12, + alignSelf: 'center', + marginTop: Platform.select({ + ios: 5.4, + android: 4.75, + }), + backgroundColor: theme.centerChannelBg, + width: 24, + height: 25, + }, + uploading: { + color: changeOpacity(theme.centerChannelColor, 0.64), + ...typography('Body', 75), + }, + progressContainer: { + alignItems: 'center', + backgroundColor: 'rgba(0, 0, 0, 0.1)', + bottom: 2, + borderBottomRightRadius: 4, + borderBottomLeftRadius: 4, + }, + progress: { + borderRadius: 4, + borderTopRightRadius: 0, + borderTopLeftRadius: 0, + }, +})); + +const shadowSides = {top: false, bottom: true, end: true, start: false}; +const hitSlop: Insets = {top: 10, bottom: 10, left: 10, right: 10}; + +const BookmarkFile = ({channelId, close, disabled, initialFile, maxFileSize, setBookmark}: Props) => { + const theme = useTheme(); + const intl = useIntl(); + const isTablet = useIsTablet(); + const serverUrl = useServerUrl(); + const [file, setFile] = useState(initialFile); + const [error, setError] = useState(''); + const [progress, setProgress] = useState(0); + const [uploading, setUploading] = useState(false); + const [failed, setFailed] = useState(false); + const styles = getStyleSheet(theme); + const subContainerStyle = useMemo(() => [styles.viewContainer, {paddingHorizontal: isTablet ? 42 : 0}], [isTablet]); + const cancelUpload = useRef<() => void | undefined>(); + + const onProgress = useCallback((p: number, bytes: number) => { + if (!file) { + return; + } + + const f: ExtractedFileInfo = {...file}; + f.bytesRead = bytes; + + setProgress(p); + setFile(f); + }, []); + + const onComplete = useCallback((response: ClientResponse) => { + cancelUpload.current = undefined; + if (response.code !== 201 || !response.data) { + setUploadError(); + return; + } + + const data = response.data.file_infos as FileInfo[] | undefined; + if (!data?.length) { + setUploadError(); + return; + } + + const fileInfo = data[0]; + setFile(fileInfo); + setBookmark(fileInfo); + setUploading(false); + setProgress(1); + setFailed(false); + setError(''); + }, []); + + const onError = useCallback(() => { + cancelUpload.current = undefined; + setUploadError(); + }, []); + + const setUploadError = useCallback(() => { + setProgress(0); + setUploading(false); + setFailed(true); + + setError(intl.formatMessage({ + id: 'channel_bookmark.add.file_upload_error', + defaultMessage: 'Error uploading file. Please try again.', + })); + }, [file, intl]); + + const startUpload = useCallback((fileInfo: FileInfo | ExtractedFileInfo) => { + setUploading(true); + setProgress(0); + + const {cancel, error: uploadError} = uploadFile( + serverUrl, + fileInfo, + channelId, + onProgress, + onComplete, + onError, + fileInfo.bytesRead, + true, + ); + + if (cancel) { + cancelUpload.current = cancel; + } + + if (uploadError) { + setUploadError(); + cancelUpload.current?.(); + } + }, [channelId, onProgress, onComplete, onError, serverUrl]); + + const browseFile = useCallback(async () => { + const picker = new PickerUtil(intl, (files) => { + if (files.length) { + const f = files[0]; + const extension = getExtensionFromMime(f.mime_type) || ''; + const fileWithExtension: ExtractedFileInfo = {...f, extension}; + setFile(fileWithExtension); + startUpload(fileWithExtension); + } + }); + + const res = await picker.attachFileFromFiles(undefined, false); + if (res.error) { + close(); + } + }, [close, startUpload]); + + const removeAndUpload = useCallback(() => { + cancelUpload.current?.(); + browseFile(); + }, [file, browseFile]); + + const retry = useCallback(() => { + cancelUpload.current?.(); + if (file) { + startUpload(file); + } + }, [file, startUpload]); + + useEffect(() => { + if (!initialFile) { + browseFile(); + } + + return () => { + cancelUpload.current?.(); + }; + }, []); + + useEffect(() => { + if (uploading) { + return; + } + + if (!file?.id && (file?.size || 0) > maxFileSize) { + setError(fileSizeWarning(intl, maxFileSize)); + return; + } + + if (!file?.id && file?.name) { + setBookmark(file); + } + }, [file, intl, maxFileSize, uploading]); + + let info; + if (error) { + info = ( + + {error} + + ); + } else if (uploading) { + info = ( + + ); + } else if (file) { + info = ( + + {`${file.extension} ${getFormattedFileSize(file.size || 0)}`} + + ); + } + + if (file) { + return ( + + + + + + + + {decodeURIComponent(file.name.trim())} + + {info} + + {failed && + + } + + + + + + + + {uploading && + + + + } + + ); + } + + return null; +}; + +export default BookmarkFile; diff --git a/app/screens/channel_bookmark/components/bookmark_file/index.ts b/app/screens/channel_bookmark/components/bookmark_file/index.ts new file mode 100644 index 00000000000..723083e709b --- /dev/null +++ b/app/screens/channel_bookmark/components/bookmark_file/index.ts @@ -0,0 +1,19 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import {withDatabase, withObservables} from '@nozbe/watermelondb/react'; + +import {DEFAULT_SERVER_MAX_FILE_SIZE} from '@constants/post_draft'; +import {observeConfigIntValue} from '@queries/servers/system'; + +import BookmarkFile from './bookmark_file'; + +import type {WithDatabaseArgs} from '@typings/database/database'; + +const enhanced = withObservables([], ({database}: WithDatabaseArgs) => { + return { + maxFileSize: observeConfigIntValue(database, 'MaxFileSize', DEFAULT_SERVER_MAX_FILE_SIZE), + }; +}); + +export default withDatabase(enhanced(BookmarkFile)); diff --git a/app/screens/channel_bookmark/components/bookmark_link.tsx b/app/screens/channel_bookmark/components/bookmark_link.tsx new file mode 100644 index 00000000000..a4d59ca47bd --- /dev/null +++ b/app/screens/channel_bookmark/components/bookmark_link.tsx @@ -0,0 +1,133 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import React, {useCallback, useMemo, useState} from 'react'; +import {useIntl} from 'react-intl'; +import {Platform, View} from 'react-native'; + +import {useIsTablet} from '@app/hooks/device'; +import FloatingTextInput from '@components/floating_text_input_label'; +import FormattedText from '@components/formatted_text'; +import Loading from '@components/loading'; +import {useTheme} from '@context/theme'; +import {debounce} from '@helpers/api/general'; +import useDidUpdate from '@hooks/did_update'; +import {fetchOpenGraph} from '@utils/opengraph'; +import {changeOpacity, getKeyboardAppearanceFromTheme, makeStyleSheetFromTheme} from '@utils/theme'; +import {typography} from '@utils/typography'; +import {getUrlAfterRedirect} from '@utils/url'; + +type Props = { + disabled: boolean; + initialUrl?: string; + resetBookmark: () => void; + setBookmark: (url: string, title: string, imageUrl: string) => void; +} + +const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({ + viewContainer: { + marginVertical: 32, + width: '100%', + }, + description: { + marginTop: 8, + }, + descriptionText: { + ...typography('Body', 75), + color: changeOpacity(theme.centerChannelColor, 0.56), + }, + loading: { + alignItems: 'flex-end', + justifyContent: 'center', + }, +})); + +const BookmarkLink = ({disabled, initialUrl = '', resetBookmark, setBookmark}: Props) => { + const theme = useTheme(); + const intl = useIntl(); + const isTablet = useIsTablet(); + const [error, setError] = useState(''); + const [url, setUrl] = useState(initialUrl); + const [loading, setLoading] = useState(false); + const styles = getStyleSheet(theme); + const keyboard = (Platform.OS === 'android') ? 'default' : 'url'; + const subContainerStyle = useMemo(() => [styles.viewContainer, {paddingHorizontal: isTablet ? 42 : 0}], [isTablet, styles]); + const descContainer = useMemo(() => [styles.description, {paddingHorizontal: isTablet ? 42 : 0}], [isTablet, styles]); + + const validateAndFetchOG = useCallback(debounce(async (text: string) => { + setLoading(true); + let link = await getUrlAfterRedirect(text, false); + + if (link.error) { + link = await getUrlAfterRedirect(text, true); + } + + if (link.url) { + const result = await fetchOpenGraph(link.url, true); + const title = result.title || text; + const imageUrl = result.favIcon || result.imageURL || ''; + setLoading(false); + setBookmark(link.url, title, imageUrl); + return; + } + setError(intl.formatMessage({ + id: 'channel_bookmark_add.link.invalid', + defaultMessage: 'Please enter a valid link', + })); + setLoading(false); + }, 500), [intl]); + + const onChangeText = useCallback((text: string) => { + resetBookmark(); + setUrl(text); + setError(''); + }, [resetBookmark]); + + const onSubmitEditing = useCallback(() => { + if (url) { + validateAndFetchOG(url); + } + }, [url, error]); + + useDidUpdate(debounce(() => { + onSubmitEditing(); + }, 300), [onSubmitEditing]); + + return ( + + + } + /> + + + + + ); +}; + +export default BookmarkLink; diff --git a/app/screens/channel_bookmark/index.tsx b/app/screens/channel_bookmark/index.tsx new file mode 100644 index 00000000000..81f310494ad --- /dev/null +++ b/app/screens/channel_bookmark/index.tsx @@ -0,0 +1,323 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import React, {useCallback, useEffect, useState} from 'react'; +import {useIntl} from 'react-intl'; +import {Alert, View, type AlertButton} from 'react-native'; +import {SafeAreaView, type Edge} from 'react-native-safe-area-context'; + +import {addRecentReaction} from '@actions/local/reactions'; +import {createChannelBookmark, deleteChannelBookmark, editChannelBookmark} from '@actions/remote/channel_bookmark'; +import Button from '@components/button'; +import {useServerUrl} from '@context/server'; +import {useTheme} from '@context/theme'; +import useAndroidHardwareBackHandler from '@hooks/android_back_handler'; +import useNavButtonPressed from '@hooks/navigation_button_pressed'; +import {buildNavigationButton, dismissModal, setButtons} from '@screens/navigation'; +import {getFullErrorMessage} from '@utils/errors'; +import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme'; + +import BookmarkDetail from './components/bookmark_detail'; +import AddBookmarkFile from './components/bookmark_file'; +import BookmarkLink from './components/bookmark_link'; + +import type {AvailableScreens} from '@typings/screens/navigation'; + +type Props = { + bookmark?: ChannelBookmark; + canDeleteBookmarks?: boolean; + channelId: string; + closeButtonId: string; + componentId: AvailableScreens; + file?: FileInfo; + ownerId: string; + type: ChannelBookmarkType; +} + +const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({ + content: { + flex: 1, + paddingHorizontal: 20, + paddingBottom: 16, + }, + progress: { + alignItems: 'center', + backgroundColor: 'rgba(0, 0, 0, 0.1)', + borderRadius: 4, + paddingLeft: 3, + marginTop: 12, + }, + deleteBg: {backgroundColor: changeOpacity(theme.errorTextColor, 0.16)}, + deleteContainer: {paddingTop: 32}, + deleteText: {color: theme.errorTextColor}, +})); + +const RIGHT_BUTTON = buildNavigationButton('edit-bookmark', 'channel_bookmark.edit.save_button'); +const edges: Edge[] = ['bottom', 'left', 'right']; +const emptyBookmark: ChannelBookmark = { + id: '', + create_at: 0, + update_at: 0, + delete_at: 0, + channel_id: '', + owner_id: '', + display_name: '', + sort_order: 0, + type: 'link', +}; + +const ChannelBookmarkAddOrEdit = ({ + bookmark: original, + canDeleteBookmarks = false, + channelId, + closeButtonId, + componentId, + file: originalFile, + ownerId, + type, +}: Props) => { + const {formatMessage} = useIntl(); + const theme = useTheme(); + const serverUrl = useServerUrl(); + const styles = getStyleSheet(theme); + const [bookmark, setBookmark] = useState(original); + const [file, setFile] = useState(originalFile); + const [isSaving, setIsSaving] = useState(false); + + const enableSaveButton = useCallback((enabled: boolean) => { + setButtons(componentId, { + rightButtons: [{ + ...RIGHT_BUTTON, + color: theme.sidebarHeaderTextColor, + text: formatMessage({id: 'channel_bookmark.edit.save_button', defaultMessage: 'Save'}), + enabled, + }], + }); + }, [formatMessage, theme]); + + const setBookmarkToSave = useCallback((b?: ChannelBookmark) => { + enableSaveButton((b?.type === 'link' && Boolean(b?.link_url)) || (b?.type === 'file' && Boolean(b.file_id))); + setBookmark(b); + }, []); + + const handleError = useCallback((error: string, buttons?: AlertButton[]) => { + const title = original ? formatMessage({id: 'channel_bookmark.edit.failed_title', defaultMessage: 'Error editing bookmark'}) : formatMessage({id: 'channel_bookmark.add.failed_title', defaultMessage: 'Error adding bookmark'}); + Alert.alert( + title, + formatMessage({ + id: 'channel_bookmark.add_edit.failed_desc', + defaultMessage: 'Details: {error}', + }, {error}), + buttons, + ); + setIsSaving(false); + const enabled = Boolean(bookmark?.display_name && + ((bookmark?.type === 'link' && Boolean(bookmark?.link_url)) || (bookmark?.type === 'file' && Boolean(bookmark.file_id)))); + enableSaveButton(enabled); + }, [bookmark, enableSaveButton, formatMessage]); + + const close = useCallback(() => { + return dismissModal({componentId}); + }, [componentId]); + + const createBookmark = useCallback(async (b: ChannelBookmark) => { + const res = await createChannelBookmark(serverUrl, channelId, b); + if (res.bookmark) { + close(); + return; + } + + handleError((res.error as Error).message); + }, [channelId, handleError, serverUrl]); + + const updateBookmark = useCallback(async (b: ChannelBookmark) => { + const res = await editChannelBookmark(serverUrl, b); + if (res.bookmarks) { + close(); + return; + } + + handleError((res.error as Error).message); + }, [handleError, serverUrl]); + + const setLinkBookmark = useCallback((url: string, title: string, imageUrl: string) => { + const b: ChannelBookmark = { + ...(bookmark || emptyBookmark), + owner_id: ownerId, + channel_id: channelId, + link_url: url, + image_url: imageUrl, + display_name: title, + type: 'link', + }; + + setBookmarkToSave(b); + }, [bookmark, channelId, setBookmarkToSave, ownerId]); + + const setFileBookmark = useCallback((f: ExtractedFileInfo) => { + const b: ChannelBookmark = { + ...(bookmark || emptyBookmark), + owner_id: ownerId, + channel_id: channelId, + display_name: decodeURIComponent(f.name), + type: 'file', + file_id: f.id, + }; + setBookmarkToSave(b); + setFile(f); + }, [bookmark, channelId, ownerId]); + + const setBookmarkDisplayName = useCallback((displayName: string) => { + if (bookmark) { + setBookmark((prev) => ({ + ...(prev!), + display_name: displayName, + })); + } + + enableSaveButton(Boolean(displayName)); + }, [bookmark, enableSaveButton]); + + const setBookmarkEmoji = useCallback((emoji?: string) => { + if (bookmark) { + setBookmark((prev) => ({ + ...prev!, + emoji, + })); + + const prevEmoji = original ? original.emoji : ''; + if (prevEmoji !== emoji) { + enableSaveButton(true); + } + } + + if (emoji) { + addRecentReaction(serverUrl, [emoji]); + } + }, [bookmark, enableSaveButton, serverUrl]); + + const resetBookmark = useCallback(() => { + setBookmarkToSave(original); + setFile(originalFile); + }, [setBookmarkToSave]); + + const onSaveBookmark = useCallback(async () => { + if (bookmark) { + enableSaveButton(false); + setIsSaving(true); + if (original) { + updateBookmark(bookmark); + return; + } + + createBookmark(bookmark); + } + }, [bookmark, createBookmark, updateBookmark]); + + const handleDelete = useCallback(async () => { + if (bookmark) { + setIsSaving(true); + enableSaveButton(false); + const {error} = await deleteChannelBookmark(serverUrl, bookmark.channel_id, bookmark.id); + if (error) { + Alert.alert( + formatMessage({id: 'channel_bookmark.delete.failed_title', defaultMessage: 'Error deleting bookmark'}), + formatMessage({ + id: 'channel_bookmark.add_edit.failed_desc', + defaultMessage: 'Details: {error}', + }, {error: getFullErrorMessage(error)}), + ); + setIsSaving(false); + enableSaveButton(true); + return; + } + + close(); + } + }, [bookmark, serverUrl, close]); + + const onDelete = useCallback(async () => { + if (bookmark) { + Alert.alert( + formatMessage({id: 'channel_bookmark.delete.confirm_title', defaultMessage: 'Delete bookmark'}), + formatMessage({id: 'channel_bookmark.delete.confirm', defaultMessage: 'You sure want to delete the bookmark {displayName}?'}, { + displayName: bookmark.display_name, + }), + [{ + text: formatMessage({id: 'channel_bookmark.delete.yes', defaultMessage: 'Yes'}), + style: 'destructive', + isPreferred: true, + onPress: handleDelete, + }, { + text: formatMessage({id: 'channel_bookmark.add.file_cancel', defaultMessage: 'Cancel'}), + style: 'cancel', + }], + ); + } + }, [bookmark, handleDelete]); + + useEffect(() => { + enableSaveButton(false); + }, []); + + useNavButtonPressed(RIGHT_BUTTON.id, componentId, onSaveBookmark, [bookmark]); + useNavButtonPressed(closeButtonId, componentId, close, [close]); + useAndroidHardwareBackHandler(componentId, close); + + return ( + + {type === 'link' && + + } + {type === 'file' && + + } + {Boolean(bookmark) && + <> + + {canDeleteBookmarks && + + + + ); + } else { + content = ( + + + + + ); + } + return ( - {loginError || error ? ( - - - - {`${loginError || error}.`} - - - - ) : ( - - - - - )} + {content} ); }; diff --git a/app/utils/apps.test.ts b/app/utils/apps.test.ts index 767e5a01592..1f2fe62255e 100644 --- a/app/utils/apps.test.ts +++ b/app/utils/apps.test.ts @@ -40,9 +40,9 @@ describe('cleanBinding', () => { }; const result = cleanBinding(binding, AppBindingLocations.COMMAND); - expect(result.bindings![0].app_id).toBe('test_app'); - expect(result.bindings![0].label).toBeTruthy(); - expect(result.bindings![0].location).toMatch(/location\/.+/); + expect(result!.bindings![0].app_id).toBe('test_app'); + expect(result!.bindings![0].label).toBeTruthy(); + expect(result!.bindings![0].location).toMatch(/location\/.+/); }); it('should remove bindings without app_id', () => { @@ -70,8 +70,8 @@ describe('cleanBinding', () => { }; const result = cleanBinding(binding, AppBindingLocations.COMMAND); - expect(result.bindings).toHaveLength(1); - expect(result.bindings![0].app_id).toBe('sub_app'); + expect(result!.bindings).toHaveLength(1); + expect(result!.bindings![0].app_id).toBe('sub_app'); }); it('should remove bindings with empty or whitespace labels', () => { @@ -109,8 +109,8 @@ describe('cleanBinding', () => { }; const result = cleanBinding(binding, AppBindingLocations.COMMAND); - expect(result.bindings).toHaveLength(1); - expect(result.bindings![0].label).toBe('valid_label'); + expect(result!.bindings).toHaveLength(1); + expect(result!.bindings![0].label).toBe('valid_label'); }); it('should remove bindings with duplicate labels in COMMAND location', () => { @@ -143,8 +143,8 @@ describe('cleanBinding', () => { }; const result = cleanBinding(binding, AppBindingLocations.COMMAND); - expect(result.bindings).toHaveLength(1); - expect(result.bindings![0].app_id).toBe('sub_app1'); + expect(result!.bindings).toHaveLength(1); + expect(result!.bindings![0].app_id).toBe('sub_app1'); }); it('should keep unique labels in IN_POST location', () => { @@ -177,7 +177,7 @@ describe('cleanBinding', () => { }; const result = cleanBinding(binding, AppBindingLocations.IN_POST); - expect(result.bindings).toHaveLength(2); + expect(result!.bindings).toHaveLength(2); }); it('should remove invalid sub-bindings without form or submit', () => { @@ -213,9 +213,9 @@ describe('cleanBinding', () => { }; const result = cleanBinding(binding, AppBindingLocations.COMMAND); - expect(result.bindings).toHaveLength(2); - expect(result.bindings![0].label).toBe('sub_label2'); - expect(result.bindings![1].label).toBe('sub_label3'); + expect(result!.bindings).toHaveLength(2); + expect(result!.bindings![0].label).toBe('sub_label2'); + expect(result!.bindings![1].label).toBe('sub_label3'); }); it('should handle forms correctly', () => { @@ -238,8 +238,8 @@ describe('cleanBinding', () => { }; const result = cleanBinding(binding, AppBindingLocations.COMMAND); - expect(result.bindings).toHaveLength(1); - expect(result.bindings![0].form).toBeTruthy(); + expect(result!.bindings).toHaveLength(1); + expect(result!.bindings![0].form).toBeTruthy(); }); it('should handle submit calls correctly', () => { @@ -260,8 +260,8 @@ describe('cleanBinding', () => { }; const result = cleanBinding(binding, AppBindingLocations.COMMAND); - expect(result.bindings).toHaveLength(1); - expect(result.bindings![0].submit).toBeTruthy(); + expect(result!.bindings).toHaveLength(1); + expect(result!.bindings![0].submit).toBeTruthy(); }); it('should recursively clean sub-bindings', () => { @@ -296,9 +296,9 @@ describe('cleanBinding', () => { }; const result = cleanBinding(binding, AppBindingLocations.COMMAND); - expect(result.bindings).toHaveLength(1); - expect(result.bindings![0].bindings).toHaveLength(1); - expect(result.bindings![0].bindings![0].label).toBe('sub_sub_label'); + expect(result!.bindings).toHaveLength(1); + expect(result!.bindings![0].bindings).toHaveLength(1); + expect(result!.bindings![0].bindings![0].label).toBe('sub_sub_label'); }); it('should handle bindings without bindings, form, or submit', () => { @@ -316,7 +316,7 @@ describe('cleanBinding', () => { }; const result = cleanBinding(binding, AppBindingLocations.COMMAND); - expect(result.bindings).toHaveLength(0); + expect(result!.bindings).toHaveLength(0); }); it('should handle multiple levels of nested bindings correctly', () => { @@ -361,10 +361,10 @@ describe('cleanBinding', () => { }; const result = cleanBinding(binding, AppBindingLocations.COMMAND); - expect(result.bindings).toHaveLength(2); - expect(result.bindings![0].bindings).toHaveLength(1); - expect(result.bindings![0].bindings![0].label).toBe('sub_sub_label'); - expect(result.bindings![1].label).toBe('sub_label2'); + expect(result!.bindings).toHaveLength(2); + expect(result!.bindings![0].bindings).toHaveLength(1); + expect(result!.bindings![0].bindings![0].label).toBe('sub_sub_label'); + expect(result!.bindings![1].label).toBe('sub_label2'); }); }); diff --git a/app/utils/apps.ts b/app/utils/apps.ts index 2605efd7c5d..bd21068c690 100644 --- a/app/utils/apps.ts +++ b/app/utils/apps.ts @@ -5,13 +5,13 @@ import {AppBindingLocations, AppCallResponseTypes, AppFieldTypes} from '@constan import {generateId} from './general'; -export function cleanBinding(binding: AppBinding, topLocation: string): AppBinding { +export function cleanBinding(binding: AppBinding, topLocation: string): AppBinding|null { return cleanBindingRec(binding, topLocation, 0); } -function cleanBindingRec(binding: AppBinding, topLocation: string, depth: number): AppBinding { +function cleanBindingRec(binding: AppBinding, topLocation: string, depth: number): AppBinding|null { if (!binding) { - return binding; + return null; } const toRemove: number[] = []; diff --git a/app/utils/categories.test.ts b/app/utils/categories.test.ts index 4a6bfd1b5c0..3f109c1dd11 100644 --- a/app/utils/categories.test.ts +++ b/app/utils/categories.test.ts @@ -184,7 +184,7 @@ describe('Categories utils', () => { const channelModels = await db.operator.handleChannel({channels, prepareRecordsOnly: false}); const myChannelModels = await db.operator.handleMyChannel({channels, myChannels, prepareRecordsOnly: false}); return channelModels.map((channel, index) => { - const myChannel = myChannelModels[index]; + const myChannel = myChannelModels[index] as MyChannelModel; return { channel, myChannel, @@ -253,7 +253,7 @@ describe('Categories utils', () => { const models = await db.operator.handleMyChannel({channels, myChannels, isCRTEnabled: false, prepareRecordsOnly: false}); expect(models).toHaveLength(1); - const myChannel = models[0]; + const myChannel = models[0] as MyChannelModel; let isUnread = isUnreadChannel(myChannel); expect(isUnread).toBe(true); await db.database.write(async () => { @@ -355,11 +355,11 @@ describe('Categories utils', () => { const myChannelModels = await db.operator.handleMyChannel({channels, myChannels, prepareRecordsOnly: false}); const data: ChannelWithMyChannel[] = [{ channel: channelModels[0], - myChannel: myChannelModels[0], + myChannel: myChannelModels[0] as MyChannelModel, sortOrder: 0, }, { channel: channelModels[1], - myChannel: myChannelModels[1], + myChannel: myChannelModels[1] as MyChannelModel, sortOrder: 1, }]; let archived = filterArchivedChannels(data, ''); @@ -472,11 +472,11 @@ describe('Categories utils', () => { const myChannelModels = await db.operator.handleMyChannel({channels, myChannels, prepareRecordsOnly: false}); const data: ChannelWithMyChannel[] = [{ channel: channelModels[0], - myChannel: myChannelModels[0], + myChannel: myChannelModels[0] as MyChannelModel, sortOrder: 0, }, { channel: channelModels[1], - myChannel: myChannelModels[1], + myChannel: myChannelModels[1] as MyChannelModel, sortOrder: 0, }]; diff --git a/app/utils/deep_link/index.test.ts b/app/utils/deep_link/index.test.ts index 57539ef5f2a..ec18e833e68 100644 --- a/app/utils/deep_link/index.test.ts +++ b/app/utils/deep_link/index.test.ts @@ -2,23 +2,25 @@ // See LICENSE.txt for license information. import {createIntl} from 'react-intl'; +import {Navigation} from 'react-native-navigation'; import {makeDirectChannel, switchToChannelByName} from '@actions/remote/channel'; import {showPermalink} from '@actions/remote/permalink'; import {fetchUsersByUsernames} from '@actions/remote/user'; -import {DeepLink, Launch, Preferences} from '@constants'; +import {DeepLink, Launch, Preferences, Screens} from '@constants'; import DatabaseManager from '@database/manager'; import {t} from '@i18n'; import WebsocketManager from '@managers/websocket_manager'; import {getActiveServerUrl} from '@queries/app/servers'; import {queryUsersByUsername} from '@queries/servers/user'; import {dismissAllModalsAndPopToRoot} from '@screens/navigation'; +import NavigationStore from '@store/navigation_store'; import {logError} from '@utils/log'; import {addNewServer} from '@utils/server'; import {alertErrorWithFallback, errorBadChannel, errorUnkownUser} from '../draft'; -import {alertInvalidDeepLink, getLaunchPropsFromDeepLink, handleDeepLink} from '.'; +import {alertInvalidDeepLink, extractServerUrl, getLaunchPropsFromDeepLink, handleDeepLink} from '.'; jest.mock('@actions/remote/user', () => ({ fetchUsersByUsernames: jest.fn(), @@ -51,6 +53,7 @@ jest.mock('@store/navigation_store', () => ({ getVisibleScreen: jest.fn(() => 'HOME'), hasModalsOpened: jest.fn(() => false), waitUntilScreenHasLoaded: jest.fn(), + getScreensInStack: jest.fn().mockReturnValue([]), })); jest.mock('@utils/server', () => ({ @@ -78,6 +81,19 @@ jest.mock('@i18n', () => ({ t: jest.fn((id) => id), })); +describe('extractServerUrl', () => { + it('should extract the sanitized server url', () => { + expect(extractServerUrl('example.com:8080//path/to///login')).toEqual('example.com:8080/path/to'); + expect(extractServerUrl('localhost:3000/signup')).toEqual('localhost:3000'); + expect(extractServerUrl('192.168.0.1/admin_console')).toEqual('192.168.0.1'); + expect(extractServerUrl('example.com/path//to/resource')).toEqual('example.com/path/to/resource'); + expect(extractServerUrl('my.local.network/.../resource/admin_console')).toEqual('my.local.network/resource'); + expect(extractServerUrl('my.local.network//ad-1/channels/%252f%252e.town-square')).toEqual(null); + expect(extractServerUrl('example.com:8080')).toEqual('example.com:8080'); + expect(extractServerUrl('example.com:8080/')).toEqual('example.com:8080'); + }); +}); + describe('handleDeepLink', () => { const intl = createIntl({locale: 'en', messages: {}}); @@ -115,6 +131,28 @@ describe('handleDeepLink', () => { expect(result).toEqual({error: false}); }); + it('should update the server url in the server url screen', async () => { + (getActiveServerUrl as jest.Mock).mockResolvedValueOnce('https://currentserver.com'); + (DatabaseManager.searchUrl as jest.Mock).mockReturnValueOnce(null); + + (NavigationStore.getVisibleScreen as jest.Mock).mockReturnValueOnce(Screens.SERVER); + const result = await handleDeepLink('https://currentserver.com/team/channels/town-square', undefined, undefined, true); + const spyOnUpdateProps = jest.spyOn(Navigation, 'updateProps'); + expect(spyOnUpdateProps).toHaveBeenCalledWith(Screens.SERVER, {serverUrl: 'currentserver.com'}); + expect(result).toEqual({error: false}); + }); + + it('should not display the new server modal if the server screen is on the stack but not as the visible screen', async () => { + (getActiveServerUrl as jest.Mock).mockResolvedValueOnce('https://currentserver.com'); + (DatabaseManager.searchUrl as jest.Mock).mockReturnValueOnce(null); + + (NavigationStore.getVisibleScreen as jest.Mock).mockReturnValueOnce(Screens.LOGIN); + (NavigationStore.getScreensInStack as jest.Mock).mockReturnValueOnce([Screens.SERVER, Screens.LOGIN]); + const result = await handleDeepLink('https://currentserver.com/team/channels/town-square', undefined, undefined, true); + expect(addNewServer).not.toHaveBeenCalled(); + expect(result).toEqual({error: false}); + }); + it('should switch to channel by name for Channel deep link', async () => { (DatabaseManager.searchUrl as jest.Mock).mockReturnValueOnce('https://existingserver.com'); (getActiveServerUrl as jest.Mock).mockResolvedValueOnce('https://existingserver.com'); @@ -187,6 +225,10 @@ describe('getLaunchPropsFromDeepLink', () => { launchType: Launch.DeepLink, coldStart: false, launchError: true, + extra: { + type: DeepLink.Invalid, + url: 'invalid-url', + }, }); }); @@ -208,6 +250,23 @@ describe('getLaunchPropsFromDeepLink', () => { extra: extraData, }); }); + + it('should return launch props with extra data to add a new server when opened from cold start', () => { + const extraData = { + type: DeepLink.Server, + data: { + serverUrl: 'existingserver.com', + }, + url: 'https://existingserver.com/login', + }; + const result = getLaunchPropsFromDeepLink('https://existingserver.com/login', true); + + expect(result).toEqual({ + launchType: Launch.DeepLink, + coldStart: true, + extra: extraData, + }); + }); }); describe('alertInvalidDeepLink', () => { diff --git a/app/utils/deep_link/index.ts b/app/utils/deep_link/index.ts index 4422d7aa375..b7ae8c9a772 100644 --- a/app/utils/deep_link/index.ts +++ b/app/utils/deep_link/index.ts @@ -2,7 +2,8 @@ // See LICENSE.txt for license information. import {match} from 'path-to-regexp'; -import {createIntl, type IntlShape} from 'react-intl'; +import {type IntlShape} from 'react-intl'; +import {Navigation} from 'react-native-navigation'; import urlParse from 'url-parse'; import {makeDirectChannel, switchToChannelByName} from '@actions/remote/channel'; @@ -12,7 +13,7 @@ import DeepLinkType from '@app/constants/deep_linking'; import {DeepLink, Launch, Screens} from '@constants'; import {getDefaultThemeByAppearance} from '@context/theme'; import DatabaseManager from '@database/manager'; -import {DEFAULT_LOCALE, getTranslations, t} from '@i18n'; +import {DEFAULT_LOCALE, t} from '@i18n'; import WebsocketManager from '@managers/websocket_manager'; import {getActiveServerUrl} from '@queries/app/servers'; import {getCurrentUser, queryUsersByUsername} from '@queries/servers/user'; @@ -20,6 +21,7 @@ import {dismissAllModalsAndPopToRoot} from '@screens/navigation'; import EphemeralStore from '@store/ephemeral_store'; import NavigationStore from '@store/navigation_store'; import {alertErrorWithFallback, errorBadChannel, errorUnkownUser} from '@utils/draft'; +import {getIntlShape} from '@utils/general'; import {logError} from '@utils/log'; import {escapeRegex} from '@utils/markdown'; import {addNewServer} from '@utils/server'; @@ -36,9 +38,9 @@ import type {AvailableScreens} from '@typings/screens/navigation'; const deepLinkScreens: AvailableScreens[] = [Screens.HOME, Screens.CHANNEL, Screens.GLOBAL_THREADS, Screens.THREAD]; -export async function handleDeepLink(deepLinkUrl: string, intlShape?: IntlShape, location?: string) { +export async function handleDeepLink(deepLinkUrl: string, intlShape?: IntlShape, location?: string, asServer = false) { try { - const parsed = parseDeepLink(deepLinkUrl); + const parsed = parseDeepLink(deepLinkUrl, asServer); if (parsed.type === DeepLink.Invalid || !parsed.data || !parsed.data.serverUrl) { return {error: true}; } @@ -49,7 +51,11 @@ export async function handleDeepLink(deepLinkUrl: string, intlShape?: IntlShape, // After checking the server for http & https then we add it if (!existingServerUrl) { const theme = EphemeralStore.theme || getDefaultThemeByAppearance(); - addNewServer(theme, parsed.data.serverUrl, undefined, parsed); + if (NavigationStore.getVisibleScreen() === Screens.SERVER) { + Navigation.updateProps(Screens.SERVER, {serverUrl: parsed.data.serverUrl}); + } else if (!NavigationStore.getScreensInStack().includes(Screens.SERVER)) { + addNewServer(theme, parsed.data.serverUrl, undefined, parsed); + } return {error: false}; } @@ -63,10 +69,7 @@ export async function handleDeepLink(deepLinkUrl: string, intlShape?: IntlShape, const {database} = DatabaseManager.getServerDatabaseAndOperator(existingServerUrl); const currentUser = await getCurrentUser(database); const locale = currentUser?.locale || DEFAULT_LOCALE; - const intl = intlShape || createIntl({ - locale, - messages: getTranslations(locale), - }); + const intl = intlShape || getIntlShape(locale); switch (parsed.type) { case DeepLink.Channel: { @@ -135,7 +138,49 @@ type PermalinkPathParams = { const PERMALINK_PATH = `:serverUrl(.*)/:teamName(${TEAM_NAME_PATH_PATTERN})/pl/:postId(${ID_PATH_PATTERN})`; export const matchPermalinkDeeplink = match(PERMALINK_PATH); -export function parseDeepLink(deepLinkUrl: string): DeepLinkWithData { +type ServerPathParams = { + serverUrl: string; + path: string; +} + +export const matchServerDeepLink = match(':serverUrl(.*)/:path(.*)', {decode: decodeURIComponent}); +const reservedWords = ['login', 'signup', 'admin_console']; + +export function extractServerUrl(url: string) { + const deepLinkUrl = decodeURIComponent(url).replace(/\.{2,}/g, '').replace(/\/+/g, '/'); + + const pattern = new RegExp( + + // Match the domain, IP address, or localhost + '^([a-zA-Z0-9.-]+|localhost|\\d{1,3}(?:\\.\\d{1,3}){3})' + + + // Match optional port + '(?::(\\d+))?' + + + // Match path segments + '(?:/([a-zA-Z0-9-/_]+))?/?$', + ); + + if (!pattern.test(deepLinkUrl)) { + return null; + } + + const matched = matchServerDeepLink(deepLinkUrl); + + if (matched) { + const {path} = matched.params; + const segments = path.split('/'); + + if (segments.length > 0 && reservedWords.includes(segments[segments.length - 1])) { + return matched.params.serverUrl; + } + return path ? `${matched.params.serverUrl}/${path}` : matched.params.serverUrl; + } + + return deepLinkUrl; +} + +export function parseDeepLink(deepLinkUrl: string, asServer = false): DeepLinkWithData { try { const url = removeProtocol(deepLinkUrl); @@ -161,6 +206,13 @@ export function parseDeepLink(deepLinkUrl: string): DeepLinkWithData { const {params: {serverUrl, teamName, postId}} = permalinkMatch; return {type: DeepLink.Permalink, url: deepLinkUrl, data: {serverUrl, teamName, postId}}; } + + if (asServer) { + const serverMatch = extractServerUrl(url); + if (serverMatch) { + return {type: DeepLink.Server, url: deepLinkUrl, data: {serverUrl: serverMatch}}; + } + } } catch (err) { // do nothing just return invalid deeplink } @@ -196,7 +248,7 @@ export function matchDeepLink(url: string, serverURL?: string, siteURL?: string) } export const getLaunchPropsFromDeepLink = (deepLinkUrl: string, coldStart = false): LaunchProps => { - const parsed = parseDeepLink(deepLinkUrl); + const parsed = parseDeepLink(deepLinkUrl, coldStart); const launchProps: LaunchProps = { launchType: Launch.DeepLink, coldStart, @@ -205,6 +257,7 @@ export const getLaunchPropsFromDeepLink = (deepLinkUrl: string, coldStart = fals switch (parsed.type) { case DeepLink.Invalid: launchProps.launchError = true; + launchProps.extra = parsed; break; default: { launchProps.extra = parsed; diff --git a/app/utils/errors.test.ts b/app/utils/errors.test.ts index 33ea2075cd2..5ca5b8675d2 100644 --- a/app/utils/errors.test.ts +++ b/app/utils/errors.test.ts @@ -1,10 +1,6 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {createIntl} from 'react-intl'; - -import {DEFAULT_LOCALE, getTranslations} from '@i18n'; - import { isServerError, isErrorWithMessage, @@ -14,6 +10,7 @@ import { isErrorWithUrl, getFullErrorMessage, } from './errors'; +import {getIntlShape} from './general'; describe('Errors', () => { test('isServerError', () => { @@ -53,8 +50,7 @@ describe('Errors', () => { }); test('getFullErrorMessage', () => { - const locale = DEFAULT_LOCALE; - const intl = createIntl({locale, messages: getTranslations(locale)}); + const intl = getIntlShape(); expect(getFullErrorMessage('error', intl)).toBe('error'); expect(getFullErrorMessage({details: 'more info', message: 'error message'}, intl)).toBe('error message; more info'); expect(getFullErrorMessage({details: 'more info', message: 'error message'}, intl, 3)).toBe('error message; error message'); diff --git a/app/utils/file/file_picker/index.test.ts b/app/utils/file/file_picker/index.test.ts new file mode 100644 index 00000000000..9887f7447b9 --- /dev/null +++ b/app/utils/file/file_picker/index.test.ts @@ -0,0 +1,711 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +/* eslint-disable max-lines */ + +import RNUtils from '@mattermost/rnutils'; +import {applicationName} from 'expo-application'; +import {Alert, Platform} from 'react-native'; +import DocumentPicker, {type DocumentPickerResponse} from 'react-native-document-picker'; +import {launchCamera, launchImageLibrary, type Asset, type ImagePickerResponse} from 'react-native-image-picker'; +import Permissions from 'react-native-permissions'; + +import {dismissBottomSheet} from '@screens/navigation'; +import TestHelper from '@test/test_helper'; +import {extractFileInfo, lookupMimeType} from '@utils/file'; +import {getIntlShape} from '@utils/general'; +import {logWarning} from '@utils/log'; + +import FilePickerUtil from '.'; + +jest.mock('expo-file-system'); +jest.mock('react-native-image-picker'); + +jest.mock('react-native-document-picker', () => ({ + pick: jest.fn(async () => []), +})); + +jest.mock('@screens/navigation', () => ({ + dismissBottomSheet: jest.fn(), +})); + +jest.mock('@utils/file', () => ({ + extractFileInfo: jest.fn(), + lookupMimeType: jest.fn(), +})); + +jest.mock('@utils/log', () => ({ + logWarning: jest.fn(), +})); + +jest.mock('@mattermost/rnutils', () => ({ + getRealFilePath: jest.fn(), + isRunningInSplitView: jest.fn().mockReturnValue({isSplit: false, isTablet: false}), +})); + +describe('FilePickerUtil', () => { + const mockUploadFiles = jest.fn(); + const intl = getIntlShape(); + const originalSelect = Platform.select; + + let filePickerUtil: FilePickerUtil; + + beforeAll(() => { + Platform.select = ({android, ios, default: dft}: any) => { + if (Platform.OS === 'android' && android) { + return android; + } else if (Platform.OS === 'ios' && ios) { + return ios; + } + + return dft; + }; + }); + + afterAll(() => { + Platform.select = originalSelect; + }); + + beforeEach(() => { + Platform.OS = 'ios'; + filePickerUtil = new FilePickerUtil(intl, mockUploadFiles); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + describe('private functions', () => { + test('should assign intl and uploadFiles correctly in the constructor', () => { + // @ts-expect-error intl is private + expect(filePickerUtil.intl).toBe(intl); + + // @ts-expect-error uploadFiles is private + expect(filePickerUtil.uploadFiles).toBe(mockUploadFiles); + }); + + test('should return correct permission messages for camera', () => { + const camera = { + title: `${applicationName} would like to access your camera`, + text: `Take photos and upload them to your server or save them to your device. Open Settings to grant ${applicationName} read and write access to your camera.`, + }; + + // @ts-expect-error getPermissionMessages is private + const result = filePickerUtil.getPermissionMessages('camera'); + expect(result).toEqual(camera); + }); + + test('should return correct permission messages for storage', () => { + const expected = { + title: `${applicationName} would like to access your files`, + text: `Upload files to your server. Open Settings to grant ${applicationName} Read and Write access to files on this device.`, + }; + + // @ts-expect-error getPermissionMessages is private + const result = filePickerUtil.getPermissionMessages('storage'); + expect(result).toEqual(expected); + }); + + test('should return correct permission messages for photo_ios', () => { + const expected = { + title: `${applicationName} would like to access your photos`, + text: `Upload photos and videos to your server or save them to your device. Open Settings to grant ${applicationName} Read and Write access to your photo and video library.`, + }; + + // @ts-expect-error getPermissionMessages is private + const result = filePickerUtil.getPermissionMessages('photo_ios'); + expect(result).toEqual(expected); + }); + + test('should return correct permission messages for photo_android', () => { + const expected = { + title: `${applicationName} would like to access your photos`, + text: `Upload photos to your server or save them to your device. Open Settings to grant ${applicationName} Read and Write access to your photo library.`, + }; + + // @ts-expect-error getPermissionMessages is private + const result = filePickerUtil.getPermissionMessages('photo_android'); + expect(result).toEqual(expected); + }); + + test('should prepare file upload correctly', async () => { + const mockFiles = [{uri: 'file://test'}] as Array; + const mockExtractedFiles = [{uri: 'file://test', name: 'test'}]; + + (extractFileInfo as jest.Mock).mockResolvedValue(mockExtractedFiles); + + // @ts-expect-error prepareFileUpload is private + await filePickerUtil.prepareFileUpload(mockFiles); + + expect(extractFileInfo).toHaveBeenCalledWith(mockFiles); + expect(dismissBottomSheet).toHaveBeenCalled(); + expect(mockUploadFiles).toHaveBeenCalledWith(mockExtractedFiles); + }); + + test('should not upload files if extraction returns an empty array', async () => { + const mockFiles = [{uri: 'file://test'}] as Array; + + (extractFileInfo as jest.Mock).mockResolvedValue([]); + + // @ts-expect-error prepareFileUpload is private + await filePickerUtil.prepareFileUpload(mockFiles); + + expect(extractFileInfo).toHaveBeenCalledWith(mockFiles); + expect(dismissBottomSheet).not.toHaveBeenCalled(); + expect(mockUploadFiles).not.toHaveBeenCalled(); + }); + + test('should return correct permission denied message with source', () => { + const expected = { + title: `${applicationName} would like to access your files`, + text: `Upload files to your server. Open Settings to grant ${applicationName} Read and Write access to files on this device.`, + }; + + // @ts-expect-error getPermissionDeniedMessage is private + const result = filePickerUtil.getPermissionDeniedMessage('storage'); + expect(result).toEqual(expected); + }); + + test('should return correct permission denied message without source', () => { + Platform.OS = 'android'; + const expected = { + title: `${applicationName} would like to access your photos`, + text: `Upload photos to your server or save them to your device. Open Settings to grant ${applicationName} Read and Write access to your photo library.`, + }; + + // @ts-expect-error getPermissionDeniedMessage is private + const result = filePickerUtil.getPermissionDeniedMessage(); + expect(result).toEqual(expected); + }); + + test('should return files from response correctly on iOS', async () => { + const mockResponse = { + assets: [{uri: 'file://test', type: 'image/jpeg'}], + } as ImagePickerResponse; + + // @ts-expect-error getFilesFromResponse is private + const result = await filePickerUtil.getFilesFromResponse(mockResponse); + + expect(result).toEqual(mockResponse.assets); + expect(logWarning).not.toHaveBeenCalled(); + }); + + test('should return files from response correctly on Android', async () => { + Platform.OS = 'android'; + const mockResponse = { + assets: [{uri: 'file://test', type: 'image/jpeg', fileName: 'test.jpg'}], + } as ImagePickerResponse; + + (RNUtils.getRealFilePath as jest.Mock).mockResolvedValue('file://real_path'); + (lookupMimeType as jest.Mock).mockReturnValue('image/jpeg'); + + // @ts-expect-error getFilesFromResponse is private + const result = await filePickerUtil.getFilesFromResponse(mockResponse); + + expect(result).toEqual([{...mockResponse.assets![0], uri: 'file://real_path'}]); + expect(RNUtils.getRealFilePath).toHaveBeenCalledWith('file://test'); + }); + + test('should log warning if no assets in response', async () => { + const mockResponse = {} as ImagePickerResponse; + + // @ts-expect-error getFilesFromResponse is private + const result = await filePickerUtil.getFilesFromResponse(mockResponse); + + expect(result).toEqual([]); + expect(logWarning).toHaveBeenCalledWith('no assets in response'); + }); + + test('should log warning if attaching file returns empty uri on Android', async () => { + Platform.OS = 'android'; + const mockResponse = { + assets: [{uri: 'file://test', type: 'image/jpeg', fileName: 'test.jpg'}], + } as ImagePickerResponse; + + (RNUtils.getRealFilePath as jest.Mock).mockResolvedValue(''); + + // @ts-expect-error getFilesFromResponse is private + const result = await filePickerUtil.getFilesFromResponse(mockResponse); + + expect(result).toEqual([]); + expect(logWarning).toHaveBeenCalledWith('attaching file reponse return empty uri', mockResponse.assets![0]); + }); + + test('should check and request photo permission correctly', async () => { + Platform.OS = 'ios'; + const source = 'camera'; + (Permissions.check as jest.Mock).mockResolvedValue(Permissions.RESULTS.DENIED); + (Permissions.request as jest.Mock).mockResolvedValue(Permissions.RESULTS.GRANTED); + + // @ts-expect-error hasPhotoPermission is private + const result = await filePickerUtil.hasPhotoPermission(source); + + expect(Permissions.check).toHaveBeenCalledWith(Permissions.PERMISSIONS.IOS.CAMERA); + expect(Permissions.request).toHaveBeenCalledWith(Permissions.PERMISSIONS.IOS.CAMERA); + expect(result).toBe(true); + }); + + test('should handle blocked photo permission correctly on iOS', async () => { + const source = 'camera'; + (Permissions.check as jest.Mock).mockResolvedValue(Permissions.RESULTS.BLOCKED); + + const alertSpy = jest.spyOn(Alert, 'alert'); + + // @ts-expect-error hasPhotoPermission is private + const result = await filePickerUtil.hasPhotoPermission(source); + + expect(Permissions.check).toHaveBeenCalledWith(Permissions.PERMISSIONS.IOS.CAMERA); + expect(alertSpy).toHaveBeenCalled(); + expect(result).toBe(false); + }); + + test('should check and request storage permission correctly on Android', async () => { + Platform.OS = 'android'; + Platform.Version = 31; + const storagePermission = Permissions.PERMISSIONS.ANDROID.READ_EXTERNAL_STORAGE; + (Permissions.check as jest.Mock).mockResolvedValue(Permissions.RESULTS.DENIED); + (Permissions.request as jest.Mock).mockResolvedValue(Permissions.RESULTS.GRANTED); + + // @ts-expect-error hasStoragePermission is private + const result = await filePickerUtil.hasStoragePermission(); + + expect(Permissions.check).toHaveBeenCalledWith(storagePermission); + expect(Permissions.request).toHaveBeenCalledWith(storagePermission); + expect(result).toBe(true); + }); + + test('should handle blocked storage permission correctly on Android', async () => { + Platform.OS = 'android'; + Platform.Version = 31; + const storagePermission = Permissions.PERMISSIONS.ANDROID.READ_EXTERNAL_STORAGE; + (Permissions.check as jest.Mock).mockResolvedValue(Permissions.RESULTS.BLOCKED); + + const alertSpy = jest.spyOn(Alert, 'alert'); + + // @ts-expect-error hasStoragePermission is private + const result = await filePickerUtil.hasStoragePermission(); + + expect(Permissions.check).toHaveBeenCalledWith(storagePermission); + expect(alertSpy).toHaveBeenCalled(); + expect(result).toBe(false); + }); + + test('should check and request write storage permission correctly on Android', async () => { + Platform.OS = 'android'; + Platform.Version = 28; + const storagePermission = Permissions.PERMISSIONS.ANDROID.WRITE_EXTERNAL_STORAGE; + (Permissions.check as jest.Mock).mockResolvedValue(Permissions.RESULTS.DENIED); + (Permissions.request as jest.Mock).mockResolvedValue(Permissions.RESULTS.GRANTED); + + // @ts-expect-error hasWriteStoragePermission is private + const result = await filePickerUtil.hasWriteStoragePermission(); + + expect(Permissions.check).toHaveBeenCalledWith(storagePermission); + expect(Permissions.request).toHaveBeenCalledWith(storagePermission); + expect(result).toBe(true); + }); + + test('should handle blocked write storage permission correctly on Android', async () => { + Platform.OS = 'android'; + Platform.Version = 28; + const storagePermission = Permissions.PERMISSIONS.ANDROID.WRITE_EXTERNAL_STORAGE; + (Permissions.check as jest.Mock).mockResolvedValue(Permissions.RESULTS.BLOCKED); + + const alertSpy = jest.spyOn(Alert, 'alert'); + + // @ts-expect-error hasWriteStoragePermission is private + const result = await filePickerUtil.hasWriteStoragePermission(); + + expect(Permissions.check).toHaveBeenCalledWith(storagePermission); + expect(alertSpy).toHaveBeenCalled(); + expect(result).toBe(false); + }); + + test('should build URI correctly on Android', async () => { + Platform.OS = 'android'; + const doc = {uri: 'file://test', fileCopyUri: 'file://copy_test'} as DocumentPickerResponse; + + // @ts-expect-error buildUri is private + const result = await filePickerUtil.buildUri(doc); + + expect(result).toEqual({doc: {uri: 'file://copy_test', fileCopyUri: 'file://copy_test'}}); + }); + + test('should handle undefined new URI on Android', async () => { + Platform.OS = 'android'; + const doc = {uri: 'file://test'} as DocumentPickerResponse; + + (RNUtils.getRealFilePath as jest.Mock).mockResolvedValue(null); + + // @ts-expect-error buildUri is private + const result = await filePickerUtil.buildUri(doc); + + expect(result).toEqual({doc: undefined}); + }); + }); + + describe('attachFileFromCamera', () => { + test('should build URI correctly when fileCopyUri is undefined on Android', async () => { + Platform.OS = 'android'; + const doc = {uri: 'file://test'} as DocumentPickerResponse; + + (RNUtils.getRealFilePath as jest.Mock).mockResolvedValue('file://real_path'); + + // @ts-expect-error buildUri is private + const result = await filePickerUtil.buildUri(doc); + + expect(result).toEqual({doc: {uri: 'file://real_path'}}); + }); + + test('should not launch camera if permission is denied', async () => { + (Permissions.check as jest.Mock).mockResolvedValue(Permissions.RESULTS.DENIED); + (Permissions.request as jest.Mock).mockResolvedValue(Permissions.RESULTS.DENIED); + + const launchCameraMock = launchCamera as jest.Mock; + + await filePickerUtil.attachFileFromCamera(); + + expect(Permissions.check).toHaveBeenCalledWith(expect.any(String)); + expect(launchCameraMock).not.toHaveBeenCalled(); + }); + + test('should launch camera if permission is granted', async () => { + (Permissions.check as jest.Mock).mockResolvedValue(Permissions.RESULTS.GRANTED); + + const launchCameraMock = launchCamera as jest.Mock; + launchCameraMock.mockImplementation((options, callback) => { + callback({assets: [{uri: 'file://camera_photo.jpg', type: 'image/jpeg', fileName: 'photo.jpg'}]}); + }); + + const mockExtractedFiles = [{fileName: 'photo.jpg', type: 'image/jpeg', uri: 'file://real_camera_photo.jpg'}]; + + (extractFileInfo as jest.Mock).mockResolvedValue(mockExtractedFiles); + + await filePickerUtil.attachFileFromCamera(); + + expect(launchCameraMock).toHaveBeenCalled(); + await TestHelper.wait(100); + expect(mockUploadFiles).toHaveBeenCalledWith([{ + uri: 'file://real_camera_photo.jpg', + type: 'image/jpeg', + fileName: 'photo.jpg', + }]); + }); + + test('should handle cancelled camera response', async () => { + (Permissions.check as jest.Mock).mockResolvedValue(Permissions.RESULTS.GRANTED); + + const launchCameraMock = launchCamera as jest.Mock; + launchCameraMock.mockImplementation((options, callback) => { + callback({didCancel: true}); + }); + + await filePickerUtil.attachFileFromCamera(); + + expect(launchCameraMock).toHaveBeenCalled(); + expect(mockUploadFiles).not.toHaveBeenCalled(); + }); + + test('should handle camera error response', async () => { + (Permissions.check as jest.Mock).mockResolvedValue(Permissions.RESULTS.GRANTED); + + const launchCameraMock = launchCamera as jest.Mock; + launchCameraMock.mockImplementation((options, callback) => { + callback({errorCode: 'camera_unavailable'}); + }); + + await filePickerUtil.attachFileFromCamera(); + + expect(launchCameraMock).toHaveBeenCalled(); + expect(mockUploadFiles).not.toHaveBeenCalled(); + }); + + test('should request write storage permission for Android API <= 28', async () => { + Platform.OS = 'android'; + Platform.Version = 28; + + (Permissions.check as jest.Mock).mockImplementation((permission) => { + if (permission === Permissions.PERMISSIONS.ANDROID.CAMERA) { + return Permissions.RESULTS.GRANTED; + } + return Permissions.RESULTS.DENIED; + }); + + (Permissions.request as jest.Mock).mockResolvedValue(Permissions.RESULTS.GRANTED); + + const mockExtractedFiles = [{fileName: 'photo.jpg', type: 'image/jpeg', uri: 'file://real_camera_photo.jpg'}]; + + (extractFileInfo as jest.Mock).mockResolvedValue(mockExtractedFiles); + + const launchCameraMock = launchCamera as jest.Mock; + launchCameraMock.mockImplementation((options, callback) => { + callback({assets: [{uri: 'file://camera_photo.jpg', type: 'image/jpeg', fileName: 'photo.jpg'}]}); + }); + + await filePickerUtil.attachFileFromCamera(); + + expect(Permissions.check).toHaveBeenCalledWith(Permissions.PERMISSIONS.ANDROID.WRITE_EXTERNAL_STORAGE); + expect(launchCameraMock).toHaveBeenCalled(); + await TestHelper.wait(100); + expect(mockUploadFiles).toHaveBeenCalledWith([{ + uri: 'file://real_camera_photo.jpg', + type: 'image/jpeg', + fileName: 'photo.jpg', + }]); + }); + + test('should not launch camera if write storage permission is denied on Android API <= 28', async () => { + Platform.OS = 'android'; + Platform.Version = 28; + + (Permissions.check as jest.Mock).mockImplementation((permission) => { + if (permission === Permissions.PERMISSIONS.ANDROID.CAMERA) { + return Permissions.RESULTS.GRANTED; + } + return Permissions.RESULTS.DENIED; + }); + + (Permissions.request as jest.Mock).mockResolvedValue(Permissions.RESULTS.DENIED); + + const launchCameraMock = launchCamera as jest.Mock; + + await filePickerUtil.attachFileFromCamera(); + + expect(Permissions.check).toHaveBeenCalledWith(Permissions.PERMISSIONS.ANDROID.WRITE_EXTERNAL_STORAGE); + expect(launchCameraMock).not.toHaveBeenCalled(); + }); + }); + + describe('attachFileFromFiles', () => { + test('should not pick files if permission is denied', async () => { + Platform.OS = 'android'; + (Permissions.check as jest.Mock).mockResolvedValue(Permissions.RESULTS.DENIED); + (Permissions.request as jest.Mock).mockResolvedValue(Permissions.RESULTS.DENIED); + + const result = await filePickerUtil.attachFileFromFiles(); + + expect(DocumentPicker.pick).not.toHaveBeenCalled(); + expect(result).toEqual({error: 'no permission'}); + }); + + test('should pick files if permission is granted', async () => { + (Permissions.check as jest.Mock).mockResolvedValue(Permissions.RESULTS.GRANTED); + + const docResponse: DocumentPickerResponse[] = [ + { + uri: 'file://document.pdf', + fileCopyUri: 'file://copy_document.pdf', + name: 'document', + type: 'pdf', + size: 10, + }, + ]; + + (DocumentPicker.pick as jest.Mock).mockResolvedValue(docResponse); + + const mockExtractedFiles = [{uri: 'file://real_document.pdf'}]; + + (extractFileInfo as jest.Mock).mockResolvedValue(mockExtractedFiles); + + const result = await filePickerUtil.attachFileFromFiles(); + + expect(DocumentPicker.pick).toHaveBeenCalledWith({ + allowMultiSelection: false, + type: ['public.item'], + copyTo: 'cachesDirectory', + }); + + await TestHelper.wait(100); + expect(mockUploadFiles).toHaveBeenCalledWith([ + {uri: 'file://real_document.pdf'}, + ]); + + expect(result).toEqual({error: undefined}); + }); + + test('should return error on DocumentPicker failure', async () => { + (Permissions.check as jest.Mock).mockResolvedValue(Permissions.RESULTS.GRANTED); + const pickerError = new Error('Picker failed'); + + (DocumentPicker.pick as jest.Mock).mockRejectedValue(pickerError); + + const result = await filePickerUtil.attachFileFromFiles(); + + expect(DocumentPicker.pick).toHaveBeenCalled(); + expect(result).toEqual({error: pickerError}); + }); + + test('should allow multi-selection when specified', async () => { + (Permissions.check as jest.Mock).mockResolvedValue(Permissions.RESULTS.GRANTED); + + const mockExtractedFiles = [ + {uri: 'file://real_document1.pdf'}, + {uri: 'file://real_document2.pdf'}, + ]; + + (extractFileInfo as jest.Mock).mockResolvedValue(mockExtractedFiles); + + const docResponse = [ + {uri: 'file://document1.pdf'}, + {uri: 'file://document2.pdf'}, + ]; + + (DocumentPicker.pick as jest.Mock).mockResolvedValue(docResponse); + + const result = await filePickerUtil.attachFileFromFiles(undefined, true); + + expect(DocumentPicker.pick).toHaveBeenCalledWith({ + allowMultiSelection: true, + type: ['public.item'], + copyTo: 'cachesDirectory', + }); + + expect(mockUploadFiles).toHaveBeenCalledWith([ + {uri: 'file://real_document1.pdf'}, + {uri: 'file://real_document2.pdf'}, + ]); + + expect(result).toEqual({error: undefined}); + }); + + test('should use specified file type', async () => { + (Permissions.check as jest.Mock).mockResolvedValue(Permissions.RESULTS.GRANTED); + + const mockExtractedFiles = [{uri: 'file://real_image.jpg'}]; + + (extractFileInfo as jest.Mock).mockResolvedValue(mockExtractedFiles); + + const docResponse = [{uri: 'file://image.jpg', fileCopyUri: 'file://copy_image.jpg'}]; + + (DocumentPicker.pick as jest.Mock).mockResolvedValue(docResponse); + + const result = await filePickerUtil.attachFileFromFiles('image/*'); + + expect(DocumentPicker.pick).toHaveBeenCalledWith({ + allowMultiSelection: false, + type: ['image/*'], + copyTo: 'cachesDirectory', + }); + + expect(mockUploadFiles).toHaveBeenCalledWith([ + {uri: 'file://real_image.jpg'}, + ]); + + expect(result).toEqual({error: undefined}); + }); + + test('should default to Android file type if specified', async () => { + Platform.OS = 'android'; + (Permissions.check as jest.Mock).mockResolvedValue(Permissions.RESULTS.GRANTED); + + const docResponse = [{uri: 'file://document.pdf'}]; + + const mockExtractedFiles = [{uri: 'file://real_document.pdf'}]; + + (extractFileInfo as jest.Mock).mockResolvedValue(mockExtractedFiles); + + (DocumentPicker.pick as jest.Mock).mockResolvedValue(docResponse); + + const result = await filePickerUtil.attachFileFromFiles(); + + expect(DocumentPicker.pick).toHaveBeenCalledWith({ + allowMultiSelection: false, + type: ['*/*'], + copyTo: 'cachesDirectory', + }); + + expect(mockUploadFiles).toHaveBeenCalledWith([ + {uri: 'file://real_document.pdf'}, + ]); + + expect(result).toEqual({error: undefined}); + }); + }); + + describe('attachFileFromPhotoGallery', () => { + test('should not open the image library if permission is denied', async () => { + (Permissions.check as jest.Mock).mockResolvedValue(Permissions.RESULTS.DENIED); + (Permissions.request as jest.Mock).mockResolvedValue(Permissions.RESULTS.DENIED); + + await filePickerUtil.attachFileFromPhotoGallery(); + + expect(launchImageLibrary).not.toHaveBeenCalled(); + }); + + test('should open the image library if permission is granted', async () => { + (Permissions.check as jest.Mock).mockResolvedValue(Permissions.RESULTS.GRANTED); + + const response: ImagePickerResponse = { + assets: [{uri: 'file://image1.jpg'}], + }; + + (launchImageLibrary as jest.Mock).mockImplementation((options, callback) => callback(response)); + + const mockExtractedFiles = [{uri: 'file://real_image1.jpg'}]; + + (extractFileInfo as jest.Mock).mockResolvedValue(mockExtractedFiles); + + await filePickerUtil.attachFileFromPhotoGallery(); + + expect(launchImageLibrary).toHaveBeenCalledWith({ + quality: 1, + mediaType: 'mixed', + includeBase64: false, + selectionLimit: 1, + }, expect.any(Function)); + + await TestHelper.wait(100); + expect(mockUploadFiles).toHaveBeenCalledWith([ + {uri: 'file://real_image1.jpg'}, + ]); + }); + + test('should handle error or cancellation in image library response', async () => { + (Permissions.check as jest.Mock).mockResolvedValue(Permissions.RESULTS.GRANTED); + + const response: ImagePickerResponse = { + errorMessage: 'An error occurred', + didCancel: false, + }; + + (launchImageLibrary as jest.Mock).mockImplementation((options, callback) => callback(response)); + + await filePickerUtil.attachFileFromPhotoGallery(); + + expect(logWarning).toHaveBeenCalledWith('Attach failed', 'An error occurred'); + + const cancelledResponse: ImagePickerResponse = { + errorMessage: '', + didCancel: true, + }; + + (launchImageLibrary as jest.Mock).mockImplementation((options, callback) => callback(cancelledResponse)); + + await filePickerUtil.attachFileFromPhotoGallery(); + + expect(logWarning).toHaveBeenCalledWith('Attach failed', 'cancelled'); + }); + + test('should handle selection limit parameter', async () => { + (Permissions.check as jest.Mock).mockResolvedValue(Permissions.RESULTS.GRANTED); + + const response: ImagePickerResponse = { + assets: [{uri: 'file://image1.jpg'}, {uri: 'file://image2.jpg'}], + }; + + (launchImageLibrary as jest.Mock).mockImplementation((options, callback) => callback(response)); + const mockExtractedFiles = [{uri: 'file://real_image1.jpg'}, {uri: 'file://real_image2.jpg'}]; + + (extractFileInfo as jest.Mock).mockResolvedValue(mockExtractedFiles); + + await filePickerUtil.attachFileFromPhotoGallery(2); + + expect(launchImageLibrary).toHaveBeenCalledWith({ + quality: 1, + mediaType: 'mixed', + includeBase64: false, + selectionLimit: 2, + }, expect.any(Function)); + }); + }); +}); diff --git a/app/utils/file/file_picker/index.ts b/app/utils/file/file_picker/index.ts index 264b2f97505..3daec52fed3 100644 --- a/app/utils/file/file_picker/index.ts +++ b/app/utils/file/file_picker/index.ts @@ -255,7 +255,7 @@ export default class FilePickerUtil { }; private buildUri = async (doc: DocumentPickerResponse) => { - let uri: string = doc.uri; + let uri: string = doc.fileCopyUri || doc.uri; if (Platform.OS === 'android') { if (doc.fileCopyUri) { @@ -266,11 +266,12 @@ export default class FilePickerUtil { if (newUri == null) { return {doc: undefined}; } - } - doc.uri = uri; + uri = newUri; + } } + doc.uri = uri; return {doc}; }; @@ -323,10 +324,13 @@ export default class FilePickerUtil { ); await this.prepareFileUpload(docs); + return {error: undefined}; } catch (error) { - // Do nothing + return {error}; } } + + return {error: 'no permission'}; }; attachFileFromPhotoGallery = async (selectionLimit = 1) => { diff --git a/app/utils/file/index.test.ts b/app/utils/file/index.test.ts new file mode 100644 index 00000000000..2112c089714 --- /dev/null +++ b/app/utils/file/index.test.ts @@ -0,0 +1,276 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import {getInfoAsync, deleteAsync} from 'expo-file-system'; +import {Platform} from 'react-native'; +import Permissions from 'react-native-permissions'; + +import {getIntlShape} from '@utils/general'; +import {logError} from '@utils/log'; +import {urlSafeBase64Encode} from '@utils/security'; + +import {deleteFileCache, deleteFileCacheByDir, deleteV1Data, extractFileInfo, fileExists, fileMaxWarning, fileSizeWarning, filterFileExtensions, getAllFilesInCachesDirectory, getAllowedServerMaxFileSize, getExtensionFromContentDisposition, getExtensionFromMime, getFileType, getFormattedFileSize, getLocalFilePathFromFile, hasWriteStoragePermission, isDocument, isGif, isImage, isVideo, lookupMimeType, uploadDisabledWarning} from '.'; + +jest.mock('expo-file-system'); +jest.mock('react-native', () => { + const RN = jest.requireActual('react-native'); + return { + Platform: { + ...RN.Platform, + OS: 'ios', + }, + Alert: {alert: jest.fn()}, + Linking: {openSettings: jest.fn()}, + NativeModules: { + ...RN.NativeModules, + RNUtils: { + getConstants: () => ({ + appGroupIdentifier: 'group.mattermost.rnbeta', + appGroupSharedDirectory: { + sharedDirectory: '', + databasePath: '', + }, + }), + addListener: jest.fn(), + removeListeners: jest.fn(), + isRunningInSplitView: jest.fn().mockReturnValue({isSplit: false, isTablet: false}), + + getDeliveredNotifications: jest.fn().mockResolvedValue([]), + removeChannelNotifications: jest.fn().mockImplementation(), + removeThreadNotifications: jest.fn().mockImplementation(), + removeServerNotifications: jest.fn().mockImplementation(), + }, + }, + }; +}); +jest.mock('react-native-permissions', () => ({ + check: jest.fn(), + request: jest.fn(), + RESULTS: { + GRANTED: 'granted', + DENIED: 'denied', + BLOCKED: 'blocked', + }, + PERMISSIONS: {ANDROID: {WRITE_EXTERNAL_STORAGE: 'WRITE_EXTERNAL_STORAGE'}}, +})); +jest.mock('@utils/log', () => ({logError: jest.fn()})); +jest.mock('@utils/mattermost_managed', () => ({ + getIOSAppGroupDetails: () => ({appGroupSharedDirectory: 'appGroupSharedDirectory'}), + deleteEntitiesFile: jest.fn(), +})); +jest.mock('@utils/security', () => ({urlSafeBase64Encode: (url: string) => btoa(url)})); + +describe('Image utils', () => { + const intl = getIntlShape(); + beforeEach(() => { + jest.clearAllMocks(); + }); + + describe('filterFileExtensions', () => { + it('should return correct filter for each type', () => { + expect(filterFileExtensions('ALL')).toBe(''); + expect(filterFileExtensions('AUDIO')).toEqual(['mp3', 'wav', 'wma', 'm4a', 'flac', 'aac', 'ogg'].map((e) => `ext:${e}`).join(' ')); + expect(filterFileExtensions('CODE')).toEqual([ + 'as', 'applescript', 'osascript', 'scpt', 'bash', 'sh', 'zsh', 'clj', 'boot', 'cl2', 'cljc', 'cljs', + 'cljs.hl', 'cljscm', 'cljx', 'hic', 'coffee', '_coffee', 'cake', 'cjsx', 'cson', 'iced', 'cpp', 'c', 'cc', 'h', 'c++', + 'h++', 'hpp', 'cs', 'csharp', 'css', 'd', 'di', 'dart', 'delphi', 'dpr', 'dfm', 'pas', 'pascal', 'freepascal', 'lazarus', + 'lpr', 'lfm', 'diff', 'django', 'jinja', 'dockerfile', 'docker', 'erl', 'f90', 'f95', 'fsharp', 'fs', 'gcode', 'nc', 'go', + 'groovy', 'handlebars', 'hbs', 'html.hbs', 'html.handlebars', 'hs', 'hx', 'java', 'jsp', 'js', 'jsx', 'json', 'jl', 'kt', + 'ktm', 'kts', 'less', 'lisp', 'lua', 'mk', 'mak', 'md', 'mkdown', 'mkd', 'matlab', 'm', 'mm', 'objc', 'obj-c', 'ml', 'perl', + 'pl', 'php', 'php3', 'php4', 'php5', 'php6', 'ps', 'ps1', 'pp', 'py', 'gyp', 'r', 'ruby', 'rb', 'gemspec', 'podspec', 'thor', + 'irb', 'rs', 'scala', 'scm', 'sld', 'scss', 'st', 'sql', 'swift', 'ts', 'tex', 'vbnet', 'vb', 'bas', 'vbs', 'v', 'veo', 'xml', + 'html', 'xhtml', 'rss', 'atom', 'xsl', 'plist', 'yaml'].map((e) => `ext:${e}`).join(' ')); + expect(filterFileExtensions('DOCUMENTS')).toEqual(['doc', 'docx', 'odt', 'pdf', 'txt', 'rtf'].map((e) => `ext:${e}`).join(' ')); + expect(filterFileExtensions('IMAGES')).toEqual(['jpg', 'gif', 'bmp', 'png', 'jpeg', 'tiff', 'tif', 'svg', 'psd', 'xcf'].map((e) => `ext:${e}`).join(' ')); + expect(filterFileExtensions('PRESENTATIONS')).toEqual(['ppt', 'pptx', 'odp'].map((e) => `ext:${e}`).join(' ')); + expect(filterFileExtensions('SPREADSHEETS')).toEqual(['xls', 'xlsx', 'csv', 'ods'].map((e) => `ext:${e}`).join(' ')); + expect(filterFileExtensions('VIDEOS')).toEqual(['mp4', 'avi', 'webm', 'mkv', 'wmv', 'mpg', 'mov', 'flv', 'ogm', 'mpeg'].map((e) => `ext:${e}`).join(' ')); + expect(filterFileExtensions()).toBe(''); + }); + }); + + describe('deleteV1Data', () => { + it('should delete V1 data', async () => { + await deleteV1Data(); + expect(deleteAsync).toHaveBeenCalled(); + Platform.OS = 'android'; + await deleteV1Data(); + expect(deleteAsync).toHaveBeenCalled(); + Platform.OS = 'ios'; + }); + }); + + describe('deleteFileCache', () => { + it('should delete file cache', async () => { + await deleteFileCache('http://server.com'); + expect(deleteAsync).toHaveBeenCalled(); + }); + }); + + describe('deleteFileCacheByDir', () => { + it('should delete file cache by dir', async () => { + await deleteFileCacheByDir('someDir'); + expect(deleteAsync).toHaveBeenCalled(); + }); + }); + + describe('lookupMimeType', () => { + it('should return correct mime type', () => { + expect(lookupMimeType('file.txt')).toBe('text/plain'); + expect(lookupMimeType('file.jpg')).toBe('image/jpeg'); + expect(lookupMimeType('file.era')).toBe('application/octet-stream'); + }); + }); + + describe('getExtensionFromMime', () => { + it('should return correct extension from mime type', () => { + expect(getExtensionFromMime('application/json')).toBe('json'); + expect(getExtensionFromMime('image/png')).toBe('png'); + expect(getExtensionFromMime('video/mp4')).toBe('mp4'); + }); + }); + + describe('getExtensionFromContentDisposition', () => { + it('should return correct extension from content disposition', () => { + expect(getExtensionFromContentDisposition('inline;filename="file.txt";')).toBe('txt'); + expect(getExtensionFromContentDisposition('inline;filename="file.jpg";')).toBe('jpg'); + expect(getExtensionFromContentDisposition('inline;')).toBe(null); + }); + }); + + describe('getAllowedServerMaxFileSize', () => { + it('should return correct max file size', () => { + expect(getAllowedServerMaxFileSize({MaxFileSize: '10485760'} as ClientConfig)).toBe(10485760); + expect(getAllowedServerMaxFileSize({MaxFileSize: '10485a60'} as ClientConfig)).toBe(10485); + expect(getAllowedServerMaxFileSize({MaxFileSize: ''} as ClientConfig)).toBe(50 * 1024 * 1024); + }); + }); + + describe('isGif', () => { + it('should correctly identify gif files', () => { + expect(isGif({name: 'file.gif', mimeType: 'image/gif'} as unknown as FileInfo)).toBe(true); + expect(isGif({name: 'file.png', mimeType: 'image/gif'} as unknown as FileInfo)).toBe(true); + expect(isGif({name: 'file.png', mimeType: 'image/png'} as unknown as FileInfo)).toBe(false); + expect(isGif()).toBe(false); + }); + }); + + describe('isImage', () => { + it('should correctly identify image files', () => { + expect(isImage({name: 'file.png', mimeType: 'image/png'} as unknown as FileInfo)).toBe(true); + expect(isImage({name: 'file.jpg', mimeType: 'image/jpeg'} as unknown as FileInfo)).toBe(true); + expect(isImage({name: 'file.png', mimeType: 'text/plain'} as unknown as FileInfo)).toBe(false); + expect(isImage({name: 'file.png', extension: '.png'} as unknown as FileInfo)).toBe(true); + }); + }); + + describe('isDocument', () => { + it('should correctly identify document files', () => { + expect(isDocument({name: 'file.pdf', mimeType: 'application/pdf'} as unknown as FileInfo)).toBe(true); + expect(isDocument({name: 'file.doc', mimeType: 'application/vnd.apple.pages'} as unknown as FileInfo)).toBe(true); + expect(isDocument({name: 'file.mp4', mimeType: 'video/mp4'} as unknown as FileInfo)).toBe(false); + expect(isDocument({name: 'file.doc', mimeType: 'application/msword'} as unknown as FileInfo)).toBe(true); + }); + }); + + describe('isVideo', () => { + it('should correctly identify video files', () => { + expect(isVideo({name: 'file.mp4', mimeType: 'video/mp4'} as unknown as FileInfo)).toBe(true); + expect(isVideo({name: 'file.mov', mimeType: 'video/quicktime'} as unknown as FileInfo)).toBe(true); + expect(isVideo({name: 'file.mkv', mimeType: 'video/x-matroska'} as unknown as FileInfo)).toBe(false); + }); + }); + + describe('getFormattedFileSize', () => { + it('should return correct formatted file size', () => { + expect(getFormattedFileSize(102)).toBe('102 B'); + expect(getFormattedFileSize(1024)).toBe('1024 B'); + expect(getFormattedFileSize(1025)).toBe('1 KB'); + expect(getFormattedFileSize(10 * 1024 * 1024)).toBe('10 MB'); + expect(getFormattedFileSize(10 * 1024 * 1024 * 1024)).toBe('10 GB'); + expect(getFormattedFileSize(10 * 1024 * 1024 * 1024 * 1024)).toBe('10 TB'); + }); + }); + + describe('getFileType', () => { + it('should return correct file type', () => { + expect(getFileType({extension: 'png'} as unknown as FileInfo)).toBe('image'); + expect(getFileType({extension: 'mp4'} as unknown as FileInfo)).toBe('video'); + expect(getFileType({extension: 'pdf'} as unknown as FileInfo)).toBe('pdf'); + expect(getFileType({extension: 'arr'} as unknown as FileInfo)).toBe('other'); + expect(getFileType({} as unknown as FileInfo)).toBe('other'); + }); + }); + + describe('getLocalFilePathFromFile', () => { + it('should return correct local file path from file', () => { + expect(getLocalFilePathFromFile('http://server.com', {id: 'someid', name: 'image.png', extension: 'png'} as unknown as FileInfo)).toBe(`file://test-cache-directory/${urlSafeBase64Encode('http://server.com')}/image-someid.png`); + expect(getLocalFilePathFromFile('http://server.com', {id: 'someid', name: 'image.png'} as unknown as FileInfo)).toBe(`file://test-cache-directory/${urlSafeBase64Encode('http://server.com')}/image-someid.png`); + expect(getLocalFilePathFromFile('http://server.com', {id: 'someid', extension: 'png'} as unknown as FileInfo)).toBe(`file://test-cache-directory/${urlSafeBase64Encode('http://server.com')}/someid.png`); + expect(getLocalFilePathFromFile('http://server.com', {id: 'someid'} as unknown as FileInfo)).toBe(`file://test-cache-directory/${urlSafeBase64Encode('http://server.com')}/someid`); + expect(() => getLocalFilePathFromFile('http://server.com', {extension: 'png'} as unknown as FileInfo)).toThrow('File path could not be set'); + }); + }); + + describe('extractFileInfo', () => { + it('should extract file info correctly', async () => { + const files = [{uri: 'file://somefile', fileSize: 12345, fileName: 'file.png', type: 'image/png'}]; + let result = await extractFileInfo([]); + expect(result).toEqual([]); + result = await extractFileInfo([{fileName: 'file.png'}]); + expect(result).toEqual([]); + expect(logError).toHaveBeenCalled(); + result = await extractFileInfo(files); + expect(result).toEqual(expect.any(Array)); + result = await extractFileInfo([{uri: 'file://somefile', size: 12345, fileName: 'file.png', type: 'image/png'}]); + expect(result).toEqual(expect.any(Array)); + }); + }); + + describe('fileSizeWarning', () => { + it('should return correct file size warning', () => { + const msg = fileSizeWarning(intl, 10485760); + expect(msg).toBe('Files must be less than 10 MB'); + }); + }); + + describe('fileMaxWarning', () => { + it('should return correct file max warning', () => { + const msg = fileMaxWarning(intl, 10); + expect(msg).toBe('Uploads limited to 10 files maximum.'); + }); + }); + + describe('uploadDisabledWarning', () => { + it('should return correct upload disabled warning', () => { + const msg = uploadDisabledWarning(intl); + expect(msg).toBe('File uploads from mobile are disabled.'); + }); + }); + + describe('fileExists', () => { + it('should check if file exists', async () => { + // @ts-expect-error type def + getInfoAsync.mockResolvedValue({exists: true}); + const exists = await fileExists('somePath'); + expect(exists).toBe(true); + }); + }); + + describe('hasWriteStoragePermission', () => { + it('should check write storage permission', async () => { + // @ts-expect-error type def + Permissions.check.mockResolvedValue(Permissions.RESULTS.GRANTED); + const result = await hasWriteStoragePermission(intl); + expect(result).toBe(true); + }); + }); + + describe('getAllFilesInCachesDirectory', () => { + it('should get all files in caches directory', async () => { + const result = await getAllFilesInCachesDirectory('http://server.com'); + expect(result.files).toEqual(expect.any(Array)); + }); + }); +}); + diff --git a/app/utils/file/index.ts b/app/utils/file/index.ts index b75113583df..ac495ecbfb1 100644 --- a/app/utils/file/index.ts +++ b/app/utils/file/index.ts @@ -327,7 +327,7 @@ export function getFormattedFileSize(bytes: number): string { return `${bytes} B`; } -export function getFileType(file: FileInfo): string { +export function getFileType(file: FileInfo | ExtractedFileInfo): string { if (!file || !file.extension) { return 'other'; } @@ -380,11 +380,11 @@ export function getLocalFilePathFromFile(serverUrl: string, file: FileInfo | Fil } } - return `${cacheDirectory}/${server}/${filename}-${fileIdPath}.${extension}`; + return `${cacheDirectory}${server}/${filename}-${fileIdPath}.${extension}`; } else if (file?.id && hasValidExtension) { - return `${cacheDirectory}/${server}/${fileIdPath}.${file.extension}`; + return `${cacheDirectory}${server}/${fileIdPath}.${file.extension}`; } else if (file?.id) { - return `${cacheDirectory}/${server}/${fileIdPath}`; + return `${cacheDirectory}${server}/${fileIdPath}`; } } diff --git a/app/utils/gallery/index.test.ts b/app/utils/gallery/index.test.ts new file mode 100644 index 00000000000..9c9c660c759 --- /dev/null +++ b/app/utils/gallery/index.test.ts @@ -0,0 +1,283 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import {DeviceEventEmitter, Image, Keyboard} from 'react-native'; +import {Navigation} from 'react-native-navigation'; +import {measure, type AnimatedRef} from 'react-native-reanimated'; + +import {clamp, clampVelocity, fileToGalleryItem, freezeOtherScreens, friction, galleryItemToFileInfo, getImageSize, getShouldRender, measureItem, openGalleryAtIndex, typedMemo, workletNoop, workletNoopTrue} from '.'; + +import type {GalleryItemType, GalleryManagerSharedValues} from '@typings/screens/gallery'; + +jest.mock('@screens/navigation', () => ({ + showOverlay: jest.fn(), +})); + +jest.mock('react-native', () => { + const ReactNative = jest.requireActual('react-native'); + const { + NativeModules: RNNativeModules, + } = ReactNative; + + const NativeModules = { + ...RNNativeModules, + RNUtils: { + getConstants: () => ({ + appGroupIdentifier: 'group.mattermost.rnbeta', + appGroupSharedDirectory: { + sharedDirectory: '', + databasePath: '', + }, + }), + addListener: jest.fn(), + removeListeners: jest.fn(), + isRunningInSplitView: jest.fn().mockReturnValue({isSplit: false, isTablet: false}), + + getDeliveredNotifications: jest.fn().mockResolvedValue([]), + removeChannelNotifications: jest.fn().mockImplementation(), + removeThreadNotifications: jest.fn().mockImplementation(), + removeServerNotifications: jest.fn().mockImplementation(), + + unlockOrientation: jest.fn(), + }, + }; + + return Object.setPrototypeOf({ + NativeModules, + DeviceEventEmitter: { + emit: jest.fn(), + }, + Keyboard: { + dismiss: jest.fn(), + }, + }, ReactNative); +}); + +// Mock react-native-reanimated measure function +jest.mock('react-native-reanimated', () => ({ + measure: jest.fn(() => ({ + pageX: 100, + pageY: 200, + width: 300, + height: 400, + })), +})); + +describe('Gallery utils', () => { + afterAll(() => { + jest.clearAllMocks(); + }); + + describe('clamp', () => { + it('should clamp a value within the given bounds', () => { + expect(clamp(5, 1, 10)).toBe(5); + expect(clamp(-5, 0, 10)).toBe(0); + expect(clamp(15, 0, 10)).toBe(10); + }); + }); + + describe('clampVelocity', () => { + it('should clamp positive velocity within the given bounds', () => { + expect(clampVelocity(5, 1, 10)).toBe(5); + expect(clampVelocity(0.5, 1, 10)).toBe(1); + expect(clampVelocity(15, 1, 10)).toBe(10); + }); + + it('should clamp negative velocity within the given bounds', () => { + expect(clampVelocity(-5, 1, 10)).toBe(-5); + expect(clampVelocity(-0.5, 1, 10)).toBe(-1); + expect(clampVelocity(-15, 1, 10)).toBe(-10); + }); + }); + + describe('fileToGalleryItem', () => { + it('should convert file info to gallery item with image type', () => { + const file = { + extension: 'jpg', + height: 600, + id: '123', + mime_type: 'image/jpeg', + name: 'test-image', + post_id: 'post123', + size: 5000, + localPath: '/path/to/image.jpg', + width: 800, + } as FileInfo; + const result = fileToGalleryItem(file); + expect(result.type).toBe('image'); + expect(result.uri).toBe(file.localPath); + }); + + it('should convert file info to gallery item with video type', () => { + const file = { + extension: 'mp4', + height: 600, + id: '123', + mime_type: 'video/mp4', + name: 'test-video', + post_id: 'post123', + size: 10000, + localPath: '/path/to/video.mp4', + mini_preview: '/path/to/preview.jpg', + width: 800, + } as FileInfo; + const result = fileToGalleryItem(file); + expect(result.type).toBe('video'); + expect(result.posterUri).toBe(file.mini_preview); + }); + + it('should convert file info to gallery item with video type and assign a file id that starts with uid', () => { + const file = { + extension: 'mp4', + height: 600, + mime_type: 'video/mp4', + name: 'test-video', + post_id: 'post123', + size: 10000, + localPath: '/path/to/video.mp4', + mini_preview: '/path/to/preview.jpg', + width: 800, + } as FileInfo; + const result = fileToGalleryItem(file); + expect(result.id.startsWith('uid')).toBeTruthy(); + }); + }); + + describe('freezeOtherScreens', () => { + it('should emit freeze screen event', () => { + freezeOtherScreens(true); + expect(DeviceEventEmitter.emit).toHaveBeenCalledWith('FREEZE_SCREEN', true); + }); + }); + + describe('friction', () => { + it('should calculate friction based on value', () => { + expect(friction(100)).toBeGreaterThan(1); + expect(friction(-100)).toBeLessThan(0); + }); + }); + + describe('galleryItemToFileInfo', () => { + it('should convert gallery item to file info', () => { + const item = { + id: '123', + name: 'test-image', + width: 800, + height: 600, + extension: 'jpg', + mime_type: 'image/jpeg', + postId: 'post123', + authorId: 'user123', + } as GalleryItemType; + const result = galleryItemToFileInfo(item); + expect(result.id).toBe(item.id); + expect(result.name).toBe(item.name); + }); + }); + + describe('getShouldRender', () => { + it('should return true if index is within range of active index', () => { + expect(getShouldRender(5, 5)).toBe(true); + expect(getShouldRender(6, 5)).toBe(true); + expect(getShouldRender(9, 5)).toBe(false); + }); + }); + + describe('measureItem', () => { + it('should measure and set shared values', () => { + const ref = jest.fn() as unknown as AnimatedRef; + const sharedValues = { + x: {value: 0}, + y: {value: 0}, + width: {value: 0}, + height: {value: 0}, + } as GalleryManagerSharedValues; + measureItem(ref, sharedValues); + expect(sharedValues.x.value).toBe(100); + expect(sharedValues.y.value).toBe(200); + }); + + it('should measure and set shared values', () => { + const ref = jest.fn() as unknown as AnimatedRef; + const sharedValues = { + x: {value: 0}, + y: {value: 0}, + width: {value: 0}, + height: {value: 0}, + } as GalleryManagerSharedValues; + measureItem(ref, sharedValues); + expect(sharedValues.x.value).toBe(100); + expect(sharedValues.y.value).toBe(200); + }); + + it('should handle measure exception and set shared values out of the viewport', () => { + const ref = jest.fn() as unknown as AnimatedRef; + const sharedValues = { + x: {value: 0}, + y: {value: 0}, + width: {value: 0}, + height: {value: 0}, + } as GalleryManagerSharedValues; + (measure as jest.Mock).mockImplementationOnce(() => { + throw new Error('error'); + }); + measureItem(ref, sharedValues); + expect(sharedValues.x.value).toBe(999999); + expect(sharedValues.y.value).toBe(999999); + }); + }); + + describe('openGalleryAtIndex', () => { + it('should open gallery and freeze other screens', () => { + const galleryIdentifier = 'gallery1'; + const initialIndex = 0; + const items = [{id: '1', name: 'item1'}, {id: '2', name: 'item2'}] as GalleryItemType[]; + + openGalleryAtIndex(galleryIdentifier, initialIndex, items); + expect(Keyboard.dismiss).toHaveBeenCalled(); + expect(Navigation.setDefaultOptions).toHaveBeenCalled(); + }); + }); + + describe('typedMemo', () => { + it('should memoize component', () => { + const component = jest.fn(); + const memoizedComponent = typedMemo(component); + + // @ts-expect-error type in typedef + expect(memoizedComponent.type).toBe(component); + }); + }); + + describe('workletNoop', () => { + it('should execute without doing anything', () => { + expect(workletNoop()).toBeUndefined(); + }); + }); + + describe('workletNoopTrue', () => { + it('should always return true', () => { + expect(workletNoopTrue()).toBe(true); + }); + }); + + describe('getImageSize', () => { + it('should resolve with image size', async () => { + jest.spyOn(Image, 'getSize').mockImplementationOnce((uri, success) => { + success(800, 600); + }); + + const result = await getImageSize('test-uri'); + expect(result).toEqual({width: 800, height: 600}); + }); + + it('should reject on error', async () => { + jest.spyOn(Image, 'getSize').mockImplementationOnce((uri, success, failure) => { + // @ts-expect-error param + failure(new Error('Failed to get size')); + }); + + await expect(getImageSize('test-uri')).rejects.toThrow('Failed to get size'); + }); + }); +}); diff --git a/app/utils/gallery/index.ts b/app/utils/gallery/index.ts index 9caa1a2adc0..80148f71e9c 100644 --- a/app/utils/gallery/index.ts +++ b/app/utils/gallery/index.ts @@ -3,7 +3,7 @@ import RNUtils from '@mattermost/rnutils'; import React from 'react'; -import {DeviceEventEmitter, Keyboard, Platform} from 'react-native'; +import {DeviceEventEmitter, Image, Keyboard, Platform} from 'react-native'; import {Navigation, type Options, type OptionsLayout} from 'react-native-navigation'; import {measure, type AnimatedRef} from 'react-native-reanimated'; @@ -177,3 +177,9 @@ export const workletNoopTrue = () => { return true; }; + +export const getImageSize = (uri: string) => { + return new Promise<{width: number; height: number}>((resolve, reject) => { + Image.getSize(uri, (width, height) => resolve({width, height}), reject); + }); +}; diff --git a/app/utils/gallery/vectors.test.ts b/app/utils/gallery/vectors.test.ts new file mode 100644 index 00000000000..d5025dbf6de --- /dev/null +++ b/app/utils/gallery/vectors.test.ts @@ -0,0 +1,166 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import { + create, + add, + sub, + divide, + multiply, + invert, + set, + min, + max, + clamp, + eq, + useSharedVector, +} from './vectors'; + +// Mock the useSharedValue from react-native-reanimated +jest.mock('react-native-reanimated', () => ({ + useSharedValue: jest.fn((initialValue) => ({ + value: initialValue, + })), +})); + +describe('Vector operations', () => { + describe('create', () => { + it('should create a vector with given x and y', () => { + const vector = create(10, 20); + expect(vector).toEqual({x: 10, y: 20}); + }); + }); + + describe('useSharedVector', () => { + it('should create a shared vector with given x and y', () => { + const vector = useSharedVector(10, 20); + expect(vector.x.value).toBe(10); + expect(vector.y.value).toBe(20); + }); + + it('should use the same value for x and y if y is not provided', () => { + const vector = useSharedVector(15); + expect(vector.x.value).toBe(15); + expect(vector.y.value).toBe(15); + }); + }); + + describe('add', () => { + it('should add multiple vectors together', () => { + const v1 = create(10, 20); + const v2 = create(30, 40); + const result = add(v1, v2); + expect(result).toEqual({x: 40, y: 60}); + }); + }); + + describe('sub', () => { + it('should subtract multiple vectors', () => { + const v1 = create(30, 40); + const v2 = create(10, 20); + const result = sub(v1, v2); + expect(result).toEqual({x: 20, y: 20}); + }); + }); + + describe('multiply', () => { + it('should multiply multiple vectors', () => { + const v1 = create(10, 20); + const v2 = create(2, 3); + const result = multiply(v1, v2); + expect(result).toEqual({x: 20, y: 60}); + }); + }); + + describe('divide', () => { + it('should divide multiple vectors', () => { + const v1 = create(20, 40); + const v2 = create(2, 4); + const result = divide(v1, v2); + expect(result).toEqual({x: 10, y: 10}); + }); + + it('should return 0 when dividing by zero', () => { + const v1 = create(20, 40); + const v2 = create(0, 0); + const result = divide(v1, v2); + expect(result).toEqual({x: 0, y: 0}); + }); + }); + + describe('invert', () => { + it('should invert the vector', () => { + const v = create(10, -20); + const result = invert(v); + expect(result).toEqual({x: -10, y: 20}); + }); + }); + + describe('set', () => { + it('should set vector values using another vector', () => { + const vector = useSharedVector(0, 0); + const newValue = create(10, 20); + set(vector, newValue); + expect(vector.x.value).toBe(10); + expect(vector.y.value).toBe(20); + }); + + it('should set vector values using a callback function', () => { + const vector = useSharedVector(0, 0); + const callback = () => 5; + set(vector, callback); + expect(vector.x.value).toBe(5); + expect(vector.y.value).toBe(5); + }); + + it('should set vector values using shared values', () => { + const vector = useSharedVector(0, 0); + const sharedValue = 10; + set(vector, sharedValue); + expect(vector.x.value).toBe(10); + expect(vector.y.value).toBe(10); + }); + }); + + describe('min', () => { + it('should return the minimum of multiple vectors', () => { + const v1 = create(10, 20); + const v2 = create(30, 5); + const result = min(v1, v2); + expect(result).toEqual({x: 10, y: 5}); + }); + }); + + describe('max', () => { + it('should return the maximum of multiple vectors', () => { + const v1 = create(10, 20); + const v2 = create(30, 5); + const result = max(v1, v2); + expect(result).toEqual({x: 30, y: 20}); + }); + }); + + describe('clamp', () => { + it('should clamp vector within given bounds', () => { + const value = create(15, 5); + const lower = create(10, 10); + const upper = create(20, 20); + const result = clamp(value, lower, upper); + expect(result).toEqual({x: 15, y: 10}); + }); + }); + + describe('eq', () => { + it('should return true if vectors are equal', () => { + const v1 = create(10, 20); + const v2 = create(10, 20); + expect(eq(v1, v2)).toBe(true); + }); + + it('should return false if vectors are not equal', () => { + const v1 = create(10, 20); + const v2 = create(20, 10); + expect(eq(v1, v2)).toBe(false); + }); + }); +}); diff --git a/app/utils/gallery/vectors.ts b/app/utils/gallery/vectors.ts index a1508f54915..06db95f6eec 100644 --- a/app/utils/gallery/vectors.ts +++ b/app/utils/gallery/vectors.ts @@ -160,6 +160,7 @@ export const set = ( if (typeof value === 'function') { vector.x.value = value(); vector.y.value = value(); + return; } const x = get(isVector(value) ? value.x : value); diff --git a/app/utils/general/index.test.ts b/app/utils/general/index.test.ts new file mode 100644 index 00000000000..f83737295f5 --- /dev/null +++ b/app/utils/general/index.test.ts @@ -0,0 +1,108 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import ReactNativeHapticFeedback, {HapticFeedbackTypes} from 'react-native-haptic-feedback'; + +import {getIntlShape, emptyFunction, generateId, hapticFeedback, sortByNewest, isBetaApp, type SortByCreatAt} from './'; + +// Mock necessary modules +jest.mock('expo-application', () => ({ + applicationId: 'com.example.rnbeta', +})); + +jest.mock('expo-crypto', () => ({ + randomUUID: jest.fn(() => '12345678-1234-1234-1234-1234567890ab'), +})); + +jest.mock('react-intl', () => ({ + createIntl: jest.fn((config) => config), +})); + +jest.mock('react-native-haptic-feedback', () => ({ + trigger: jest.fn(), + HapticFeedbackTypes: { + impactLight: 'impactLight', + impactMedium: 'impactMedium', + }, +})); + +jest.mock('@i18n', () => ({ + getTranslations: jest.fn((locale) => ({message: `translations for ${locale}`})), + DEFAULT_LOCALE: 'en', +})); + +describe('getIntlShape', () => { + it('should return intl shape with default locale', () => { + const result = getIntlShape(); + expect(result).toEqual({ + locale: 'en', + messages: {message: 'translations for en'}, + }); + }); + + it('should return intl shape with specified locale', () => { + const result = getIntlShape('fr'); + expect(result).toEqual({ + locale: 'fr', + messages: {message: 'translations for fr'}, + }); + }); +}); + +describe('emptyFunction', () => { + it('should do nothing', () => { + expect(emptyFunction()).toBeUndefined(); + }); +}); + +describe('generateId', () => { + it('should generate an ID without prefix', () => { + const result = generateId(); + expect(result).toBe('12345678-1234-1234-1234-1234567890ab'); + }); + + it('should generate an ID with prefix', () => { + const result = generateId('prefix'); + expect(result).toBe('prefix-12345678-1234-1234-1234-1234567890ab'); + }); +}); + +describe('hapticFeedback', () => { + it('should trigger haptic feedback with default method', () => { + hapticFeedback(); + expect(ReactNativeHapticFeedback.trigger).toHaveBeenCalledWith('impactLight', { + enableVibrateFallback: false, + ignoreAndroidSystemSettings: false, + }); + }); + + it('should trigger haptic feedback with specified method', () => { + hapticFeedback(HapticFeedbackTypes.impactMedium); + expect(ReactNativeHapticFeedback.trigger).toHaveBeenCalledWith('impactMedium', { + enableVibrateFallback: false, + ignoreAndroidSystemSettings: false, + }); + }); +}); + +describe('sortByNewest', () => { + it('should sort by newest create_at', () => { + const a = {create_at: 2000} as SortByCreatAt; + const b = {create_at: 1000} as SortByCreatAt; + const result = sortByNewest(a, b); + expect(result).toBe(-1); + }); + + it('should sort by oldest create_at', () => { + const a = {create_at: 1000} as SortByCreatAt; + const b = {create_at: 2000} as SortByCreatAt; + const result = sortByNewest(a, b); + expect(result).toBe(1); + }); +}); + +describe('isBetaApp', () => { + it('should be true if applicationId includes rnbeta', () => { + expect(isBetaApp).toBe(true); + }); +}); diff --git a/app/utils/general/index.ts b/app/utils/general/index.ts index 4b9ea98afb9..5e4b99a2ac2 100644 --- a/app/utils/general/index.ts +++ b/app/utils/general/index.ts @@ -6,13 +6,13 @@ import {randomUUID} from 'expo-crypto'; import {createIntl} from 'react-intl'; import ReactNativeHapticFeedback, {HapticFeedbackTypes} from 'react-native-haptic-feedback'; -import {getTranslations} from '@i18n'; +import {DEFAULT_LOCALE, getTranslations} from '@i18n'; -type SortByCreatAt = (Session | Channel | Team | Post) & { +export type SortByCreatAt = (Session | Channel | Team | Post) & { create_at: number; } -export function getIntlShape(locale = 'en') { +export function getIntlShape(locale = DEFAULT_LOCALE) { return createIntl({ locale, messages: getTranslations(locale), diff --git a/app/utils/helpers.ts b/app/utils/helpers.ts index 67a2ac238b0..f654063aa8c 100644 --- a/app/utils/helpers.ts +++ b/app/utils/helpers.ts @@ -164,6 +164,10 @@ export function isMainActivity() { return true; } +function localeCompare(a: string, b: string) { + return a.localeCompare(b); +} + export function areBothStringArraysEqual(a: string[], b: string[]) { if (a.length !== b.length) { return false; @@ -173,8 +177,8 @@ export function areBothStringArraysEqual(a: string[], b: string[]) { return false; } - const aSorted = a.sort(); - const bSorted = b.sort(); + const aSorted = a.sort(localeCompare); + const bSorted = b.sort(localeCompare); const areBothEqual = aSorted.every((value, index) => value === bSorted[index]); return areBothEqual; diff --git a/app/utils/images/index.test.ts b/app/utils/images/index.test.ts new file mode 100644 index 00000000000..4e3036a3d59 --- /dev/null +++ b/app/utils/images/index.test.ts @@ -0,0 +1,152 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import {Dimensions} from 'react-native'; + +import {IMAGE_MAX_HEIGHT} from '@constants/image'; + +import { + calculateDimensions, + getViewPortWidth, + isGifTooLarge, +} from './index'; + +jest.mock('react-native', () => ({ + Dimensions: { + get: jest.fn(() => ({width: 800, height: 600})), + }, +})); + +jest.mock('@constants/image', () => ({ + IMAGE_MAX_HEIGHT: 800, + IMAGE_MIN_DIMENSION: 50, + MAX_GIF_SIZE: 1000000, + VIEWPORT_IMAGE_OFFSET: 20, + VIEWPORT_IMAGE_REPLY_OFFSET: 40, +})); + +jest.mock('@constants', () => ({ + View: { + TABLET_SIDEBAR_WIDTH: 100, + }, +})); + +describe('calculateDimensions', () => { + it('should return 0 dimensions when height and width are not provided', () => { + const result = calculateDimensions(); + expect(result).toEqual({height: 0, width: 0}); + }); + + it('should return correct dimensions when width exceeds viewport width', () => { + const result = calculateDimensions(200, 400, 300); + expect(result).toEqual({height: 150, width: 300}); + }); + + it('should assign imageHeight as IMAGE_MAX_HEIGHT when imageHeight exceeds IMAGE_MAX_HEIGHT and viewPortHeight is undefined', () => { + const result = calculateDimensions(900, 400, 600); + expect(result).toEqual({ + height: IMAGE_MAX_HEIGHT, + width: IMAGE_MAX_HEIGHT * (400 / 900), + }); + }); + + it('should return minimum dimensions when width is less than IMAGE_MIN_DIMENSION', () => { + const result = calculateDimensions(20, 30, 100); + expect(result).toEqual({height: 50, width: 75}); + }); + + it('should return correct dimensions when height exceeds IMAGE_MAX_HEIGHT', () => { + const result = calculateDimensions(900, 500, 600, 700); + expect(result).toEqual({height: 700, width: 388.8888888888889}); + }); + + it('should return minimum dimensions when height is less than IMAGE_MIN_DIMENSION', () => { + const result = calculateDimensions(30, 40, 100); + expect(result).toEqual({height: 50, width: 66.66666666666666}); + }); + + it('should assign imageHeight as viewPortHeight when imageHeight exceeds viewPortHeight and viewPortHeight is less than IMAGE_MAX_HEIGHT', () => { + const result = calculateDimensions(900, 400, 600, 700); + expect(result).toEqual({ + height: 700, + width: 700 * (400 / 900), + }); + }); + + it('should assign imageHeight and imageWidth based on IMAGE_MIN_DIMENSION when imageHeight is less than IMAGE_MIN_DIMENSION and fits within viewPortWidth', () => { + const result = calculateDimensions(20, 40, 100); + expect(result).toEqual({ + height: 50, + width: 50 * (40 / 20), + }); + }); + + it('should assign imageHeight and imageWidth based on viewPortHeight when imageHeight exceeds viewPortHeight', () => { + const result = calculateDimensions(600, 400, 500, 300); + expect(result).toEqual({ + height: 300, + width: 300 * (400 / 600), + }); + }); + + it('should assign imageHeight and imageWidth based on viewPortHeight when imageHeight exceeds viewPortHeight and previous conditions are not met', () => { + const result = calculateDimensions(1700, 900, 1000, 900); + expect(result).toEqual({ + height: 900, + width: 900 * (900 / 1700), + }); + }); +}); + +describe('getViewPortWidth', () => { + beforeEach(() => { + (Dimensions.get as jest.Mock).mockReturnValue({width: 800, height: 600}); + }); + + it('should calculate viewport width for normal post', () => { + const result = getViewPortWidth(false); + expect(result).toBe(580); + }); + + it('should calculate viewport width for reply post', () => { + const result = getViewPortWidth(true); + expect(result).toBe(540); + }); + + it('should calculate viewport width for tablet offset', () => { + const result = getViewPortWidth(false, true); + expect(result).toBe(480); + }); + + it('should calculate viewport width for reply post with tablet offset', () => { + const result = getViewPortWidth(true, true); + expect(result).toBe(440); + }); +}); + +describe('isGifTooLarge', () => { + it('should return false if image is not a gif', () => { + const result = isGifTooLarge({format: 'jpg', frame_count: 1, height: 500, width: 500}); + expect(result).toBe(false); + }); + + it('should return false if image metadata is undefined', () => { + const result = isGifTooLarge(undefined); + expect(result).toBe(false); + }); + + it('should return true if gif is too large', () => { + const result = isGifTooLarge({format: 'gif', frame_count: 100, height: 1000, width: 1000}); + expect(result).toBe(true); + }); + + it('should return false if gif is not too large', () => { + const result = isGifTooLarge({format: 'gif', frame_count: 10, height: 100, width: 100}); + expect(result).toBe(false); + }); + + it('should return false if gif does not specify frame count', () => { + const result = isGifTooLarge({format: 'gif', height: 100, width: 100}); + expect(result).toBe(false); + }); +}); diff --git a/app/utils/images/index.ts b/app/utils/images/index.ts index e61b3ed998c..e023d3e50f1 100644 --- a/app/utils/images/index.ts +++ b/app/utils/images/index.ts @@ -2,7 +2,6 @@ // See LICENSE.txt for license information. import {Dimensions} from 'react-native'; -import 'react-native-reanimated'; import {View} from '@constants'; import {IMAGE_MAX_HEIGHT, IMAGE_MIN_DIMENSION, MAX_GIF_SIZE, VIEWPORT_IMAGE_OFFSET, VIEWPORT_IMAGE_REPLY_OFFSET} from '@constants/image'; diff --git a/app/utils/markdown/index.test.ts b/app/utils/markdown/index.test.ts new file mode 100644 index 00000000000..63f28685162 --- /dev/null +++ b/app/utils/markdown/index.test.ts @@ -0,0 +1,203 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import {Platform, type TextStyle} from 'react-native'; + +import {Preferences} from '@constants'; + +import { + getCodeFont, + getMarkdownTextStyles, + getMarkdownBlockStyles, + getHighlightLanguageFromNameOrAlias, + getHighlightLanguageName, + escapeRegex, + getMarkdownImageSize, + computeTextStyle, + parseSearchTerms, + convertSearchTermToRegex, +} from './index'; + +jest.mock('@utils/images', () => ({ + getViewPortWidth: jest.fn(), +})); + +jest.mock('@utils/log', () => ({ + logError: jest.fn(), +})); + +describe('Utility functions', () => { + describe('getCodeFont', () => { + it('should return the correct font for iOS', () => { + Platform.OS = 'ios'; + expect(getCodeFont()).toBe('Menlo'); + }); + + it('should return the correct font for Android', () => { + Platform.OS = 'android'; + expect(getCodeFont()).toBe('monospace'); + }); + }); + + describe('getMarkdownTextStyles', () => { + it('should return correct text styles', () => { + const styles = getMarkdownTextStyles(Preferences.THEMES.denim); + expect(styles).toHaveProperty('emph'); + expect(styles).toHaveProperty('strong'); + expect(styles).toHaveProperty('del'); + expect(styles).toHaveProperty('link'); + expect(styles).toHaveProperty('heading1'); + expect(styles).toHaveProperty('code'); + expect(styles).toHaveProperty('mention'); + expect(styles).toHaveProperty('error'); + expect(styles).toHaveProperty('table_header_row'); + expect(styles).toHaveProperty('mention_highlight'); + }); + }); + + describe('getMarkdownBlockStyles', () => { + it('should return correct block styles', () => { + const styles = getMarkdownBlockStyles(Preferences.THEMES.denim); + expect(styles).toHaveProperty('adjacentParagraph'); + expect(styles).toHaveProperty('horizontalRule'); + expect(styles).toHaveProperty('quoteBlockIcon'); + }); + }); + + describe('getHighlightLanguageFromNameOrAlias', () => { + it('should return correct language name or alias', () => { + expect(getHighlightLanguageFromNameOrAlias('javascript')).toBe('javascript'); + expect(getHighlightLanguageFromNameOrAlias('js')).toBe('javascript'); + expect(getHighlightLanguageFromNameOrAlias('unknown')).toBe(''); + }); + }); + + describe('getHighlightLanguageName', () => { + it('should return correct language name', () => { + expect(getHighlightLanguageName('javascript')).toBe('JavaScript'); + expect(getHighlightLanguageName('unknown')).toBe(''); + }); + }); + + describe('escapeRegex', () => { + it('should escape special regex characters', () => { + expect(escapeRegex('hello.*')).toBe('hello\\.\\*'); + }); + }); + + describe('getMarkdownImageSize', () => { + it('should return correct size for sourceSize', () => { + const size = getMarkdownImageSize(false, false, {width: 100, height: 200}); + expect(size).toEqual({width: 100, height: 200}); + }); + + it('should return correct size for sourceSize without height', () => { + const size = getMarkdownImageSize(false, false, {width: 100}); + expect(size).toEqual({width: 100, height: 100}); + }); + + it('should return correct size for sourceSize without height and known size', () => { + const size = getMarkdownImageSize(false, false, {width: 100}, {width: 100, height: 200}); + expect(size).toEqual({width: 100, height: 200}); + }); + + it('should return correct size for sourceSize height height and known size', () => { + const size = getMarkdownImageSize(false, false, {height: 100}, {width: 100, height: 200}); + expect(size).toEqual({width: 50, height: 100}); + }); + + it('should return correct size for knownSize', () => { + const size = getMarkdownImageSize(false, false, undefined, {width: 100, height: 200}); + expect(size).toEqual({width: 100, height: 200}); + }); + + it('should return correct size when no metadata and source size is not specified', () => { + const size = getMarkdownImageSize(false, false, undefined, undefined, 150, 250); + expect(size).toEqual({width: 150, height: 250}); + }); + }); + + describe('computeTextStyle', () => { + const textStyles: { [key: string]: TextStyle } = { + bold: {fontWeight: 'bold'}, + italic: {fontStyle: 'italic'}, + underline: {textDecorationLine: 'underline'}, + }; + + const baseStyle: TextStyle = {color: 'black'}; + + it('should return base style if context is empty', () => { + expect(computeTextStyle(textStyles, baseStyle, [])).toEqual(baseStyle); + }); + + it('should return base style if context has no matching styles', () => { + expect(computeTextStyle(textStyles, baseStyle, ['unknown'])).toEqual(baseStyle); + }); + + it('should apply a single context style', () => { + expect(computeTextStyle(textStyles, baseStyle, ['bold'])).toEqual([baseStyle, textStyles.bold]); + }); + + it('should apply multiple context styles', () => { + expect(computeTextStyle(textStyles, baseStyle, ['bold', 'italic'])).toEqual([baseStyle, textStyles.bold, textStyles.italic]); + }); + + it('should ignore undefined styles', () => { + expect(computeTextStyle(textStyles, baseStyle, ['bold', 'unknown'])).toEqual([baseStyle, textStyles.bold]); + }); + + it('should handle multiple undefined styles', () => { + expect(computeTextStyle(textStyles, baseStyle, ['unknown1', 'unknown2'])).toEqual(baseStyle); + }); + }); + + describe('parseSearchTerms', () => { + it('should capture quoted strings', () => { + expect(parseSearchTerms('"hello world"')).toEqual(['hello world']); + }); + + it('should ignore search flags', () => { + expect(parseSearchTerms('in:channel before:2021-01-01')).toEqual([]); + }); + + it('should capture @ mentions', () => { + expect(parseSearchTerms('@username')).toEqual(['username']); + }); + + it('should capture plain text up to the next quote or search flag', () => { + expect(parseSearchTerms('plain text "quoted text"')).toEqual(['plain', 'text', 'quoted text']); + }); + + it('should split plain text into words', () => { + expect(parseSearchTerms('this is a test')).toEqual(['this', 'is', 'a', 'test']); + }); + + it('should handle a mix of all cases', () => { + const searchTerm = 'in:channel @username "quoted text" plain text'; + const expected = ['username', 'quoted text', 'plain', 'text']; + expect(parseSearchTerms(searchTerm)).toEqual(expected); + }); + }); + + describe('convertSearchTermToRegex', () => { + it('should create regex for CJK characters', () => { + const result = convertSearchTermToRegex('你好'); + expect(result.pattern).toEqual(/()(你好)/gi); + }); + + it('should create regex for wildcard at the end', () => { + const result = convertSearchTermToRegex('hello*'); + expect(result.pattern).toEqual(/\b()(hello)/gi); + }); + + it('should create regex for mentions and hashtags', () => { + const result = convertSearchTermToRegex('@user'); + expect(result.pattern).toEqual(/(\W|^)(@user)\b/gi); + }); + + it('should create regex for plain text', () => { + const result = convertSearchTermToRegex('hello'); + expect(result.pattern).toEqual(/\b()(hello)\b/gi); + }); + }); +}); diff --git a/app/utils/markdown/index.ts b/app/utils/markdown/index.ts index 9611c3f6ce6..95824ffcd24 100644 --- a/app/utils/markdown/index.ts +++ b/app/utils/markdown/index.ts @@ -331,10 +331,11 @@ export function parseSearchTerms(searchTerm: string): string[] | undefined { } // remove punctuation from each term - terms = terms.map((term) => { - term.replace(puncStart, ''); + terms = terms.map((t) => { + let term = t; + term = term.replace(puncStart, ''); if (term.charAt(term.length - 1) !== '*') { - term.replace(puncEnd, ''); + term = term.replace(puncEnd, ''); } return term; }); diff --git a/app/utils/navigation/index.test.ts b/app/utils/navigation/index.test.ts new file mode 100644 index 00000000000..800e1b5bbef --- /dev/null +++ b/app/utils/navigation/index.test.ts @@ -0,0 +1,80 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import {createIntl} from 'react-intl'; +import {Alert} from 'react-native'; +import {Navigation} from 'react-native-navigation'; + +import {ServerErrors} from '@constants'; +import {DEFAULT_LOCALE, getTranslations} from '@i18n'; + +import {mergeNavigationOptions, alertTeamRemove, alertChannelRemove, alertChannelArchived, alertTeamAddError} from '.'; + +describe('Navigation utils', () => { + const componentId = 'component-id'; + const options = {topBar: {title: {text: 'Test'}}}; + const displayName = 'Test Display Name'; + const serverError = {server_error_id: ServerErrors.TEAM_MEMBERSHIP_DENIAL_ERROR_ID}; + const genericServerError = {server_error_id: 'api.some_server_error.id', message: 'Generic error message'}; + const intl = createIntl({locale: DEFAULT_LOCALE, messages: getTranslations(DEFAULT_LOCALE)}); + + afterEach(() => { + jest.clearAllMocks(); + }); + + it('should call Navigation.mergeOptions with the correct arguments', () => { + mergeNavigationOptions(componentId, options); + expect(Navigation.mergeOptions).toHaveBeenCalledWith(componentId, options); + }); + + it('should display alert when a user is removed from a team', () => { + alertTeamRemove(displayName, intl); + expect(Alert.alert).toHaveBeenCalledWith( + 'Removed from team', + 'You have been removed from team Test Display Name.', + [{style: 'cancel', text: 'OK'}], + ); + }); + + it('should display alert when a user is removed from a channel', () => { + alertChannelRemove(displayName, intl); + expect(Alert.alert).toHaveBeenCalledWith( + 'Removed from channel', + 'You have been removed from channel Test Display Name.', + [{style: 'cancel', text: 'OK'}], + ); + }); + + it('should display alert when a channel is archived', () => { + alertChannelArchived(displayName, intl); + expect(Alert.alert).toHaveBeenCalledWith( + 'Archived channel', + 'The channel Test Display Name has been archived.', + [{style: 'cancel', text: 'OK'}], + ); + }); + + it('should display alert for team add error with default message', () => { + alertTeamAddError({}, intl); + expect(Alert.alert).toHaveBeenCalledWith( + 'Error joining a team', + 'There has been an error joining the team', + ); + }); + + it('should display alert for team add error with specific server error message', () => { + alertTeamAddError(serverError, intl); + expect(Alert.alert).toHaveBeenCalledWith( + 'Error joining a team', + 'You need to be a member of a linked group to join this team.', + ); + }); + + it('should display alert for team add error with generic error message', () => { + alertTeamAddError(genericServerError, intl); + expect(Alert.alert).toHaveBeenCalledWith( + 'Error joining a team', + 'Generic error message', + ); + }); +}); diff --git a/app/utils/notification/index.test.ts b/app/utils/notification/index.test.ts new file mode 100644 index 00000000000..c7c36887cf8 --- /dev/null +++ b/app/utils/notification/index.test.ts @@ -0,0 +1,146 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import moment from 'moment-timezone'; +import {createIntl} from 'react-intl'; +import {Alert, DeviceEventEmitter} from 'react-native'; +import {Notifications} from 'react-native-notifications'; + +import {Events} from '@constants'; +import {DEFAULT_LOCALE, getTranslations} from '@i18n'; +import {popToRoot} from '@screens/navigation'; + +import { + convertToNotificationData, + notificationError, + emitNotificationError, + scheduleExpiredNotification, +} from '.'; + +describe('Notification Utils', () => { + const intl = createIntl({locale: DEFAULT_LOCALE, messages: getTranslations(DEFAULT_LOCALE)}); + const notification = { + identifier: 'id', + payload: { + ack_id: 'ack_id', + channel_id: 'channel_id', + channel_name: 'channel_name', + from_webhook: true, + message: 'Test message', + override_icon_url: 'icon_url', + override_username: 'username', + post_id: 'post_id', + root_id: 'root_id', + sender_id: 'sender_id', + sender_name: 'sender_name', + server_id: 'server_id', + server_url: 'server_url', + team_id: 'team_id', + type: 'message', + sub_type: 'sub_type', + use_user_icon: true, + version: '1.0', + is_crt_enabled: 'true', + data: {}, + }, + body: 'body', + }; + + const session = { + expires_at: moment().add(10, 'hours').valueOf(), + }; + + afterEach(() => { + jest.clearAllMocks(); + }); + + describe('convertToNotificationData', () => { + it('should convert notification with payload to NotificationWithData', () => { + const result = convertToNotificationData(notification as any, true); + const not = {...notification}; + Reflect.deleteProperty(not.payload, 'is_crt_enabled'); + expect(result).toEqual({ + ...not, + payload: { + ...not.payload, + identifier: 'id', + isCRTEnabled: true, + message: 'Test message', + }, + userInteraction: true, + foreground: false, + }); + }); + + it('should return the original notification if no payload is present', () => { + const result = convertToNotificationData({identifier: 'id'} as any, false); + expect(result).toEqual({identifier: 'id'}); + }); + }); + + describe('notificationError', () => { + it('should display alert and popToRoot for Channel type', () => { + notificationError(intl, 'Channel'); + expect(Alert.alert).toHaveBeenCalledWith( + 'Message not found', + 'This message belongs to a channel where you are not a member.', + ); + expect(popToRoot).toHaveBeenCalled(); + }); + + it('should display alert and popToRoot for Team type', () => { + notificationError(intl, 'Team'); + expect(Alert.alert).toHaveBeenCalledWith( + 'Message not found', + 'This message belongs to a team where you are not a member.', + ); + expect(popToRoot).toHaveBeenCalled(); + }); + + it('should display alert and popToRoot for Post type', () => { + notificationError(intl, 'Post'); + expect(Alert.alert).toHaveBeenCalledWith( + 'Message not found', + 'The message has not been found.', + ); + expect(popToRoot).toHaveBeenCalled(); + }); + + it('should display alert and popToRoot for Connection type', () => { + notificationError(intl, 'Connection'); + expect(Alert.alert).toHaveBeenCalledWith( + 'Message not found', + 'The server is unreachable and it was not possible to retrieve the specific message information for the notification.', + ); + expect(popToRoot).toHaveBeenCalled(); + }); + }); + + describe('emitNotificationError', () => { + it('should emit notification error after 500ms', (done) => { + const spyEmit = jest.spyOn(DeviceEventEmitter, 'emit'); + emitNotificationError('Channel'); + setTimeout(() => { + expect(spyEmit).toHaveBeenCalledWith(Events.NOTIFICATION_ERROR, 'Channel'); + done(); + }, 600); // wait a little longer than 500ms to ensure the timeout has executed + }); + }); + + describe('scheduleExpiredNotification', () => { + it('should schedule a notification for session expiration with hours', () => { + const result = scheduleExpiredNotification('server_url', session as any, 'ServerName', 'en'); + expect(Notifications.postLocalNotification).toHaveBeenCalledWith(expect.objectContaining({ + fireDate: new Date(session.expires_at).toISOString(), + body: 'Please log in to continue receiving notifications. Sessions for ServerName are configured to expire every 10 hours.', + title: 'Session Expired', + })); + expect(result).toBeDefined(); + }); + + it('should return 0 if expiresAt is not defined', () => { + const result = scheduleExpiredNotification('server_url', {} as any, 'ServerName', 'en'); + expect(result).toBe(0); + }); + }); +}); diff --git a/app/utils/notification/index.ts b/app/utils/notification/index.ts index 81309510dc4..275d90d9098 100644 --- a/app/utils/notification/index.ts +++ b/app/utils/notification/index.ts @@ -2,14 +2,15 @@ // See LICENSE.txt for license information. import moment from 'moment-timezone'; -import {createIntl, type IntlShape} from 'react-intl'; +import {type IntlShape} from 'react-intl'; import {Alert, DeviceEventEmitter} from 'react-native'; import {Events} from '@constants'; import {NOTIFICATION_TYPE} from '@constants/push_notification'; -import {DEFAULT_LOCALE, getTranslations} from '@i18n'; +import {DEFAULT_LOCALE} from '@i18n'; import PushNotifications from '@init/push_notifications'; import {popToRoot} from '@screens/navigation'; +import {getIntlShape} from '@utils/general'; export const convertToNotificationData = (notification: Notification, tapped = true): NotificationWithData => { if (!notification.payload) { @@ -95,7 +96,7 @@ export const scheduleExpiredNotification = (serverUrl: string, session: Session, const expiresInHours = Math.ceil(Math.abs(moment.duration(moment().diff(moment(expiresAt))).asHours())); const expiresInDays = Math.floor(expiresInHours / 24); // Calculate expiresInDays const remainingHours = expiresInHours % 24; // Calculate remaining hours - const intl = createIntl({locale, messages: getTranslations(locale)}); + const intl = getIntlShape(locale); let body = ''; if (expiresInDays === 0) { body = intl.formatMessage({ diff --git a/app/utils/opengraph.ts b/app/utils/opengraph.ts index f91e3946da8..19fa1c6e288 100644 --- a/app/utils/opengraph.ts +++ b/app/utils/opengraph.ts @@ -1,11 +1,53 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. +import {decode} from 'html-entities'; +import htmlParser from 'node-html-parser'; +import urlParse from 'url-parse'; + export type BestImage = { secure_url?: string; url?: string; }; +export type OpenGraph = { + link: string; + title?: string; + imageURL?: string; + favIcon?: string; + error?: any; +} + +type LinkRelIcon = { + href: string; + sizes?: string; +} + +const metaTags: Record = { + title: 'title', + description: 'description', + ogUrl: 'og:url', + ogType: 'og:type', + ogTitle: 'og:title', + ogDescription: 'og:description', + ogImage: 'og:image', + ogVideo: 'og:video', + ogVideoType: 'og:video:type', + ogVideoWidth: 'og:video:width', + ogVideoHeight: 'og:video:height', + ogVideoUrl: 'og:video:url', + twitterPlayer: 'twitter:player', + twitterPlayerWidth: 'twitter:player:width', + twitterPlayerHeight: 'twitter:player:height', + twitterPlayerStream: 'twitter:player:stream', + twitterCard: 'twitter:card', + twitterDomain: 'twitter:domain', + twitterUrl: 'twitter:url', + twitterTitle: 'twitter:title', + twitterDescription: 'twitter:description', + twitterImage: 'twitter:image', +}; + export function getDistanceBW2Points(point1: Record, point2: Record, xAttr = 'x', yAttr = 'y') { return Math.sqrt(Math.pow(point1[xAttr] - point2[xAttr], 2) + Math.pow(point1[yAttr] - point2[yAttr], 2)); } @@ -27,3 +69,122 @@ export function getNearestPoint(pivotPoint: {height: number; width: number}, poi } return nearestPoint as BestImage; } + +const fetchRaw = async (url: string) => { + try { + const res = await fetch(url, { + headers: { + 'User-Agent': 'OpenGraph', + 'Cache-Control': 'no-cache', + Accept: '*/*', + Connection: 'keep-alive', + }, + }); + + if (!res.ok) { + return res; + } + + return (await res.text()) as any; + } catch (error: any) { + return {message: error.message}; + } +}; + +const getFavIcon = (url: string, html: string) => { + const getSize = (el: LinkRelIcon) => { + return (el.sizes && el.sizes[0] && parseInt(el.sizes[0], 10)) || 0; + }; + + const root = htmlParser.parse(html); + + const icons = (root.querySelectorAll('link')).reduce((r, e) => { + if (e.attributes.rel === 'icon' || e.attributes.rel === 'shortcut icon') { + const {href, sizes} = e.attributes; + r.push({href, sizes}); + } + return r; + }, []).sort((a, b) => { + return getSize(b) - getSize(a); + }); + + if (icons.length) { + const icon = icons[0].href; + let parsed = urlParse(icon); + if (!parsed.host) { + parsed = urlParse(url); + return `${parsed.protocol}//${parsed.host}${icon}`; + } + return icon; + } + const parsed = urlParse(url); + return `${parsed.protocol}//${parsed.host}/favicon.ico`; +}; + +export const fetchOpenGraph = async (url: string, includeFavIcon = false): Promise => { + const { + ogTitle, + ogImage, + } = metaTags; + + try { + const html = await fetchRaw(url); + if (html.message) { + throw Error(html.message); + } + + let siteTitle = ''; + + const tagTitle = html.match( + /]*>[\r\n\t\s]*([^<]+)[\r\n\t\s]*<\/title>/gim, + ); + siteTitle = tagTitle[0].replace( + /]*>[\r\n\t\s]*([^<]+)[\r\n\t\s]*<\/title>/gim, + '$1', + ); + + const og = []; + const metas: any = html.match(/]+>/gim); + + // There is no else statement + /* istanbul ignore else */ + if (metas) { + for (const m of metas) { + const meta = m.replace(/\s*\/?>$/, ' />'); + const zname = meta.replace(/[\s\S]*(property|name)\s*=\s*([\s\S]+)/, '$2'); + const name = (/^["']/).test(zname) ? zname.substr(1, zname.slice(1).indexOf(zname[0])) : zname.substr(0, zname.search(/[\s\t]/g)); + const valid = Boolean(Object.keys(metaTags).filter((key: string) => metaTags[key].toLowerCase() === name.toLowerCase()).length); + + // There is no else statement + /* istanbul ignore else */ + if (valid) { + const zcontent = meta.replace(/[\s\S]*(content)\s*=\s*([\s\S]+)/, '$2'); + const content = (/^["']/).test(zcontent) ? zcontent.substr(1, zcontent.slice(1).indexOf(zcontent[0])) : zcontent.substr(0, zcontent.search(/[\s\t]/g)); + og.push({name, value: content === 'undefined' ? null : content}); + } + } + } + + const result: OpenGraph = {link: url}; + const data = og.reduce( + (chain: any, meta: any) => ({...chain, [meta.name]: decode(meta.value)}), + {url}, + ); + + // Image + result.imageURL = data[ogImage] ? data[ogImage] : null; + result.favIcon = includeFavIcon ? getFavIcon(url, html) : undefined; + + // Title + data[ogTitle] = data[ogTitle] ? data[ogTitle] : siteTitle; + + result.title = data[ogTitle]; + + return result; + } catch (error: any) { + return { + link: url, + error, + }; + } +}; diff --git a/app/utils/permalink/index.test.ts b/app/utils/permalink/index.test.ts new file mode 100644 index 00000000000..add8265bddc --- /dev/null +++ b/app/utils/permalink/index.test.ts @@ -0,0 +1,84 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import {Keyboard, Platform} from 'react-native'; +import {OptionsModalPresentationStyle} from 'react-native-navigation'; + +import {dismissAllModals, showModalOverCurrentContext} from '@screens/navigation'; + +import {displayPermalink, closePermalink} from '.'; + +jest.mock('@screens/navigation', () => ({ + dismissAllModals: jest.fn(), + showModalOverCurrentContext: jest.fn(), +})); + +describe('permalinkUtils', () => { + const originalSelect = Platform.select; + + beforeAll(() => { + Platform.select = ({android, ios, default: dft}: any) => { + if (Platform.OS === 'android' && android) { + return android; + } else if (Platform.OS === 'ios' && ios) { + return ios; + } + + return dft; + }; + }); + + afterAll(() => { + Platform.select = originalSelect; + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + describe('displayPermalink', () => { + it('should dismiss keyboard and show permalink modal', async () => { + const dismiss = jest.spyOn(Keyboard, 'dismiss'); + await displayPermalink('teamName', 'postId'); + expect(dismiss).toHaveBeenCalled(); + expect(showModalOverCurrentContext).toHaveBeenCalledWith( + 'Permalink', + {isPermalink: true, teamName: 'teamName', postId: 'postId'}, + { + modalPresentationStyle: OptionsModalPresentationStyle.overFullScreen, + layout: { + componentBackgroundColor: 'rgba(0,0,0,0.2)', + }, + }, + ); + }); + + it('should dismiss all modals if showingPermalink is true', async () => { + // Simulate showingPermalink being true + await displayPermalink('teamName', 'postId'); + await displayPermalink('teamName', 'postId'); + expect(dismissAllModals).toHaveBeenCalled(); + }); + + it('should handle platform specific options correctly', async () => { + await displayPermalink('teamName', 'postId'); + expect(showModalOverCurrentContext).toHaveBeenCalledWith( + 'Permalink', + {isPermalink: true, teamName: 'teamName', postId: 'postId'}, + { + modalPresentationStyle: OptionsModalPresentationStyle.overFullScreen, + layout: { + componentBackgroundColor: 'rgba(0,0,0,0.2)', + }, + }, + ); + }); + }); + + describe('closePermalink', () => { + it('should set showingPermalink to false', () => { + const showingPermalink = closePermalink(); + expect(showingPermalink).toBe(false); + }); + }); +}); diff --git a/app/utils/permalink/index.ts b/app/utils/permalink/index.ts index b52f1dba202..6af509fc3ed 100644 --- a/app/utils/permalink/index.ts +++ b/app/utils/permalink/index.ts @@ -39,4 +39,5 @@ export const displayPermalink = async (teamName: string, postId: string, openAsP export const closePermalink = () => { showingPermalink = false; + return showingPermalink; }; diff --git a/app/utils/post/index.test.ts b/app/utils/post/index.test.ts index 8fd8bd93c0e..463577f2bd4 100644 --- a/app/utils/post/index.test.ts +++ b/app/utils/post/index.test.ts @@ -1,52 +1,585 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import * as utils from './index'; +import {createIntl} from 'react-intl'; +import {Alert} from 'react-native'; + +import {getUsersCountFromMentions} from '@actions/local/post'; +import {General, Post} from '@constants'; +import {DEFAULT_LOCALE, getTranslations} from '@i18n'; +import {getUserById} from '@queries/servers/user'; +import {toMilliseconds} from '@utils/datetime'; + +import { + areConsecutivePosts, + isFromWebhook, + isEdited, + isPostEphemeral, + isPostFailed, + isPostPendingOrFailed, + isSystemMessage, + fromAutoResponder, + postUserDisplayName, + shouldIgnorePost, + processPostsFetched, + getLastFetchedAtFromPosts, + moreThan5minAgo, + hasSpecialMentions, + persistentNotificationsConfirmation, +} from '.'; + +import type PostModel from '@typings/database/models/servers/post'; +import type UserModel from '@typings/database/models/servers/user'; + +jest.mock('@actions/local/post', () => ({ + getUsersCountFromMentions: jest.fn(), +})); + +jest.mock('@queries/servers/user', () => ({ + getUserById: jest.fn(), +})); + +jest.mock('@database/manager', () => ({ + getServerDatabaseAndOperator: jest.fn().mockReturnValue({ + database: {}, + }), +})); describe('post utils', () => { - test.each([ - ['@here where is Jessica Hyde', true], - ['@all where is Jessica Hyde', true], - ['@channel where is Jessica Hyde', true], - - ['where is Jessica Hyde @here', true], - ['where is Jessica Hyde @all', true], - ['where is Jessica Hyde @channel', true], - - ['where is Jessica @here Hyde', true], - ['where is Jessica @all Hyde', true], - ['where is Jessica @channel Hyde', true], - - ['where is Jessica Hyde\n@here', true], - ['where is Jessica Hyde\n@all', true], - ['where is Jessica Hyde\n@channel', true], - - ['where is Jessica\n@here Hyde', true], - ['where is Jessica\n@all Hyde', true], - ['where is Jessica\n@channel Hyde', true], - - ['where is Jessica Hyde @her', false], - ['where is Jessica Hyde @al', false], - ['where is Jessica Hyde @chann', false], - - ['where is Jessica Hyde@here', false], - ['where is Jessica Hyde@all', false], - ['where is Jessica Hyde@channel', false], - - ['where is Jessica @hereHyde', false], - ['where is Jessica @allHyde', false], - ['where is Jessica @channelHyde', false], - - ['@herewhere is Jessica Hyde@here', false], - ['@allwhere is Jessica Hyde@all', false], - ['@channelwhere is Jessica Hyde@channel', false], - - ['where is Jessica Hyde here', false], - ['where is Jessica Hyde all', false], - ['where is Jessica Hyde channel', false], - - ['where is Jessica Hyde', false], - ])('hasSpecialMentions: %s => %s', (message, expected) => { - expect(utils.hasSpecialMentions(message)).toBe(expected); + describe('areConsecutivePosts', () => { + it('should return true for consecutive posts from the same user within the collapse timeout', () => { + const post = { + userId: 'user1', + createAt: 1000, + props: {}, + } as PostModel; + const previousPost = { + userId: 'user1', + createAt: 500, + props: {}, + } as PostModel; + + const result = areConsecutivePosts(post, previousPost); + expect(result).toBe(true); + }); + + it('should return false for posts from different users', () => { + const post = { + userId: 'user1', + createAt: 1000, + props: {}, + } as PostModel; + const previousPost = { + userId: 'user2', + createAt: 500, + props: {}, + } as PostModel; + + const result = areConsecutivePosts(post, previousPost); + expect(result).toBe(false); + }); + }); + + describe('isFromWebhook', () => { + it('should return true for posts from a webhook', () => { + const post = { + props: { + from_webhook: 'true', + }, + } as PostModel; + + const result = isFromWebhook(post); + expect(result).toBe(true); + }); + + it('should return false for posts not from a webhook', () => { + const post = { + props: { + from_webhook: 'false', + }, + } as PostModel; + + const result = isFromWebhook(post); + expect(result).toBe(false); + }); + }); + + describe('isEdited', () => { + it('should return true if the post is edited', () => { + const post = { + editAt: 1000, + } as PostModel; + + const result = isEdited(post); + expect(result).toBe(true); + }); + + it('should return false if the post is not edited', () => { + const post = { + editAt: 0, + } as PostModel; + + const result = isEdited(post); + expect(result).toBe(false); + }); + }); + + describe('isPostEphemeral', () => { + it('should return true for an ephemeral post', () => { + const post = { + type: Post.POST_TYPES.EPHEMERAL, + } as PostModel; + + const result = isPostEphemeral(post); + expect(result).toBe(true); + }); + + it('should return false for a non-ephemeral post', () => { + const post = { + type: 'normal', + } as PostModel; + + const result = isPostEphemeral(post); + expect(result).toBe(false); + }); + }); + + describe('isPostFailed', () => { + it('should return true if the post has failed prop', () => { + const post = { + props: { + failed: true, + }, + pendingPostId: 'id', + id: 'id', + updateAt: Date.now() - Post.POST_TIME_TO_FAIL - 1000, + } as PostModel; + + const result = isPostFailed(post); + expect(result).toBe(true); + }); + + it('should return true if the post is pending and the update time has exceeded the failure time', () => { + const post = { + props: {}, + pendingPostId: 'id', + id: 'id', + updateAt: Date.now() - Post.POST_TIME_TO_FAIL - 1000, + } as PostModel; + + const result = isPostFailed(post); + expect(result).toBe(true); + }); + + it('should return false if the post is not failed', () => { + const post = { + props: {}, + pendingPostId: 'id', + id: 'id', + updateAt: Date.now(), + } as PostModel; + + const result = isPostFailed(post); + expect(result).toBe(false); + }); + }); + + describe('isPostPendingOrFailed', () => { + it('should return true if the post is pending', () => { + const post = { + pendingPostId: 'id', + id: 'id', + props: {}, + } as PostModel; + + const result = isPostPendingOrFailed(post); + expect(result).toBe(true); + }); + + it('should return true if the post has failed', () => { + const post = { + pendingPostId: 'id', + id: 'id', + updateAt: Date.now() - Post.POST_TIME_TO_FAIL - 1000, + props: {}, + } as PostModel; + + const result = isPostPendingOrFailed(post); + expect(result).toBe(true); + }); + + it('should return false if the post is neither pending nor failed', () => { + const post = { + pendingPostId: 'differentId', + id: 'id', + props: {}, + } as PostModel; + + const result = isPostPendingOrFailed(post); + expect(result).toBe(false); + }); + }); + + describe('isSystemMessage', () => { + it('should return true if the post is a system message', () => { + const post = { + type: `${Post.POST_TYPES.SYSTEM_MESSAGE_PREFIX}any_type`, + } as PostModel; + + const result = isSystemMessage(post); + expect(result).toBe(true); + }); + + it('should return false if the post is not a system message', () => { + const post = { + type: 'normal_type', + } as PostModel; + + const result = isSystemMessage(post); + expect(result).toBe(false); + }); + }); + + describe('hasSpecialMentions', () => { + test.each([ + ['@here where is Jessica Hyde', true], + ['@all where is Jessica Hyde', true], + ['@channel where is Jessica Hyde', true], + + ['where is Jessica Hyde @here', true], + ['where is Jessica Hyde @all', true], + ['where is Jessica Hyde @channel', true], + + ['where is Jessica @here Hyde', true], + ['where is Jessica @all Hyde', true], + ['where is Jessica @channel Hyde', true], + + ['where is Jessica Hyde\n@here', true], + ['where is Jessica Hyde\n@all', true], + ['where is Jessica Hyde\n@channel', true], + + ['where is Jessica\n@here Hyde', true], + ['where is Jessica\n@all Hyde', true], + ['where is Jessica\n@channel Hyde', true], + + ['where is Jessica Hyde @her', false], + ['where is Jessica Hyde @al', false], + ['where is Jessica Hyde @chann', false], + + ['where is Jessica Hyde@here', false], + ['where is Jessica Hyde@all', false], + ['where is Jessica Hyde@channel', false], + + ['where is Jessica @hereHyde', false], + ['where is Jessica @allHyde', false], + ['where is Jessica @channelHyde', false], + + ['@herewhere is Jessica Hyde@here', false], + ['@allwhere is Jessica Hyde@all', false], + ['@channelwhere is Jessica Hyde@channel', false], + + ['where is Jessica Hyde here', false], + ['where is Jessica Hyde all', false], + ['where is Jessica Hyde channel', false], + + ['where is Jessica Hyde', false], + ])('hasSpecialMentions: %s => %s', (message, expected) => { + expect(hasSpecialMentions(message)).toBe(expected); + }); + }); + + describe('fromAutoResponder', () => { + it('should return true if the post is from an auto responder', () => { + const post = { + type: Post.POST_TYPES.SYSTEM_AUTO_RESPONDER, + } as PostModel; + + const result = fromAutoResponder(post); + expect(result).toBe(true); + }); + + it('should return false if the post is not from an auto responder', () => { + const post = { + type: 'normal_type', + } as PostModel; + + const result = fromAutoResponder(post); + expect(result).toBe(false); + }); + }); + + describe('persistentNotificationsConfirmation', () => { + const serverUrl = 'http://server'; + const value = '@user'; + const mentionsList = ['@user']; + const sendMessage = jest.fn(); + const persistentNotificationMaxRecipients = 10; + const persistentNotificationInterval = 5; + const currentUserId = 'current_user_id'; + const channelName = 'channel_id__teammate_id'; + const intl = createIntl({locale: DEFAULT_LOCALE, messages: getTranslations(DEFAULT_LOCALE)}); + + it('should show alert with DM channel description when channelType is DM_CHANNEL', async () => { + const mockUser = {username: 'teammate'}; + (getUserById as jest.Mock).mockResolvedValue(mockUser); + + await persistentNotificationsConfirmation( + serverUrl, + value, + mentionsList, + intl, + sendMessage, + persistentNotificationMaxRecipients, + persistentNotificationInterval, + currentUserId, + channelName, + General.DM_CHANNEL, + ); + + expect(Alert.alert).toHaveBeenCalledWith( + intl.formatMessage({ + id: 'persistent_notifications.confirm.title', + defaultMessage: 'Send persistent notifications', + }), + intl.formatMessage({ + id: 'persistent_notifications.dm_channel.description', + defaultMessage: '@{username} will be notified every {interval, plural, one {minute} other {{interval} minutes}} until they’ve acknowledged or replied to the message.', + }, { + interval: persistentNotificationInterval, + username: mockUser.username, + }), + expect.any(Array), + ); + }); + + it('should show alert when special mentions are present', async () => { + await persistentNotificationsConfirmation( + serverUrl, + '@channel', + mentionsList, + intl, + sendMessage, + persistentNotificationMaxRecipients, + persistentNotificationInterval, + currentUserId, + channelName, + ); + + expect(Alert.alert).toHaveBeenCalledWith( + '', + intl.formatMessage({ + id: 'persistent_notifications.error.special_mentions', + defaultMessage: 'Cannot use @channel, @all or @here to mention recipients of persistent notifications.', + }), + expect.any(Array), + ); + }); + + it('should show alert when no mentions found', async () => { + (getUsersCountFromMentions as jest.Mock).mockResolvedValue(0); + + await persistentNotificationsConfirmation( + serverUrl, + value, + mentionsList, + intl, + sendMessage, + persistentNotificationMaxRecipients, + persistentNotificationInterval, + currentUserId, + channelName, + ); + + expect(Alert.alert).toHaveBeenCalledWith( + intl.formatMessage({ + id: 'persistent_notifications.error.no_mentions.title', + defaultMessage: 'Recipients must be @mentioned', + }), + intl.formatMessage({ + id: 'persistent_notifications.error.no_mentions.description', + defaultMessage: 'There are no recipients mentioned in your message. You’ll need add mentions to be able to send persistent notifications.', + }), + expect.any(Array), + ); + }); + + it('should show alert when mentions exceed max recipients', async () => { + (getUsersCountFromMentions as jest.Mock).mockResolvedValue(15); + + await persistentNotificationsConfirmation( + serverUrl, + value, + mentionsList, + intl, + sendMessage, + persistentNotificationMaxRecipients, + persistentNotificationInterval, + currentUserId, + channelName, + ); + + expect(Alert.alert).toHaveBeenCalledWith( + intl.formatMessage({ + id: 'persistent_notifications.error.max_recipients.title', + defaultMessage: 'Too many recipients', + }), + intl.formatMessage({ + id: 'persistent_notifications.error.max_recipients.description', + defaultMessage: 'You can send persistent notifications to a maximum of {max} recipients. There are {count} recipients mentioned in your message. You’ll need to change who you’ve mentioned before you can send.', + }, { + max: persistentNotificationMaxRecipients, + count: mentionsList.length, + }), + expect.any(Array), + ); + }); + + it('should show confirmation alert for valid mentions within limit', async () => { + (getUsersCountFromMentions as jest.Mock).mockResolvedValue(5); + + await persistentNotificationsConfirmation( + serverUrl, + value, + mentionsList, + intl, + sendMessage, + persistentNotificationMaxRecipients, + persistentNotificationInterval, + currentUserId, + channelName, + ); + + expect(Alert.alert).toHaveBeenCalledWith( + intl.formatMessage({ + id: 'persistent_notifications.confirm.title', + defaultMessage: 'Send persistent notifications', + }), + intl.formatMessage({ + id: 'persistent_notifications.confirm.description', + defaultMessage: 'Mentioned recipients will be notified every {interval, plural, one {minute} other {{interval} minutes}} until they’ve acknowledged or replied to the message.', + }, { + interval: persistentNotificationInterval, + }), + expect.any(Array), + ); + }); + }); + + describe('postUserDisplayName', () => { + it('should return the override username if from webhook and override is enabled', () => { + const post = { + props: { + from_webhook: 'true', + override_username: 'webhook_user', + }, + } as PostModel; + + const result = postUserDisplayName(post, undefined, undefined, true); + expect(result).toBe('webhook_user'); + }); + + it('should return the author’s display name if not from webhook or override is disabled', () => { + const post = { + props: { + from_webhook: 'false', + }, + } as PostModel; + const author = { + username: 'user1', + locale: 'en', + } as UserModel; + + const result = postUserDisplayName(post, author, undefined, false); + expect(result).toBe('user1'); + }); + + it('should return the author’s display name using the teammate name display', () => { + const post = { + props: { + from_webhook: 'false', + }, + } as PostModel; + const author = { + username: 'user1', + locale: 'en', + } as UserModel; + + const result = postUserDisplayName(post, author, 'nickname', false); + expect(result).toBe('user1'); + }); + }); + + describe('shouldIgnorePost', () => { + it('should return true if the post type is in the ignore list', () => { + const post = { + type: Post.POST_TYPES.CHANNEL_DELETED, + } as Post; + + const result = shouldIgnorePost(post); + expect(result).toBe(true); + }); + + it('should return false if the post type is not in the ignore list', () => { + const post = { + type: Post.POST_TYPES.EPHEMERAL, + } as Post; + + const result = shouldIgnorePost(post); + expect(result).toBe(false); + }); + }); + + describe('processPostsFetched', () => { + it('should process the fetched posts correctly', () => { + const data = { + order: ['post1', 'post2'], + posts: { + post1: {id: 'post1', message: 'First post'}, + post2: {id: 'post2', message: 'Second post'}, + }, + prev_post_id: 'post0', + } as unknown as PostResponse; + + const result = processPostsFetched(data); + expect(result).toEqual({ + posts: [ + {id: 'post1', message: 'First post'}, + {id: 'post2', message: 'Second post'}, + ], + order: ['post1', 'post2'], + previousPostId: 'post0', + }); + }); + }); + + describe('getLastFetchedAtFromPosts', () => { + it('should return the maximum timestamp from the posts', () => { + const posts = [ + {create_at: 1000, update_at: 2000, delete_at: 0}, + {create_at: 1500, update_at: 2500, delete_at: 3000}, + ] as Post[]; + + const result = getLastFetchedAtFromPosts(posts); + expect(result).toBe(3000); + }); + + it('should return 0 if no posts are provided', () => { + const result = getLastFetchedAtFromPosts(); + expect(result).toBe(0); + }); + }); + + describe('moreThan5minAgo', () => { + it('should return true if the time is more than 5 minutes ago', () => { + const time = Date.now() - toMilliseconds({minutes: 6}); + const result = moreThan5minAgo(time); + expect(result).toBe(true); + }); + + it('should return false if the time is within 5 minutes', () => { + const time = Date.now() - toMilliseconds({minutes: 4}); + const result = moreThan5minAgo(time); + expect(result).toBe(false); + }); }); }); diff --git a/app/utils/url/index.ts b/app/utils/url/index.ts index 52d4086c71a..def29fcbe4d 100644 --- a/app/utils/url/index.ts +++ b/app/utils/url/index.ts @@ -20,7 +20,7 @@ export function isValidUrl(url = '') { export function sanitizeUrl(url: string, useHttp = false) { let preUrl = urlParse(url, true); - let protocol = preUrl.protocol; + let protocol = useHttp ? 'http:' : preUrl.protocol; if (!preUrl.host || preUrl.protocol === 'file:') { preUrl = urlParse('https://' + stripTrailingSlashes(url), true); @@ -37,6 +37,18 @@ export function sanitizeUrl(url: string, useHttp = false) { ); } +export async function getUrlAfterRedirect(url: string, useHttp = false) { + const link = sanitizeUrl(url, useHttp); + try { + const result = await fetch(link, { + method: 'HEAD', + }); + return {url: result.url}; + } catch (error) { + return {error}; + } +} + export async function getServerUrlAfterRedirect(serverUrl: string, useHttp = false) { let url = sanitizeUrl(serverUrl, useHttp); diff --git a/app/utils/url/test.ts b/app/utils/url/test.ts index ba78b5a0c49..bdb16205a7e 100644 --- a/app/utils/url/test.ts +++ b/app/utils/url/test.ts @@ -437,7 +437,8 @@ describe('UrlUtils', () => { const onError = jest.fn(); const onSuccess = jest.fn(); - await UrlUtils.tryOpenURL(url, onError, onSuccess); + UrlUtils.tryOpenURL(url, onError, onSuccess); + await TestHelper.wait(100); expect(onError).not.toHaveBeenCalled(); expect(onSuccess).toHaveBeenCalledTimes(1); }); diff --git a/assets/base/i18n/ar.json b/assets/base/i18n/ar.json index 723c4b5c039..fc552a2f202 100644 --- a/assets/base/i18n/ar.json +++ b/assets/base/i18n/ar.json @@ -1,9 +1,24 @@ { "about.date": "تاريخ الاصدار:", + "about.enterpriseEditionLearn": "اعرف المزيد حول نسخة المؤسسات ", + "about.enterpriseEditionSt": "اتصلات حديثة من خلف جدار الحماية الخاص بك.", "about.enterpriseEditione1": "النسخة المؤسسية", + "about.teamEditionLearn": "انضم الى مجتمع ماترموست من خلال", + "about.teamEditionSt": "كل محادثات فريقك فى مكان واحد, بحث فورى وتستطيع الوصول له من اى مكان.", "about.teamEditiont0": "النسخة المحددة للفريق", "about.teamEditiont1": "النسخة المؤسسية", "account.logout": "تسجيل الخروج", "account.logout_from": "تسجيل الخروج من {serverName}", - "account.settings": "الاعدادات" + "account.settings": "الاعدادات", + "account.your_profile": "ملفك الشخصي", + "alert.channel_deleted.description": "تم أرشفة القناة {displayName}.", + "alert.channel_deleted.title": "قناة مؤرشفة", + "alert.push_proxy.button": "حسنا", + "alert.push_proxy_error.description": "بناء على اعدادات هذا السيرفر لا يمكن ارسال اشعارات عبر تطبيق الهاتف. تواصل مع مدير السيرفر للمزيد من المعلومات.", + "alert.push_proxy_error.title": "لا يمكن استقبال اشعارات من هذا الخادم", + "alert.push_proxy_unknown.description": "الخادم غير قادر على استقبال الاشعارات لسبب غير معروف. سيتم المحاولة مرة اخرى فالمرة القادمة التى تتصل بها بالخادم.", + "alert.push_proxy_unknown.title": "لا يمكن استلام اشعارات من الخادم", + "alert.removed_from_channel.description": "لقد تمت إذالتك من القناة {displayName}.", + "alert.removed_from_channel.title": "تمت إذالته من القناة", + "alert.removed_from_team.description": "تمت إذالتك من الفريق {displayName}." } diff --git a/assets/base/i18n/de.json b/assets/base/i18n/de.json index bf1a1c35e64..02a016b29f4 100644 --- a/assets/base/i18n/de.json +++ b/assets/base/i18n/de.json @@ -96,6 +96,28 @@ "camera_type.video.option": "Video aufnehmen", "center_panel.archived.closeChannel": "Kanal schließen", "channel_add_members.add_members.button": "Mitglieder hinzufügen", + "channel_bookmark.add.detail_title": "Titel", + "channel_bookmark.add.emoji": "Emoji hinzufügen", + "channel_bookmark.add.failed_title": "Fehler beim Hinzufügen eines Lesezeichens", + "channel_bookmark.add.file_cancel": "Abbrechen", + "channel_bookmark.add.file_title": "Anhang", + "channel_bookmark.add.file_upload_error": "Fehler beim Hochladen der Datei. Bitte versuche es erneut.", + "channel_bookmark.add.file_uploading": "Hochladen... ({progress}%)", + "channel_bookmark.add_edit.failed_desc": "Details: {error}", + "channel_bookmark.copy_option": "Link kopieren", + "channel_bookmark.delete.confirm": "Willst du das Lesezeichen {displayName} wirklich löschen?", + "channel_bookmark.delete.confirm_title": "Lesezeichen löschen", + "channel_bookmark.delete.failed_detail": "Details: {error}", + "channel_bookmark.delete.failed_title": "Fehler beim Löschen des Lesezeichens", + "channel_bookmark.delete.yes": "Ja", + "channel_bookmark.delete_option": "Löschen", + "channel_bookmark.edit.failed_title": "Fehler beim Bearbeiten des Lesezeichens", + "channel_bookmark.edit.save_button": "Speichern", + "channel_bookmark.edit_option": "Bearbeiten", + "channel_bookmark.share_option": "Teilen", + "channel_bookmark_add.link": "Link", + "channel_bookmark_add.link.input.description": "Füge einen Link zu einer beliebigen Nachricht, einer Datei oder einem externen Link hinzu", + "channel_bookmark_add.link.invalid": "Bitte gib einen gültigen Link ein", "channel_files.empty.paragraph": "Dateien, die in diesem Kanal gepostet wurden, werden hier angezeigt.", "channel_files.empty.title": "Noch keine Dateien", "channel_files.noFiles.paragraph": "Dieser Kanal enthält keine Dateien mit den angewandten Filtern", @@ -103,6 +125,10 @@ "channel_header.directchannel.you": "{displayName} (Du)", "channel_header.info": "Zeige Info", "channel_header.member_count": "{count, plural, one {# Teilnehmer} other {# Teilnehmer}}", + "channel_info.add_bookmark": "Lesezeichen hinzufügen", + "channel_info.add_bookmark.file": "Datei anhängen", + "channel_info.add_bookmark.link": "Link hinzufügen", + "channel_info.add_bookmark.max_reached": "Dieser Kanal hat die maximale Anzahl an Lesezeichen erreicht.", "channel_info.add_members": "Mitglieder hinzufügen", "channel_info.alertNo": "Nein", "channel_info.alertYes": "Ja", @@ -460,6 +486,7 @@ "mobile.calls_ended_at": "Beendet am", "mobile.calls_error_message": "Fehler: {error}", "mobile.calls_error_title": "Fehler", + "mobile.calls_group_calls_not_available": "Anrufe sind nur in Direktnachrichten möglich.", "mobile.calls_headset": "Kopfhörer", "mobile.calls_hide_cc": "Live-Untertitel ausblenden", "mobile.calls_host": "Gastgeber", @@ -676,6 +703,8 @@ "mobile.no_results_with_term.messages": "Keine Treffer für \"{term}\" gefunden", "mobile.oauth.failed_to_login": "Dein Anmeldeversuch ist fehlgeschlagen. Bitte nochmal versuchen.", "mobile.oauth.something_wrong.okButton": "OK", + "mobile.oauth.success.description": "Melde jetzt an, nur einen Moment...", + "mobile.oauth.success.title": "Authentifizierung erfolgreich", "mobile.oauth.switch_to_browser": "Du wirst zu deinem Login-Anbieter weitergeleitet", "mobile.oauth.switch_to_browser.error_title": "Fehler bei der Anmeldung", "mobile.oauth.switch_to_browser.title": "Weiterleitung...", @@ -963,6 +992,8 @@ "screen.search.results.filter.title": "Nach Dateityp filtern", "screen.search.results.filter.videos": "Videos", "screen.search.title": "Suche", + "screens.channel_bookmark_add": "Lesezeichen hinzufügen", + "screens.channel_bookmark_edit": "Lesezeichen bearbeiten", "screens.channel_edit": "Kanal bearbeiten", "screens.channel_edit_header": "Kanalüberschrift bearbeiten", "screens.channel_info": "Kanal-Infos", @@ -976,6 +1007,7 @@ "select_team.title": "Wähle ein Team", "server.invalid.certificate.description": "Das Zertifikat für diesen Server ist ungültig.\nMöglicherweise stellen Sie eine Verbindung zu einem Server her, der vorgibt, \"{hostname}\" zu sein, wodurch Ihre vertraulichen Daten gefährdet werden könnten.", "server.invalid.certificate.title": "Ungültiges SSL-Zertifikat", + "server.invalid.pinning.title": "Ungültiges gepinntes SSL Zertifikat", "server.logout.alert_description": "Alle zugehörigen Daten werden entfernt", "server.logout.alert_title": "Bist du sicher, dass du dich bei {displayName} abmelden möchtest?", "server.remove.alert_description": "Dadurch wird er aus deiner Serverliste entfernt. Alle zugehörigen Daten werden entfernt", @@ -1080,6 +1112,7 @@ "suggestion.mention.groups": "Gruppenerwähnungen", "suggestion.mention.here": "Benachrichtigt jeden in diesem Kanal", "suggestion.mention.morechannels": "Andere Kanäle", + "suggestion.mention.nonmembers": "Nicht im Kanal", "suggestion.mention.special": "Spezielle Erwähnungen", "suggestion.mention.users": "Benutzer", "suggestion.search.direct": "Direktnachricht", diff --git a/assets/base/i18n/en.json b/assets/base/i18n/en.json index daaece327dc..27e7df620fe 100644 --- a/assets/base/i18n/en.json +++ b/assets/base/i18n/en.json @@ -96,6 +96,28 @@ "camera_type.video.option": "Record Video", "center_panel.archived.closeChannel": "Close Channel", "channel_add_members.add_members.button": "Add Members", + "channel_bookmark_add.link": "Link", + "channel_bookmark_add.link.input.description": "Add a link to any post, file, or any external link", + "channel_bookmark_add.link.invalid": "Please enter a valid link", + "channel_bookmark.add_edit.failed_desc": "Details: {error}", + "channel_bookmark.add.detail_title": "Title", + "channel_bookmark.add.emoji": "Add emoji", + "channel_bookmark.add.failed_title": "Error adding bookmark", + "channel_bookmark.add.file_cancel": "Cancel", + "channel_bookmark.add.file_title": "Attachment", + "channel_bookmark.add.file_upload_error": "Error uploading file. Please try again.", + "channel_bookmark.add.file_uploading": "Uploading... ({progress}%)", + "channel_bookmark.copy_option": "Copy Link", + "channel_bookmark.delete_option": "Delete", + "channel_bookmark.delete.confirm": "You sure want to delete the bookmark {displayName}?", + "channel_bookmark.delete.confirm_title": "Delete bookmark", + "channel_bookmark.delete.failed_detail": "Details: {error}", + "channel_bookmark.delete.failed_title": "Error deleting bookmark", + "channel_bookmark.delete.yes": "Yes", + "channel_bookmark.edit_option": "Edit", + "channel_bookmark.edit.failed_title": "Error editing bookmark", + "channel_bookmark.edit.save_button": "Save", + "channel_bookmark.share_option": "Share", "channel_files.empty.paragraph": "Files posted in this channel will show here.", "channel_files.empty.title": "No files yet", "channel_files.noFiles.paragraph": "This channel doesn't contain any files with the applied filters", @@ -103,6 +125,10 @@ "channel_header.directchannel.you": "{displayName} (you)", "channel_header.info": "View info", "channel_header.member_count": "{count, plural, one {# member} other {# members}}", + "channel_info.add_bookmark": "Add a bookmark", + "channel_info.add_bookmark.file": "Attach a file", + "channel_info.add_bookmark.link": "Add a link", + "channel_info.add_bookmark.max_reached": "This channel has reached the maximum number of bookmarks.", "channel_info.add_members": "Add members", "channel_info.alert_retry": "Try Again", "channel_info.alertNo": "No", @@ -460,6 +486,7 @@ "mobile.calls_ended_at": "Ended at", "mobile.calls_error_message": "Error: {error}", "mobile.calls_error_title": "Error", + "mobile.calls_group_calls_not_available": "Calls are only available in DM channels.", "mobile.calls_headset": "Headset", "mobile.calls_hide_cc": "Hide live captions", "mobile.calls_host": "host", @@ -676,6 +703,8 @@ "mobile.no_results.spelling": "Check the spelling or try another search.", "mobile.oauth.failed_to_login": "Your login attempt failed. Please try again.", "mobile.oauth.something_wrong.okButton": "OK", + "mobile.oauth.success.description": "Signing in now, just a moment...", + "mobile.oauth.success.title": "Authentication successful", "mobile.oauth.switch_to_browser": "You are being redirected to your login provider", "mobile.oauth.switch_to_browser.error_title": "Sign in error", "mobile.oauth.switch_to_browser.title": "Redirecting...", @@ -963,6 +992,8 @@ "screen.search.results.filter.title": "Filter by file type", "screen.search.results.filter.videos": "Videos", "screen.search.title": "Search", + "screens.channel_bookmark_add": "Add a bookmark", + "screens.channel_bookmark_edit": "Edit bookmark", "screens.channel_edit": "Edit Channel", "screens.channel_edit_header": "Edit Channel Header", "screens.channel_info": "Channel Info", @@ -981,6 +1012,7 @@ "server_upgrade.learn_more": "Learn More", "server.invalid.certificate.description": "The certificate for this server is invalid.\nYou might be connecting to a server that is pretending to be “{hostname}” which could put your confidential information at risk.", "server.invalid.certificate.title": "Invalid SSL certificate", + "server.invalid.pinning.title": "Invalid pinned SSL certificate", "server.logout.alert_description": "All associated data will be removed", "server.logout.alert_title": "Are you sure you want to log out of {displayName}?", "server.remove.alert_description": "This will remove it from your list of servers. All associated data will be removed", diff --git a/assets/base/i18n/en_AU.json b/assets/base/i18n/en_AU.json index 3617466ef69..0db30908dd2 100644 --- a/assets/base/i18n/en_AU.json +++ b/assets/base/i18n/en_AU.json @@ -96,6 +96,28 @@ "camera_type.video.option": "Record Video", "center_panel.archived.closeChannel": "Close Channel", "channel_add_members.add_members.button": "Add Members", + "channel_bookmark.add.detail_title": "Title", + "channel_bookmark.add.emoji": "Add emoji", + "channel_bookmark.add.failed_title": "Error adding bookmark", + "channel_bookmark.add.file_cancel": "Cancel", + "channel_bookmark.add.file_title": "Attachment", + "channel_bookmark.add.file_upload_error": "Error uploading file. Please try again.", + "channel_bookmark.add.file_uploading": "Uploading... ({progress}%)", + "channel_bookmark.add_edit.failed_desc": "Details: {error}", + "channel_bookmark.copy_option": "Copy Link", + "channel_bookmark.delete.confirm": "You sure want to delete the bookmark {displayName}?", + "channel_bookmark.delete.confirm_title": "Delete bookmark", + "channel_bookmark.delete.failed_detail": "Details: {error}", + "channel_bookmark.delete.failed_title": "Error deleting bookmark", + "channel_bookmark.delete.yes": "Yes", + "channel_bookmark.delete_option": "Delete", + "channel_bookmark.edit.failed_title": "Error editing bookmark", + "channel_bookmark.edit.save_button": "Save", + "channel_bookmark.edit_option": "Edit", + "channel_bookmark.share_option": "Share", + "channel_bookmark_add.link": "Link", + "channel_bookmark_add.link.input.description": "Add a link to any post, file or any external link", + "channel_bookmark_add.link.invalid": "Please enter a valid link", "channel_files.empty.paragraph": "Files posted in this channel will show here.", "channel_files.empty.title": "No files yet", "channel_files.noFiles.paragraph": "This channel doesn't contain any files with the applied filters", @@ -103,6 +125,10 @@ "channel_header.directchannel.you": "{displayName} (you)", "channel_header.info": "View info", "channel_header.member_count": "{count, plural, one {# member} other {# members}}", + "channel_info.add_bookmark": "Add a bookmark", + "channel_info.add_bookmark.file": "Attach a file", + "channel_info.add_bookmark.link": "Add a link", + "channel_info.add_bookmark.max_reached": "This channel has reached the maximum number of bookmarks.", "channel_info.add_members": "Add members", "channel_info.alertNo": "No", "channel_info.alertYes": "Yes", @@ -460,6 +486,7 @@ "mobile.calls_ended_at": "Ended at", "mobile.calls_error_message": "Error: {error}", "mobile.calls_error_title": "Error", + "mobile.calls_group_calls_not_available": "Calls are only available in DM channels.", "mobile.calls_headset": "Headset", "mobile.calls_hide_cc": "Hide live captions", "mobile.calls_host": "host", @@ -796,6 +823,16 @@ "notification_settings.auto_responder.footer.message": "Set a custom message that is automatically sent in response to direct messages, such as an out of office or holiday reply. Enabling this setting changes your status to Out of Office and disables notifications.", "notification_settings.auto_responder.message": "Message", "notification_settings.auto_responder.to.enable": "Enable automatic replies", + "notification_settings.call_notification": "Call Notifications", + "notification_settings.calls": "Call Notifications", + "notification_settings.calls.callsInfo": "Note: silent mode must be off to hear the ringtone preview.", + "notification_settings.calls.calm": "Calm", + "notification_settings.calls.cheerful": "Cheerful", + "notification_settings.calls.dynamic": "Dynamic", + "notification_settings.calls.enable_sound": "Notification sound for incoming calls", + "notification_settings.calls.urgent": "Urgent", + "notification_settings.calls_off": "Off", + "notification_settings.calls_on": "On", "notification_settings.email": "Email Notifications", "notification_settings.email.crt.emailInfo": "When enabled, any reply to a thread you're following will send an email notification", "notification_settings.email.crt.send": "Thread reply notifications", @@ -953,6 +990,8 @@ "screen.search.results.filter.title": "Filter by file type", "screen.search.results.filter.videos": "Videos", "screen.search.title": "Search", + "screens.channel_bookmark_add": "Add a bookmark", + "screens.channel_bookmark_edit": "Edit bookmark", "screens.channel_edit": "Edit Channel", "screens.channel_edit_header": "Edit Channel Header", "screens.channel_info": "Channel Info", @@ -966,6 +1005,7 @@ "select_team.title": "Select a team", "server.invalid.certificate.description": "The certificate for this server is invalid.\nYou might be connecting to a server that is pretending to be \"{hostname}\" which could put your confidential information at risk.", "server.invalid.certificate.title": "Invalid SSL certificate", + "server.invalid.pinning.title": "Invalid pinned SSL certificate", "server.logout.alert_description": "All associated data will be removed", "server.logout.alert_title": "Are you sure you want to log out of {displayName}?", "server.remove.alert_description": "This will remove it from your list of servers. All associated data will be removed.", @@ -1070,6 +1110,7 @@ "suggestion.mention.groups": "Group Mentions", "suggestion.mention.here": "Notifies everyone online in this channel", "suggestion.mention.morechannels": "Other Channels", + "suggestion.mention.nonmembers": "Not in Channel", "suggestion.mention.special": "Special Mentions", "suggestion.mention.users": "Users", "suggestion.search.direct": "Direct Messages", diff --git a/assets/base/i18n/hr.json b/assets/base/i18n/hr.json index c66e67ac3fe..db5f695bd9d 100644 --- a/assets/base/i18n/hr.json +++ b/assets/base/i18n/hr.json @@ -424,7 +424,7 @@ "settings.about.server.version": "Verzija servera: {version} (izgradnja {buildNumber}", "settings.about.server.version.noBuild": "Verzija servera: {version}", "settings.about.server.version.title": "Verzija servera:", - "settings.about.server.version.value": "{version} (izgradnja {number})", + "settings.about.server.version.value": "{version} (izgradnja {buildNumber})", "settings.advanced.cancel": "Odustani", "settings.advanced.delete": "Izbriši", "settings.advanced.delete_data": "Izbriši lokalne datoteke", diff --git a/assets/base/i18n/it.json b/assets/base/i18n/it.json index 156af96b97e..75cf5407174 100644 --- a/assets/base/i18n/it.json +++ b/assets/base/i18n/it.json @@ -96,6 +96,20 @@ "camera_type.video.option": "Registrazione video", "center_panel.archived.closeChannel": "Chiudi Canale", "channel_add_members.add_members.button": "Aggiungi membri", + "channel_bookmark.add.detail_title": "Titolo", + "channel_bookmark.add.emoji": "Aggiungi emoji", + "channel_bookmark.add.file_cancel": "Annulla", + "channel_bookmark.add.file_title": "Allegato", + "channel_bookmark.add.file_upload_error": "Errore durante il caricamento. Riprova più tardi.", + "channel_bookmark.add_edit.failed_desc": "Dettagli:{error}", + "channel_bookmark.copy_option": "Copia il Collegamento", + "channel_bookmark.delete.failed_detail": "Dettagli: {error}", + "channel_bookmark.delete.yes": "Si", + "channel_bookmark.delete_option": "Cancella", + "channel_bookmark.edit.save_button": "Salva", + "channel_bookmark.edit_option": "Modifica", + "channel_bookmark.share_option": "Condividi", + "channel_bookmark_add.link": "Collegamento", "channel_files.empty.paragraph": "I file inviati in questo canale saranno mostrati qui.", "channel_files.empty.title": "Ancora nessun file", "channel_files.noFiles.paragraph": "Questo canale non contiene alcun file con i filtri applicati", @@ -103,6 +117,8 @@ "channel_header.directchannel.you": "{displayName} (tu)", "channel_header.info": "Visualizza info", "channel_header.member_count": "{count, plural, one {# membro} other {# membri}}", + "channel_info.add_bookmark.file": "Allega un file", + "channel_info.add_bookmark.link": "Aggiungi un collegamento", "channel_info.add_members": "Aggiungi membri", "channel_info.alertNo": "No", "channel_info.alertYes": "Sì", @@ -119,6 +135,10 @@ "channel_info.close_gm": "Chiudere il messaggio di gruppo", "channel_info.close_gm_channel": "Siete sicuri di voler chiudere questo messaggio di gruppo? In questo modo lo si rimuove dalla schermata iniziale, ma è sempre possibile riaprirlo.", "channel_info.convert_failed": "Non siamo riusciti a convertire {displayName} in un canale privato.", + "channel_info.convert_gm_to_channel": "Converti in un Canale Privato", + "channel_info.convert_gm_to_channel.button_text": "Converti in Canale Privato", + "channel_info.convert_gm_to_channel.button_text_converting": "Conversione...", + "channel_info.convert_gm_to_channel.screen_title": "Converti in Canale Privato", "channel_info.convert_private": "Convertire in canale privato", "channel_info.convert_private_description": "Quando si converte {displayName} in un canale privato, la cronologia e l'iscrizione vengono conservate. I file condivisi pubblicamente rimangono accessibili a chiunque abbia il link. L'iscrizione a un canale privato è solo su invito.\n\nLa modifica è permanente e non può essere annullata.\n\nSei sicuro di voler convertire {displayName} in un canale privato?", "channel_info.convert_private_success": "{displayName} è ora un canale privato.", @@ -319,6 +339,7 @@ "general_settings.help": "Aiuto", "general_settings.notifications": "Notifiche", "general_settings.report_problem": "Segnala un problema", + "generic.back": "Indietro", "get_post_link_modal.title": "Copia collegamento", "global_threads.allThreads": "Tutti i tuoi thread", "global_threads.emptyThreads.message": "Tutte le discussioni in cui si è menzionati o a cui si è partecipato verranno visualizzate qui, insieme a tutte le discussioni che si sono seguite.", @@ -355,6 +376,7 @@ "invite.send_error": "Si è verificato un errore durante il tentativo di invio degli inviti. Controllare la connessione di rete e riprovare.", "invite.send_invite": "Inviare", "invite.shareLink": "Condividi il link", + "invite.summary.back": "Torna indietro", "invite.summary.done": "Fatto", "invite.summary.email_invite": "È stata inviata un'e-mail di invito", "invite.summary.error": "{invitationsCount, plural, one {Invitation} other {Invitations}} non è stato possibile inviarlo con successo.", @@ -413,9 +435,12 @@ "mobile.android.photos_permission_denied_description": "Caricare le foto sul server o salvarle sul dispositivo. Aprire le Impostazioni per concedere a {applicationName} l'accesso in lettura e scrittura alla libreria fotografica.", "mobile.android.photos_permission_denied_title": "{applicationName} vorrebbe accedere alle tue foto", "mobile.announcement_banner.title": "Annuncio", + "mobile.calls_audio_device": "Seleziona dispositivo audio", + "mobile.calls_bluetooth": "Bluetooth", "mobile.calls_call_ended": "Chiamata terminata", "mobile.calls_call_screen": "Chiamata", "mobile.calls_call_thread": "Thread chiamata", + "mobile.calls_cancel": "Annulla", "mobile.calls_disable": "Disattivare le chiamate", "mobile.calls_dismiss": "Congedo", "mobile.calls_enable": "Abilitazione delle chiamate", @@ -429,6 +454,8 @@ "mobile.calls_error_message": "Errore: {error}", "mobile.calls_error_title": "Errore", "mobile.calls_host": "ospite", + "mobile.calls_host_end_confirm": "Termina la chiamata per tutti", + "mobile.calls_host_leave_confirm": "Lascia la chiamata", "mobile.calls_host_rec": "Stai registrando la riunione. Informa tutti i partecipanti che questa riunione viene registrata.", "mobile.calls_host_rec_error": "Provare a registrare di nuovo. È inoltre possibile contattare l'amministratore del sistema per ottenere assistenza nella risoluzione dei problemi.", "mobile.calls_host_rec_error_title": "Si è verificato un problema nella registrazione", diff --git a/assets/base/i18n/ja.json b/assets/base/i18n/ja.json index 8074f74cc5f..203f1783b4e 100644 --- a/assets/base/i18n/ja.json +++ b/assets/base/i18n/ja.json @@ -96,6 +96,28 @@ "camera_type.video.option": "動画を撮影する", "center_panel.archived.closeChannel": "チャンネルを閉じる", "channel_add_members.add_members.button": "メンバーを追加する", + "channel_bookmark.add.detail_title": "タイトル", + "channel_bookmark.add.emoji": "絵文字を追加", + "channel_bookmark.add.failed_title": "ブックマーク追加時にエラーが発生しました", + "channel_bookmark.add.file_cancel": "キャンセル", + "channel_bookmark.add.file_title": "添付", + "channel_bookmark.add.file_upload_error": "ファイルアップロード時にエラーが発生しました。再度試してください。", + "channel_bookmark.add.file_uploading": "アップロード中... ({progress}%)", + "channel_bookmark.add_edit.failed_desc": "詳細: {error}", + "channel_bookmark.copy_option": "リンクをコピー", + "channel_bookmark.delete.confirm": "本当にブックマーク {displayName} を削除しますか?", + "channel_bookmark.delete.confirm_title": "ブックマークを削除", + "channel_bookmark.delete.failed_detail": "詳細: {error}", + "channel_bookmark.delete.failed_title": "ブックマーク削除時にエラーが発生しました", + "channel_bookmark.delete.yes": "はい", + "channel_bookmark.delete_option": "削除", + "channel_bookmark.edit.failed_title": "ブックマーク編集時にエラーが発生しました", + "channel_bookmark.edit.save_button": "保存", + "channel_bookmark.edit_option": "編集", + "channel_bookmark.share_option": "共有", + "channel_bookmark_add.link": "リンク", + "channel_bookmark_add.link.input.description": "投稿、ファイル、外部サイトへのリンクを追加してください", + "channel_bookmark_add.link.invalid": "有効なリンクを入力してください", "channel_files.empty.paragraph": "このチャンネルに投稿されたファイルがここに表示されます。", "channel_files.empty.title": "まだファイルはありません", "channel_files.noFiles.paragraph": "このチャンネルには、適用されたフィルタに合致するファイルがありません", @@ -103,6 +125,10 @@ "channel_header.directchannel.you": "{displayName} (あなた)", "channel_header.info": "情報を表示する", "channel_header.member_count": "{count, plural, one {# メンバー} other {# メンバー}}", + "channel_info.add_bookmark": "ブックマークを追加", + "channel_info.add_bookmark.file": "ファイルを添付", + "channel_info.add_bookmark.link": "リンクを追加", + "channel_info.add_bookmark.max_reached": "このチャンネルのブックマーク数が上限に達しました。", "channel_info.add_members": "メンバーを追加する", "channel_info.alertNo": "いいえ", "channel_info.alertYes": "はい", @@ -460,6 +486,7 @@ "mobile.calls_ended_at": "終了時刻", "mobile.calls_error_message": "エラー: {error}", "mobile.calls_error_title": "エラー", + "mobile.calls_group_calls_not_available": "通話はDMチャンネルでのみ利用できます。", "mobile.calls_headset": "ヘッドセット", "mobile.calls_hide_cc": "ライブキャプションを非表示にする", "mobile.calls_host": "ホスト", @@ -676,6 +703,8 @@ "mobile.no_results_with_term.messages": "\"{term}\"に該当する情報がありませんでした", "mobile.oauth.failed_to_login": "ログインできませんでした。再度試してみてください。", "mobile.oauth.something_wrong.okButton": "OK", + "mobile.oauth.success.description": "サインイン中です。少々お待ちください...", + "mobile.oauth.success.title": "認証に成功しました", "mobile.oauth.switch_to_browser": "ログインプロバイダにリダイレクトされています", "mobile.oauth.switch_to_browser.error_title": "サインインエラー", "mobile.oauth.switch_to_browser.title": "リダイレクト中...", @@ -963,6 +992,8 @@ "screen.search.results.filter.title": "ファイル形式で絞り込む", "screen.search.results.filter.videos": "動画", "screen.search.title": "検索", + "screens.channel_bookmark_add": "ブックマークを追加する", + "screens.channel_bookmark_edit": "ブックマークを編集", "screens.channel_edit": "チャンネルを編集する", "screens.channel_edit_header": "チャンネルヘッダを編集", "screens.channel_info": "チャンネル情報", @@ -976,6 +1007,7 @@ "select_team.title": "チームを選択する", "server.invalid.certificate.description": "このサーバーの証明書は無効です。\n\"{hostname}\" を装ったサーバーに接続しようとしている可能性があり、機密情報が危険にさらされる可能性があります。", "server.invalid.certificate.title": "無効なSSL証明書", + "server.invalid.pinning.title": "不正なピン留めされたSSL証明書", "server.logout.alert_description": "関連するデータはすべて削除されます", "server.logout.alert_title": "本当に {displayName} からログアウトしますか?", "server.remove.alert_description": "これにより、サーバーのリストから削除されます。関連するデータはすべて削除されます", @@ -1080,6 +1112,7 @@ "suggestion.mention.groups": "グループメンション", "suggestion.mention.here": "このチャンネルの現在オンラインの人に通知", "suggestion.mention.morechannels": "他のチャンネル", + "suggestion.mention.nonmembers": "チャンネルにいません", "suggestion.mention.special": "特殊なメンション", "suggestion.mention.users": "ユーザー", "suggestion.search.direct": "ダイレクトメッセージ", diff --git a/assets/base/i18n/nb_NO.json b/assets/base/i18n/nb_NO.json index 42d8143bdf6..6a275fb21ca 100644 --- a/assets/base/i18n/nb_NO.json +++ b/assets/base/i18n/nb_NO.json @@ -95,12 +95,38 @@ "camera_type.video.option": "Ta opp video", "center_panel.archived.closeChannel": "Lukk kanal", "channel_add_members.add_members.button": "Legg til medlemmer", + "channel_bookmark.add.detail_title": "Tittel", + "channel_bookmark.add.emoji": "Legg til emoji", + "channel_bookmark.add.failed_title": "Feil ved å legge til bokmerke", + "channel_bookmark.add.file_cancel": "Avbryt", + "channel_bookmark.add.file_title": "Vedlegg", + "channel_bookmark.add.file_upload_error": "Feil ved opplasting av fil. Vennligst prøv igjen.", + "channel_bookmark.add.file_uploading": "Laster opp ... ({progress}%)", + "channel_bookmark.add_edit.failed_desc": "Detaljer: {error}", + "channel_bookmark.copy_option": "Kopier lenke", + "channel_bookmark.delete.confirm": "Er du sikker på at du vil slette bokmerket {displayName}?", + "channel_bookmark.delete.confirm_title": "Slett bokmerke", + "channel_bookmark.delete.failed_detail": "Detaljer: {error}", + "channel_bookmark.delete.failed_title": "Feil ved sletting av bokmerke", + "channel_bookmark.delete.yes": "Ja", + "channel_bookmark.delete_option": "Slett", + "channel_bookmark.edit.failed_title": "Feil ved redigering av bokmerke", + "channel_bookmark.edit.save_button": "Lagre", + "channel_bookmark.edit_option": "Rediger", + "channel_bookmark.share_option": "Dele", + "channel_bookmark_add.link": "Lenke", + "channel_bookmark_add.link.input.description": "Legg til en lenke til ethvert innlegg, fil eller ekstern lenke", + "channel_bookmark_add.link.invalid": "Vennligst skriv inn en gyldig lenke", "channel_files.empty.paragraph": "Filer som er lagt ut i denne kanalen vil vises her.", "channel_files.empty.title": "Ingen filer ennå", "channel_files.noFiles.paragraph": "Denne kanalen inneholder ingen filer med de brukte filtrene", "channel_files.noFiles.title": "Ingen filer funnet", "channel_header.directchannel.you": "{displayName} (deg)", "channel_header.info": "Vis info", + "channel_info.add_bookmark": "Legg til et bokmerke", + "channel_info.add_bookmark.file": "Legg ved en fil", + "channel_info.add_bookmark.link": "Legg til en lenke", + "channel_info.add_bookmark.max_reached": "Denne kanalen har nådd maksimalt antall bokmerker.", "channel_info.add_members": "Legg til medlemmer", "channel_info.alertNo": "Nei", "channel_info.alertYes": "Ja", @@ -131,6 +157,7 @@ "channel_info.convert_gm_to_channel.warning.no_teams.header": "Kan ikke konvertere til en kanal fordi gruppemedlemmer er en del av forskjellige team", "channel_info.convert_private": "Konverter til privat kanal", "channel_info.convert_private_success": "{displayName} er nå en privat kanal.", + "channel_info.convert_private_title": "Konverter {displayName} til en privat kanal?", "channel_info.copied": "Kopiert", "channel_info.copy_link": "Kopier lenke", "channel_info.copy_purpose_text": "Kopier formålstekst", @@ -140,6 +167,7 @@ "channel_info.favorite": "Favoritt", "channel_info.favorited": "Favorittmarkert", "channel_info.header": "Overskrift:", + "channel_info.ignore_mentions": "Ignorer @channel, @here, @all", "channel_info.leave": "Forlat", "channel_info.leave_channel": "Forlat kanal", "channel_info.leave_private_channel": "Er du sikker på at du vil forlate den private kanalen {displayName}? Du kan ikke bli med på kanalen igjen med mindre du blir invitert på nytt.", @@ -155,6 +183,7 @@ "channel_info.notification.mention": "Omtaler", "channel_info.notification.none": "Aldri", "channel_info.pinned_messages": "Festede meldinger", + "channel_info.position": "Posisjon", "channel_info.private_channel": "Privat kanal", "channel_info.public_channel": "Åpen kanal", "channel_info.send_a_mesasge": "Send melding", @@ -235,6 +264,7 @@ "create_post.deactivated": "Du ser på en arkivert kanal med en deaktivert bruker.", "create_post.thread_reply": "Svar på denne tråden...", "create_post.write": "Skriv til {channelDisplayName}", + "custom_status.expiry.at": "på", "custom_status.expiry.until": "Før", "custom_status.expiry_dropdown.custom": "Egendefinert", "custom_status.expiry_dropdown.date_and_time": "Dato og tid", @@ -261,8 +291,10 @@ "display_settings.clock.military": "24 timer", "display_settings.clock.standard": "12 timer", "display_settings.clockDisplay": "Klokkevisning", + "display_settings.crt": "Skjulte svartråder", "display_settings.crt.off": "Av", "display_settings.crt.on": "På", + "display_settings.theme": "Tema", "display_settings.timezone": "Tidssone", "display_settings.tz.auto": "Auto", "display_settings.tz.manual": "Manuelt", @@ -270,6 +302,12 @@ "edit_post.editPost": "Rediger innlegget...", "edit_post.save": "Lagre", "edit_server.description": "Angi et visningsnavn for denne serveren", + "edit_server.display_help": "Server: {url}", + "edit_server.save": "Lagre", + "edit_server.saving": "Lagrer", + "edit_server.title": "Rediger navn på server", + "emoji_picker.activities": "Aktiviteter", + "emoji_picker.animals-nature": "Dyr og natur", "emoji_picker.custom": "Egendefinert", "emoji_picker.flags": "Flagg", "emoji_picker.food-drink": "Mat og drikke", @@ -283,6 +321,7 @@ "emoji_skin.dark_skin_tone": "mørk hudfarge", "emoji_skin.default": "standard hudfarge", "emoji_skin.light_skin_tone": "lys hudfarge", + "emoji_skin.medium_dark_skin_tone": "middels mørk hudtone", "emoji_skin.medium_light_skin_tone": "middels lys hudfarge", "emoji_skin.medium_skin_tone": "middels hudfarge", "extension.no_memberships.description": "For å dele innhold, må du være medlem av et team på en Mattermost-server.", @@ -370,6 +409,7 @@ "invite.summary.smtp_failure": "SMTP er ikke konfigurert i System Console", "invite.summary.try_again": "Prøv igjen", "invite.title": "Inviter", + "invite.title.summary": "Invitasjonssammendrag", "invite_people_to_team.message": "Her er en lenke for å samarbeide og kommunisere med oss på Mattermost.", "invite_people_to_team.title": "Bli med i {team}-teamet", "join_team.error.group_error": "Du må være medlem av en tilknyttet gruppe for å bli med i dette teamet.", @@ -744,6 +784,11 @@ "notification_settings.auto_responder.footer.message": "Sett opp en egendefinert melding som automatisk sendes som svar på direktemeldinger, for eksempel fravær eller feriesvar. Aktivering av denne innstillingen endrer statusen din til Ikke på kontoret og deaktiverer varsler.", "notification_settings.auto_responder.message": "Melding", "notification_settings.auto_responder.to.enable": "Aktiver automatiske svar", + "notification_settings.calls.calm": "Rolig", + "notification_settings.calls.cheerful": "Munter", + "notification_settings.calls.dynamic": "Dynamisk", + "notification_settings.calls.enable_sound": "Varslingslyd for innkommende samtaler", + "notification_settings.calls.urgent": "Haster", "notification_settings.email": "Varsler på e-post", "notification_settings.email.crt.emailInfo": "Når aktivert, vil ethvert svar på en tråd du følger sende et varsel på e-post", "notification_settings.email.crt.send": "Varsler om trådsvar", @@ -814,8 +859,10 @@ "permalink.show_dialog_warn.join": "Bli med", "permalink.show_dialog_warn.title": "Bli med på privat kanal", "persistent_notifications.confirm.cancel": "Avbryt", + "persistent_notifications.confirm.description": "Nevnte mottakere vil bli varslet hvert {interval, plural, one {minute} other {{interval} minutes}} til de har bekreftet eller svart på meldingen.", "persistent_notifications.confirm.send": "Send", "persistent_notifications.confirm.title": "Send vedvarende varsler", + "persistent_notifications.dm_channel.description": "{username} vil bli varslet hvert {intervall, plural, one {minute} other {{interval} minutes}} til de har bekreftet eller svart på meldingen.", "persistent_notifications.error.max_recipients.description": "Du kan sende vedvarende varsler til maksimalt {max} mottakere. Det er {count} mottakere nevnt i meldingen din. Du må endre hvem du har nevnt før du kan sende.", "persistent_notifications.error.max_recipients.title": "For mange mottakere", "persistent_notifications.error.no_mentions.description": "Det er ingen mottakere nevnt i meldingen din. Du må legge til omtaler for å kunne sende vedvarende varsler.", @@ -833,9 +880,13 @@ "postList.scrollToBottom.newMessages": "Nye meldinger", "postList.scrollToBottom.newReplies": "Nye svar", "post_body.check_for_out_of_channel_groups_mentions.message": "ble ikke varslet av denne omtalen fordi de ikke er i kanalen. De er heller ikke medlem av gruppene som er knyttet til denne kanalen.", + "post_body.check_for_out_of_channel_mentions.link.and": " and ", "post_body.check_for_out_of_channel_mentions.link.private": "legg dem til denne private kanalen", "post_body.check_for_out_of_channel_mentions.link.public": "legge dem til kanalen", + "post_body.check_for_out_of_channel_mentions.message.multiple": "ble nevnt, men de er ikke i kanalen. Vil du ", + "post_body.check_for_out_of_channel_mentions.message.one": "ble nevnt, men er ikke i kanalen. Vil du ", "post_body.check_for_out_of_channel_mentions.message_last": "? De vil ha tilgang til all meldingshistorikk.", + "post_body.commentedOn": "Kommenterte {name}{apostrophe}-meldingen: ", "post_body.deleted": "(melding slettet)", "post_info.auto_responder": "Automatisk svar", "post_info.bot": "Bot", @@ -893,6 +944,8 @@ "screen.search.results.filter.title": "Filtrer etter filtype", "screen.search.results.filter.videos": "Videoer", "screen.search.title": "Søk", + "screens.channel_bookmark_add": "Legg til et bokmerke", + "screens.channel_bookmark_edit": "Rediger bokmerke", "screens.channel_edit": "Rediger kanal", "screens.channel_edit_header": "Rediger kanaloverskrift", "screens.channel_info": "Kanal info", @@ -905,6 +958,7 @@ "select_team.no_team.title": "Ingen team er tilgjengelige for å bli med i", "select_team.title": "Velg et team", "server.invalid.certificate.title": "Ugyldig SSL-sertifikat", + "server.invalid.pinning.title": "Ugyldig festet SSL-sertifikat", "server.logout.alert_description": "Alle tilknyttede data vil bli fjernet", "server.logout.alert_title": "Er du sikker på at du vil logge ut av {displayName}?", "server.remove.alert_description": "Dette vil fjerne den fra listen over servere. Alle tilknyttede data vil bli slettet", @@ -937,10 +991,11 @@ "settings.about.server.version": "Serverversjon: {version} (Build {buildNumber}", "settings.about.server.version.noBuild": "Serverversjon: {version}", "settings.about.server.version.title": "Serverversjon:", - "settings.about.server.version.value": "{version} (Build {number})", + "settings.about.server.version.value": "{version} (Build {buildnumber})", "settings.advanced.cancel": "Avbryt", "settings.advanced.delete": "Slett", "settings.advanced.delete_data": "Slett lokale filer", + "settings.advanced.delete_message.confirmation": "\nDette vil slette alle filer som er lastet ned gjennom appen for denne serveren. Vennligst bekreft for å fortsette.\n", "settings.advanced_settings": "Avanserte innstillinger", "settings.display": "Visning", "settings.link.error.text": "Kan ikke åpne lenken.", @@ -955,6 +1010,7 @@ "settings_display.clock.normal.desc": "Eksempel: 4:00 PM", "settings_display.clock.standard": "12-timers klokke", "settings_display.crt.desc": "Når aktivert, vises ikke svarmeldinger i kanalen, og du vil bli varslet om tråder du følger i «Tråder»-visningen.", + "settings_display.crt.label": "Skjulte svartråder", "settings_display.custom_theme": "Egendefinert drakt", "settings_display.timezone.automatically": "Still inn automatisk", "settings_display.timezone.manual": "Endre tidssone", @@ -963,6 +1019,7 @@ "share_extension.channel_error": "Du er ikke medlem av et team på den valgte serveren. Velg en annen server eller åpne Mattermost for å bli med i et team.", "share_extension.channel_label": "Kanal", "share_extension.channels_screen.title": "Velg kanal", + "share_extension.count_limit": "Du kan bare dele {count, number} {count, plural, one {file} other {files}} på denne serveren", "share_extension.error_screen.description": "Det oppsto en feil ved forsøk på å dele innholdet med {applicationName}.", "share_extension.error_screen.label": "En feil oppstod", "share_extension.error_screen.reason": "Årsak: {reason}", @@ -982,6 +1039,7 @@ "skintone_selector.tooltip.description": "Du kan nå velge hudtonen du foretrekker å bruke for emojiene dine.", "skintone_selector.tooltip.title": "Velg din standard hudtone", "smobile.search.recent_title": "Nylige søk i {teamName}", + "snack.bar.channel.members.added": "{numMembers, number} {numMembers, plural, one {member} other {members}} lagt til", "snack.bar.favorited.channel": "Denne kanalen ble satt som favoritt", "snack.bar.following.thread": "Tråd ble fulgt", "snack.bar.info.copied": "Info kopiert til utklippstavle", @@ -1005,6 +1063,7 @@ "suggestion.mention.groups": "Gruppeomtaler", "suggestion.mention.here": "Varsler alle som er på nett i denne kanalen", "suggestion.mention.morechannels": "Andre kanaler", + "suggestion.mention.nonmembers": "Ikke i kanalen", "suggestion.mention.special": "Spesielle omtaler", "suggestion.mention.users": "Brukere", "suggestion.search.direct": "Direktemeldinger", @@ -1013,6 +1072,7 @@ "system_notice.dont_show": "Ikke vis igjen", "system_notice.remind_me": "Påminn meg senere", "system_notice.title.gm_as_dm": "Oppdateringer til gruppemeldinger", + "system_noticy.body.gm_as_dm": "Du vil nå bli varslet for all aktivitet i gruppemeldingene dine sammen med et varslingsmerke for hver ny melding.\n\nDu kan konfigurere dette i varselinnstillingene for hver gruppemelding.", "team_list.no_other_teams.description": "For å bli med i et annet team, spør en teamadministrator om en invitasjon, eller lag ditt eget team.", "team_list.no_other_teams.title": "Ingen flere team å være med i", "terms_of_service.acceptButton": "Aksepter", @@ -1033,6 +1093,7 @@ "thread.loadingReplies": "Laster inn svar...", "thread.noReplies": "Ingen svar ennå", "thread.options.title": "Tråd handlinger", + "thread.repliesCount": "{repliesCount, number} {repliesCount, plural, one {reply} other {replies}}", "threads": "Tråder", "threads.deleted": "Opprinnelig melding slettet", "threads.end_of_list.subtitle": "Hvis du ser etter eldre samtaler, prøv å søke i stedet", @@ -1041,6 +1102,8 @@ "threads.followMessage": "Følg melding", "threads.followThread": "Følg tråd", "threads.following": "Følger", + "threads.newReplies": "{count} ny {count, plural, one {reply} other {replies}}", + "threads.replies": "{count} {count, plural, one {reply} other {replies}}", "threads.unfollowMessage": "Ikke følg melding", "threads.unfollowThread": "Ikke følg tråd", "unreads.empty.paragraph": "Slå av ulest filteret for å vise alle kanalene dine.", diff --git a/assets/base/i18n/nl.json b/assets/base/i18n/nl.json index 420c6473ba9..d2e433fe190 100644 --- a/assets/base/i18n/nl.json +++ b/assets/base/i18n/nl.json @@ -96,6 +96,28 @@ "camera_type.video.option": "Neem video op", "center_panel.archived.closeChannel": "Kanaal sluiten", "channel_add_members.add_members.button": "Voeg leden toe", + "channel_bookmark.add.detail_title": "Titel", + "channel_bookmark.add.emoji": "Emoji toevoegen", + "channel_bookmark.add.failed_title": "Fout bij toevoegen bladwijzer", + "channel_bookmark.add.file_cancel": "Annuleren", + "channel_bookmark.add.file_title": "Bijlage", + "channel_bookmark.add.file_upload_error": "Fout bij het uploaden van bestand. Probeer het opnieuw.", + "channel_bookmark.add.file_uploading": "Uploaden... ({progress}%)", + "channel_bookmark.add_edit.failed_desc": "Details: {error}", + "channel_bookmark.copy_option": "Link kopiëren", + "channel_bookmark.delete.confirm": "Weet je zeker dat je de bladwijzer {displayName} wil verwijderen?", + "channel_bookmark.delete.confirm_title": "Bladwijzer verwijderen", + "channel_bookmark.delete.failed_detail": "Details: {error}", + "channel_bookmark.delete.failed_title": "Fout bij het verwijderen van bladwijzer", + "channel_bookmark.delete.yes": "Ja", + "channel_bookmark.delete_option": "Verwijderen", + "channel_bookmark.edit.failed_title": "Fout bij het bewerken van bladwijzer", + "channel_bookmark.edit.save_button": "Bewaren", + "channel_bookmark.edit_option": "Bewerken", + "channel_bookmark.share_option": "Delen", + "channel_bookmark_add.link": "Link", + "channel_bookmark_add.link.input.description": "Voeg een link toe aan een bericht, bestand of externe link", + "channel_bookmark_add.link.invalid": "Voer een geldige link in", "channel_files.empty.paragraph": "Bestanden geplaatst in dit kanaal worden hier getoond.", "channel_files.empty.title": "Nog geen bestanden", "channel_files.noFiles.paragraph": "Dit kanaal bevat geen bestanden met de toegepaste filters", @@ -103,6 +125,10 @@ "channel_header.directchannel.you": "{displayName} (jijzelf)", "channel_header.info": "Bekijk info", "channel_header.member_count": "{count, plural, one {# lid} other {# leden}}", + "channel_info.add_bookmark": "Een bladwijzer toevoegen", + "channel_info.add_bookmark.file": "Een bestand bijvoegen", + "channel_info.add_bookmark.link": "Een link toevoegen", + "channel_info.add_bookmark.max_reached": "Dit kanaal heeft het maximale aantal bladwijzers bereikt.", "channel_info.add_members": "Voeg leden toe", "channel_info.alertNo": "Nee", "channel_info.alertYes": "Ja", @@ -460,6 +486,7 @@ "mobile.calls_ended_at": "Beëindigd op", "mobile.calls_error_message": "Fout: {error}", "mobile.calls_error_title": "Fout", + "mobile.calls_group_calls_not_available": "Gesprekken zijn alleen beschikbaar in DM-kanalen.", "mobile.calls_headset": "Hoofdtelefoon", "mobile.calls_hide_cc": "Live ondertiteling verbergen", "mobile.calls_host": "gastheer", @@ -963,6 +990,8 @@ "screen.search.results.filter.title": "Filteren op bestandstype", "screen.search.results.filter.videos": "Video's", "screen.search.title": "Zoeken", + "screens.channel_bookmark_add": "Een bladwijzer toevoegen", + "screens.channel_bookmark_edit": "Bladwijzer bewerken", "screens.channel_edit": "Kanaal bewerken", "screens.channel_edit_header": "Kanaalhoofding bewerken", "screens.channel_info": "Kanaalinfo", @@ -976,6 +1005,7 @@ "select_team.title": "Selecteer een team", "server.invalid.certificate.description": "Het certificaat voor deze server is ongeldig.\nMogelijks maak je verbinding met een server die zich voordoet als \"{hostname}\", waardoor je vertrouwelijke gegevens in gevaar kunnen komen.", "server.invalid.certificate.title": "Ongeldig SSL-certificaat", + "server.invalid.pinning.title": "Ongeldig vastgemaakt SSL-certificaat", "server.logout.alert_description": "Alle bijbehorende gegevens worden verwijderd", "server.logout.alert_title": "Weet je zeker dat je wilt uitloggen bij {displayName}?", "server.remove.alert_description": "Dit zal de server verwijderen uit jouw lijst van servers. Alle bijbehorende gegevens worden verwijderd", @@ -1080,6 +1110,7 @@ "suggestion.mention.groups": "Groepsvermeldingen", "suggestion.mention.here": "Verwittig iedereen in dit kanaal", "suggestion.mention.morechannels": "Andere Kanalen", + "suggestion.mention.nonmembers": "Niet in kanaal", "suggestion.mention.special": "Speciale Vermeldingen", "suggestion.mention.users": "Gebruikers", "suggestion.search.direct": "Privé bericht", diff --git a/assets/base/i18n/pl.json b/assets/base/i18n/pl.json index 18d298bab25..de0f1ce8725 100644 --- a/assets/base/i18n/pl.json +++ b/assets/base/i18n/pl.json @@ -96,6 +96,28 @@ "camera_type.video.option": "Nagraj wideo", "center_panel.archived.closeChannel": "Zamknij Kanał", "channel_add_members.add_members.button": "Dodaj Użytkowników", + "channel_bookmark.add.detail_title": "Tytuł", + "channel_bookmark.add.emoji": "Dodaj emoji", + "channel_bookmark.add.failed_title": "Błąd dodawania zakładki", + "channel_bookmark.add.file_cancel": "Anuluj", + "channel_bookmark.add.file_title": "Załącznik", + "channel_bookmark.add.file_upload_error": "Błąd przesyłania pliku. Spróbuj ponownie.", + "channel_bookmark.add.file_uploading": "Przesyłanie... ({progress}%)", + "channel_bookmark.add_edit.failed_desc": "Szczegóły: {error}", + "channel_bookmark.copy_option": "Kopiuj Odnośnik", + "channel_bookmark.delete.confirm": "Na pewno chcesz usunąć zakładkę {displayName}?", + "channel_bookmark.delete.confirm_title": "Usuń zakładkę", + "channel_bookmark.delete.failed_detail": "Szczegóły: {error}", + "channel_bookmark.delete.failed_title": "Błąd usuwania zakładki", + "channel_bookmark.delete.yes": "Tak", + "channel_bookmark.delete_option": "Usuń", + "channel_bookmark.edit.failed_title": "Błąd edycji zakładki", + "channel_bookmark.edit.save_button": "Zapisz", + "channel_bookmark.edit_option": "Edytuj", + "channel_bookmark.share_option": "Udostępnij", + "channel_bookmark_add.link": "Link", + "channel_bookmark_add.link.input.description": "Dodaj link do dowolnego postu, pliku lub linku zewnętrznego", + "channel_bookmark_add.link.invalid": "Wprowadź prawidłowy link", "channel_files.empty.paragraph": "Pliki umieszczone w tym kanale będą wyświetlane tutaj.", "channel_files.empty.title": "Nie ma jeszcze plików", "channel_files.noFiles.paragraph": "Ten kanał nie zawiera żadnych plików z zastosowanymi filtrami", @@ -103,6 +125,10 @@ "channel_header.directchannel.you": "{displayName} (ty)", "channel_header.info": "Wyświetl informacje", "channel_header.member_count": "{count, plural, one {# członek} other {# członkowie}}", + "channel_info.add_bookmark": "Dodaj zakładkę", + "channel_info.add_bookmark.file": "Dołącz plik", + "channel_info.add_bookmark.link": "Dodaj link", + "channel_info.add_bookmark.max_reached": "Ten kanał osiągnął maksymalną liczbę zakładek.", "channel_info.add_members": "Dodaj użytkowników", "channel_info.alertNo": "Nie", "channel_info.alertYes": "Tak", @@ -460,6 +486,7 @@ "mobile.calls_ended_at": "Zakończone", "mobile.calls_error_message": "Błąd: {error}", "mobile.calls_error_title": "Błąd", + "mobile.calls_group_calls_not_available": "Połączenia są dostępne tylko w kanałach DM.", "mobile.calls_headset": "Zestaw słuchawkowy", "mobile.calls_hide_cc": "Ukryj napisy na żywo", "mobile.calls_host": "host", @@ -796,6 +823,16 @@ "notification_settings.auto_responder.footer.message": "Ustaw niestandardową wiadomość, która jest automatycznie wysyłana w odpowiedzi na wiadomości bezpośrednie, takie jak spoza biura lub z urlopu. Włączenie tego ustawienia powoduje zmianę statusu użytkownika na Poza biurem i wyłączy powiadomienia.", "notification_settings.auto_responder.message": "Wiadomość", "notification_settings.auto_responder.to.enable": "Włącz automatyczne odpowiedzi", + "notification_settings.call_notification": "Powiadomienia o połączeniach", + "notification_settings.calls": "Powiadomienia o połączeniach", + "notification_settings.calls.callsInfo": "Uwaga: aby usłyszeć podgląd dzwonka, tryb cichy musi być wyłączony.", + "notification_settings.calls.calm": "Spokój", + "notification_settings.calls.cheerful": "Wesoły", + "notification_settings.calls.dynamic": "Dynamiczny", + "notification_settings.calls.enable_sound": "Dźwięk powiadomienia dla połączeń przychodzących", + "notification_settings.calls.urgent": "Pilne", + "notification_settings.calls_off": "Wył", + "notification_settings.calls_on": "Wł", "notification_settings.email": "Powiadomienia Email", "notification_settings.email.crt.emailInfo": "Po włączeniu każda odpowiedź na wątek, który obserwujesz, wyśle powiadomienie e-mail", "notification_settings.email.crt.send": "Powiadomienia o odpowiedziach w wątku", @@ -953,6 +990,8 @@ "screen.search.results.filter.title": "Filtrowanie według typu pliku", "screen.search.results.filter.videos": "Wideo", "screen.search.title": "Szukaj", + "screens.channel_bookmark_add": "Dodaj zakładkę", + "screens.channel_bookmark_edit": "Edytuj zakładkę", "screens.channel_edit": "Edytuj Kanał", "screens.channel_edit_header": "Edycja nagłówka kanału", "screens.channel_info": "Informacje o kanale", @@ -966,6 +1005,7 @@ "select_team.title": "Wybierz zespół", "server.invalid.certificate.description": "Certyfikat tego serwera jest nieprawidłowy.\nMożliwe, że łączysz się z serwerem podszywającym się pod \"{hostname}\", co może narazić Twoje poufne informacje na ryzyko.", "server.invalid.certificate.title": "Nieprawidłowy certyfikat SSL", + "server.invalid.pinning.title": "Nieprawidłowy przypięty certyfikat SSL", "server.logout.alert_description": "Wszystkie powiązane dane zostaną usunięte", "server.logout.alert_title": "Czy na pewno chcesz się wylogować z {displayName}?", "server.remove.alert_description": "Spowoduje to usunięcie z listy serwerów. Wszystkie powiązane dane zostaną usunięte", @@ -1070,6 +1110,7 @@ "suggestion.mention.groups": "Group Mentions", "suggestion.mention.here": "Powiadamia wszystkich obecnie dostępnych na kanale", "suggestion.mention.morechannels": "Inne kanały", + "suggestion.mention.nonmembers": "Nie na Kanale", "suggestion.mention.special": "Specjalne wzmianki", "suggestion.mention.users": "Użytkownicy", "suggestion.search.direct": "Wiadomości bezpośrednie", diff --git a/assets/base/i18n/ru.json b/assets/base/i18n/ru.json index 13f12c827de..7530c9c00c8 100644 --- a/assets/base/i18n/ru.json +++ b/assets/base/i18n/ru.json @@ -96,6 +96,28 @@ "camera_type.video.option": "Записать видео", "center_panel.archived.closeChannel": "Закрыть канал", "channel_add_members.add_members.button": "Добавить участников", + "channel_bookmark.add.detail_title": "Заголовок", + "channel_bookmark.add.emoji": "Добавить эмодзи", + "channel_bookmark.add.failed_title": "Ошибка при добавлении закладки", + "channel_bookmark.add.file_cancel": "Отмена", + "channel_bookmark.add.file_title": "Вложение", + "channel_bookmark.add.file_upload_error": "Ошибка при загрузке файла. Пожалуйста, попробуйте еще раз.", + "channel_bookmark.add.file_uploading": "Загрузка... ({progress}%)", + "channel_bookmark.add_edit.failed_desc": "Подробности: {error}", + "channel_bookmark.copy_option": "Копировать ссылку", + "channel_bookmark.delete.confirm": "Вы уверены, что хотите удалить закладку {displayName}?", + "channel_bookmark.delete.confirm_title": "Удалить закладку", + "channel_bookmark.delete.failed_detail": "Подробности: {error}", + "channel_bookmark.delete.failed_title": "Ошибка при удалении закладки", + "channel_bookmark.delete.yes": "Да", + "channel_bookmark.delete_option": "Удалить", + "channel_bookmark.edit.failed_title": "Ошибка при изменении закладки", + "channel_bookmark.edit.save_button": "Сохранить", + "channel_bookmark.edit_option": "Изменить", + "channel_bookmark.share_option": "Поделиться", + "channel_bookmark_add.link": "Ссылка", + "channel_bookmark_add.link.input.description": "Добавьте ссылку на любое сообщение, файл или любую внешнюю ссылку", + "channel_bookmark_add.link.invalid": "Пожалуйста, введите валидную ссылку", "channel_files.empty.paragraph": "Здесь показаны опубликованные в этом канале файлы.", "channel_files.empty.title": "Файлов пока нет", "channel_files.noFiles.paragraph": "Этот канал не содержит файлов с примененными фильтрами", @@ -103,6 +125,10 @@ "channel_header.directchannel.you": "{displayName} (вы)", "channel_header.info": "Посмотреть информацию", "channel_header.member_count": "{count, plural, one {# участник} few {# участника} other {# участников}}", + "channel_info.add_bookmark": "Добавить закладку", + "channel_info.add_bookmark.file": "Прикрепить файл", + "channel_info.add_bookmark.link": "Добавить ссылку", + "channel_info.add_bookmark.max_reached": "На этом канале достигнуто максимальное количество закладок.", "channel_info.add_members": "Добавить участников", "channel_info.alertNo": "Нет", "channel_info.alertYes": "Да", @@ -229,11 +255,11 @@ "combined_system_message.left_team.two": "{firstUser} и {secondUser} **покинули команду**.", "combined_system_message.removed_from_channel.many_expanded": "{users} и {lastUser} **удалены с канала**.", "combined_system_message.removed_from_channel.one": "{firstUser} был **удалён с канала**.", - "combined_system_message.removed_from_channel.one_you": "Вы были **удалены с канала**.", + "combined_system_message.removed_from_channel.one_you": "Вы были **удалены из канала**.", "combined_system_message.removed_from_channel.two": "{firstUser} и {secondUser} были **удалены с канала**.", "combined_system_message.removed_from_team.many_expanded": "{users} и {lastUser} **удалены из команды**.", "combined_system_message.removed_from_team.one": "{firstUser} был **удалён из команды**.", - "combined_system_message.removed_from_team.one_you": "Вы были **удалены с канала**.", + "combined_system_message.removed_from_team.one_you": "Вы были **удалены из канала**.", "combined_system_message.removed_from_team.two": "{firstUser} и {secondUser} **удалены из команды**.", "combined_system_message.you": "Вы", "connection_banner.connected": "Соединение восстановлено", @@ -460,6 +486,7 @@ "mobile.calls_ended_at": "Окончен в", "mobile.calls_error_message": "Ошибка: {error}", "mobile.calls_error_title": "Ошибка", + "mobile.calls_group_calls_not_available": "Звонки доступны только в приватных каналах (DM).", "mobile.calls_headset": "Гарнитура", "mobile.calls_hide_cc": "Скрыть живые титры", "mobile.calls_host": "хост", @@ -676,6 +703,8 @@ "mobile.no_results_with_term.messages": "Не найдено совпадений для \"{term}\"", "mobile.oauth.failed_to_login": "Ваша попытка входа не удалась. Пожалуйста, попробуйте ещё раз.", "mobile.oauth.something_wrong.okButton": "ОК", + "mobile.oauth.success.description": "Входим в систему, один момент...", + "mobile.oauth.success.title": "Аутентификация успешна", "mobile.oauth.switch_to_browser": "Вы перенаправляетесь к поставщику услуг входа в систему", "mobile.oauth.switch_to_browser.error_title": "Ошибка при входе", "mobile.oauth.switch_to_browser.title": "Перенаправление...", @@ -963,6 +992,8 @@ "screen.search.results.filter.title": "Фильтр по типу файлов", "screen.search.results.filter.videos": "Видео", "screen.search.title": "Поиск", + "screens.channel_bookmark_add": "Добавить закладку", + "screens.channel_bookmark_edit": "Изменить закладку", "screens.channel_edit": "Изменить канал", "screens.channel_edit_header": "Изменить заголовок канала", "screens.channel_info": "Информация о канале", @@ -976,6 +1007,7 @@ "select_team.title": "Выберите команду", "server.invalid.certificate.description": "Сертификат этого сервера недействителен.\nВозможно, вы подключаетесь к серверу, который выдает себя за \"{hostname}\", что может подвергнуть риску вашу конфиденциальную информацию.", "server.invalid.certificate.title": "Недействительный сертификат SSL", + "server.invalid.pinning.title": "Недействительный прикрепленный SSL-сертификат", "server.logout.alert_description": "Все связанные данные будут удалены", "server.logout.alert_title": "Вы уверены, что хотите выйти из {displayName}?", "server.remove.alert_description": "Это удалит его из вашего списка серверов. Все связанные с ним данные будут удалены", @@ -1080,6 +1112,7 @@ "suggestion.mention.groups": "Групповые упоминания", "suggestion.mention.here": "Уведомляет всех кто онлайн на канале", "suggestion.mention.morechannels": "Другие каналы", + "suggestion.mention.nonmembers": "Не в канале", "suggestion.mention.special": "Особые упоминания", "suggestion.mention.users": "Пользователи", "suggestion.search.direct": "Личные сообщения", diff --git a/assets/base/i18n/sv.json b/assets/base/i18n/sv.json index cb3dfd8133a..f748a7200ad 100644 --- a/assets/base/i18n/sv.json +++ b/assets/base/i18n/sv.json @@ -96,6 +96,28 @@ "camera_type.video.option": "Spela in video", "center_panel.archived.closeChannel": "Stäng kanalen", "channel_add_members.add_members.button": "Lägg till medlemmar", + "channel_bookmark.add.detail_title": "Titel", + "channel_bookmark.add.emoji": "Lägg till emoji", + "channel_bookmark.add.failed_title": "Fel när bokmärke lades till", + "channel_bookmark.add.file_cancel": "Avbryt", + "channel_bookmark.add.file_title": "Bilaga", + "channel_bookmark.add.file_upload_error": "Fel då fil laddades upp. Prova igen.", + "channel_bookmark.add.file_uploading": "Laddar upp... ({progress}%)", + "channel_bookmark.add_edit.failed_desc": "Detaljer: {error}", + "channel_bookmark.copy_option": "Kopiera länk", + "channel_bookmark.delete.confirm": "Är du säker på att du vill ta bort bokmärket {displayName}?", + "channel_bookmark.delete.confirm_title": "Ta bort bokmärke", + "channel_bookmark.delete.failed_detail": "Detaljer: {error}", + "channel_bookmark.delete.failed_title": "Fel när bokmärke togs bort", + "channel_bookmark.delete.yes": "Ja", + "channel_bookmark.delete_option": "Ta bort", + "channel_bookmark.edit.failed_title": "Fel då bokmärke redigerades", + "channel_bookmark.edit.save_button": "Spara", + "channel_bookmark.edit_option": "Redigera", + "channel_bookmark.share_option": "Dela", + "channel_bookmark_add.link": "Länk", + "channel_bookmark_add.link.input.description": "Lägg till en länk till ett meddelande, fil eller extern länk", + "channel_bookmark_add.link.invalid": "Ange en giltig länk", "channel_files.empty.paragraph": "Filer postade i kanalen visas här.", "channel_files.empty.title": "Ännu inga filer", "channel_files.noFiles.paragraph": "Den här kanalen innehåller inga filer med det filter som används", @@ -103,6 +125,10 @@ "channel_header.directchannel.you": "{displayName} (du)", "channel_header.info": "Visa info", "channel_header.member_count": "{count, plural, one {# medlem} other {# medlemmar}}", + "channel_info.add_bookmark": "Lägg till ett bokmärke", + "channel_info.add_bookmark.file": "Bifoga en fil", + "channel_info.add_bookmark.link": "Lägg till en länk", + "channel_info.add_bookmark.max_reached": "Kanalen har nått maximalt antal bokmärken.", "channel_info.add_members": "Lägg till medlemmar", "channel_info.alertNo": "Nej", "channel_info.alertYes": "Ja", @@ -460,6 +486,7 @@ "mobile.calls_ended_at": "Avslutades den", "mobile.calls_error_message": "Fel: {error}", "mobile.calls_error_title": "Fel", + "mobile.calls_group_calls_not_available": "Calls är bara tillgängligt i DM-kanaler.", "mobile.calls_headset": "Headset", "mobile.calls_hide_cc": "Dölj live-textning", "mobile.calls_host": "värd", @@ -654,7 +681,7 @@ "mobile.manage_members.section_title_admins": "KANALADMINISTRATÖRER", "mobile.manage_members.section_title_members": "MEDLEMMAR", "mobile.managed.blocked_by": "Blockerad av {vendor}", - "mobile.managed.exit": "Redigera", + "mobile.managed.exit": "Avsluta", "mobile.managed.jailbreak": "Din jailbrejkade eller rootade enhet är inte betrodd av {vendor}.\n\nAppen kommer stängas.", "mobile.managed.not_secured.android": "Enheten behöver säkras med skärmlås för att använda Mattermost.", "mobile.managed.not_secured.ios": "Enheten behöver säkras med en passcode för att använda Mattermost.\n\nGå till Inställningar > Face ID & Passcode.", @@ -676,6 +703,8 @@ "mobile.no_results_with_term.messages": "Inga träffar hittades för “{term}”", "mobile.oauth.failed_to_login": "Inloggningen misslyckades. Försök igen.", "mobile.oauth.something_wrong.okButton": "OK", + "mobile.oauth.success.description": "Inloggning pågår, ett ögonblick...", + "mobile.oauth.success.title": "Autentiseringen lyckades", "mobile.oauth.switch_to_browser": "Du omdirigeras till din inloggningsleverantör", "mobile.oauth.switch_to_browser.error_title": "Fel vid inloggning", "mobile.oauth.switch_to_browser.title": "Omdirigeras...", @@ -963,6 +992,8 @@ "screen.search.results.filter.title": "Filtrera efter filtyp", "screen.search.results.filter.videos": "Videor", "screen.search.title": "Sök", + "screens.channel_bookmark_add": "Lägg till ett bokmärke", + "screens.channel_bookmark_edit": "Redigera bokmärke", "screens.channel_edit": "Redigera kanal", "screens.channel_edit_header": "Redigera kanalens sidhuvud", "screens.channel_info": "Information om kanalen", @@ -976,6 +1007,7 @@ "select_team.title": "Välj ett team", "server.invalid.certificate.description": "Servercertifikatet på servern är felaktigt.\nDet kan vara så att du ansluter till en server som låtsas vara \"{hostname}\" och din information skulle kunna riskera läcka till obehöriga.", "server.invalid.certificate.title": "Ogiltigt SSL-certifikat", + "server.invalid.pinning.title": "Ogiltigt nålat SSL-certifikat", "server.logout.alert_description": "Alla tillhörande uppgifter kommer tas bort", "server.logout.alert_title": "Är du säker på att du vill logga ut från {displayName}?", "server.remove.alert_description": "Detta kommer att ta bort den från listan över servrar. Allt tillhörande data tas bort", @@ -1080,6 +1112,7 @@ "suggestion.mention.groups": "Gruppomnämnanden", "suggestion.mention.here": "Notifierar alla anslutna i kanalen", "suggestion.mention.morechannels": "Andra kanaler", + "suggestion.mention.nonmembers": "Inte i kanalen", "suggestion.mention.special": "Speciella omnämnanden", "suggestion.mention.users": "Användare", "suggestion.search.direct": "Direktmeddelande", diff --git a/assets/base/i18n/zh-CN.json b/assets/base/i18n/zh-CN.json index 31701f8375e..11ad61b8775 100644 --- a/assets/base/i18n/zh-CN.json +++ b/assets/base/i18n/zh-CN.json @@ -96,6 +96,28 @@ "camera_type.video.option": "录制视频", "center_panel.archived.closeChannel": "关闭频道", "channel_add_members.add_members.button": "添加成员", + "channel_bookmark.add.detail_title": "标题", + "channel_bookmark.add.emoji": "添加表情符", + "channel_bookmark.add.failed_title": "添加书签时出错", + "channel_bookmark.add.file_cancel": "取消", + "channel_bookmark.add.file_title": "附件", + "channel_bookmark.add.file_upload_error": "上传文件时出错。请重试。", + "channel_bookmark.add.file_uploading": "上传中…({progress}%)", + "channel_bookmark.add_edit.failed_desc": "详情:{error}", + "channel_bookmark.copy_option": "复制链接", + "channel_bookmark.delete.confirm": "您确定要删除书签{displayName}吗?", + "channel_bookmark.delete.confirm_title": "删除书签", + "channel_bookmark.delete.failed_detail": "详情:{error}", + "channel_bookmark.delete.failed_title": "删除书签时出错", + "channel_bookmark.delete.yes": "是", + "channel_bookmark.delete_option": "删除", + "channel_bookmark.edit.failed_title": "编辑书签时出错", + "channel_bookmark.edit.save_button": "保存", + "channel_bookmark.edit_option": "编辑", + "channel_bookmark.share_option": "分享", + "channel_bookmark_add.link": "链接", + "channel_bookmark_add.link.input.description": "添加一个指向任何消息、文件的链接,或任何外部链接", + "channel_bookmark_add.link.invalid": "请输入有效链接", "channel_files.empty.paragraph": "在此频道中发布的文件将显示在此处。", "channel_files.empty.title": "还没有文件", "channel_files.noFiles.paragraph": "此频道未包含任何应用的过滤器的文件", @@ -103,6 +125,10 @@ "channel_header.directchannel.you": "{displayName} (您)", "channel_header.info": "频道信息", "channel_header.member_count": "{count, plural, other {# 位成员}}", + "channel_info.add_bookmark": "添加书签", + "channel_info.add_bookmark.file": "附加文件", + "channel_info.add_bookmark.link": "添加链接", + "channel_info.add_bookmark.max_reached": "此频道已达到书签数量上限。", "channel_info.add_members": "添加成员", "channel_info.alertNo": "不", "channel_info.alertYes": "是", @@ -460,6 +486,7 @@ "mobile.calls_ended_at": "结束于", "mobile.calls_error_message": "错误:{error}", "mobile.calls_error_title": "错误", + "mobile.calls_group_calls_not_available": "通话仅在私信中可用。", "mobile.calls_headset": "耳机", "mobile.calls_hide_cc": "隐藏实时字幕", "mobile.calls_host": "主持人", @@ -676,6 +703,8 @@ "mobile.no_results_with_term.messages": "\"{term}\"没有匹配的发现", "mobile.oauth.failed_to_login": "登入失败。请重试。", "mobile.oauth.something_wrong.okButton": "好的", + "mobile.oauth.success.description": "正在登录,请稍候…", + "mobile.oauth.success.title": "验证成功", "mobile.oauth.switch_to_browser": "您正在被切换到登录服务提供商", "mobile.oauth.switch_to_browser.error_title": "登录错误", "mobile.oauth.switch_to_browser.title": "切换中…", @@ -796,6 +825,16 @@ "notification_settings.auto_responder.footer.message": "设置自定义消息,以用来在离开办公室或者度假时在聊天中自动发出的回复。开启此设定将改变您的状态到“不在办公“状态并且关闭推送通知。", "notification_settings.auto_responder.message": "消息", "notification_settings.auto_responder.to.enable": "开启自动回复", + "notification_settings.call_notification": "通话通知", + "notification_settings.calls": "通话通知", + "notification_settings.calls.callsInfo": "注意:在试听铃声前需要关闭静音模式。", + "notification_settings.calls.calm": "Calm", + "notification_settings.calls.cheerful": "Cheerful", + "notification_settings.calls.dynamic": "Dynamic", + "notification_settings.calls.enable_sound": "来电铃声", + "notification_settings.calls.urgent": "Urgent", + "notification_settings.calls_off": "关", + "notification_settings.calls_on": "开", "notification_settings.email": "邮件通知", "notification_settings.email.crt.emailInfo": "当开启时,任何你关注话题的回复将发送邮件通知", "notification_settings.email.crt.send": "话题回复通知", @@ -953,6 +992,8 @@ "screen.search.results.filter.title": "通过文件类型过滤", "screen.search.results.filter.videos": "视频", "screen.search.title": "搜索", + "screens.channel_bookmark_add": "添加书签", + "screens.channel_bookmark_edit": "编辑书签", "screens.channel_edit": "编辑频道", "screens.channel_edit_header": "编辑频道标题", "screens.channel_info": "频道信息", @@ -966,6 +1007,7 @@ "select_team.title": "选择一个团队", "server.invalid.certificate.description": "此服务器的证书无效。\n您可能正在连接到一个假冒为 \"{hostname}\" 的服务器,这可能会危及您的机密信息。", "server.invalid.certificate.title": "无效的SSL证书", + "server.invalid.pinning.title": "无效的内嵌 SSL 证书", "server.logout.alert_description": "所以相关的数据都将被删除", "server.logout.alert_title": "您确定要登出{displayName}吗?", "server.remove.alert_description": "这将会从您的服务器列表中删除此项。所有关联的数据将会被删除", @@ -1070,6 +1112,7 @@ "suggestion.mention.groups": "群组提及", "suggestion.mention.here": "通知所有在此频道在线的人", "suggestion.mention.morechannels": "其他频道", + "suggestion.mention.nonmembers": "不在频道中", "suggestion.mention.special": "特别提及", "suggestion.mention.users": "用户", "suggestion.search.direct": "私信", diff --git a/assets/certs/.gitignore b/assets/certs/.gitignore new file mode 100644 index 00000000000..86d0cb2726c --- /dev/null +++ b/assets/certs/.gitignore @@ -0,0 +1,4 @@ +# Ignore everything in this directory +* +# Except this file +!.gitignore \ No newline at end of file diff --git a/detox/package-lock.json b/detox/package-lock.json index b04bc04afc5..2e830883ec2 100644 --- a/detox/package-lock.json +++ b/detox/package-lock.json @@ -6,41 +6,41 @@ "": { "name": "mattermost-mobile-e2e", "devDependencies": { - "@aws-sdk/client-s3": "3.445.0", - "@aws-sdk/lib-storage": "3.445.0", + "@aws-sdk/client-s3": "3.624.0", + "@aws-sdk/lib-storage": "3.624.0", "@babel/plugin-proposal-class-properties": "7.18.6", - "@babel/plugin-transform-modules-commonjs": "7.22.15", - "@babel/plugin-transform-runtime": "7.22.15", - "@babel/preset-env": "7.22.20", + "@babel/plugin-transform-modules-commonjs": "7.24.8", + "@babel/plugin-transform-runtime": "7.24.7", + "@babel/preset-env": "7.25.3", "@jest/test-sequencer": "29.7.0", - "@types/jest": "29.5.5", - "@types/tough-cookie": "4.0.3", - "@types/uuid": "9.0.4", + "@types/jest": "29.5.12", + "@types/tough-cookie": "4.0.5", + "@types/uuid": "10.0.0", "async": "3.2.5", - "axios": "1.5.0", - "axios-cookiejar-support": "4.0.7", + "axios": "1.7.3", + "axios-cookiejar-support": "5.0.2", "babel-jest": "29.7.0", - "babel-plugin-module-resolver": "5.0.0", + "babel-plugin-module-resolver": "5.0.2", "client-oauth2": "4.3.3", "deepmerge": "4.3.1", - "detox": "20.19.3", + "detox": "20.25.2", "form-data": "4.0.0", "jest": "29.7.0", "jest-circus": "29.7.0", "jest-cli": "29.7.0", - "jest-html-reporters": "3.1.4", + "jest-html-reporters": "3.1.7", "jest-junit": "16.0.0", - "jest-stare": "2.5.1", - "junit-report-merger": "6.0.2", - "moment-timezone": "0.5.43", + "jest-stare": "2.5.2", + "junit-report-merger": "7.0.0", + "moment-timezone": "0.5.45", "recursive-readdir": "2.2.3", "sanitize-filename": "1.6.3", "shelljs": "0.8.5", - "tough-cookie": "4.1.3", - "ts-jest": "29.1.1", - "tslib": "2.6.2", - "typescript": "5.2.2", - "uuid": "9.0.1", + "tough-cookie": "4.1.4", + "ts-jest": "29.2.4", + "tslib": "2.6.3", + "typescript": "5.5.4", + "uuid": "10.0.0", "xml2js": "0.6.2" } }, @@ -58,799 +58,948 @@ } }, "node_modules/@aws-crypto/crc32": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-3.0.0.tgz", - "integrity": "sha512-IzSgsrxUcsrejQbPVilIKy16kAT52EwB6zSaI+M3xxIhKh5+aldEyvI+z6erM7TCLB2BJsFrtHjp6/4/sr+3dA==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-5.2.0.tgz", + "integrity": "sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@aws-crypto/util": "^3.0.0", + "@aws-crypto/util": "^5.2.0", "@aws-sdk/types": "^3.222.0", - "tslib": "^1.11.1" + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@aws-crypto/crc32/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, "node_modules/@aws-crypto/crc32c": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/crc32c/-/crc32c-3.0.0.tgz", - "integrity": "sha512-ENNPPManmnVJ4BTXlOjAgD7URidbAznURqD0KvfREyc4o20DPYdEldU1f5cQ7Jbj0CJJSPaMIk/9ZshdB3210w==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/crc32c/-/crc32c-5.2.0.tgz", + "integrity": "sha512-+iWb8qaHLYKrNvGRbiYRHSdKRWhto5XlZUEBwDjYNf+ly5SVYG6zEoYIdxvf5R3zyeP16w4PLBn3rH1xc74Rag==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@aws-crypto/util": "^3.0.0", + "@aws-crypto/util": "^5.2.0", "@aws-sdk/types": "^3.222.0", - "tslib": "^1.11.1" + "tslib": "^2.6.2" } }, - "node_modules/@aws-crypto/crc32c/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, - "node_modules/@aws-crypto/ie11-detection": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/ie11-detection/-/ie11-detection-3.0.0.tgz", - "integrity": "sha512-341lBBkiY1DfDNKai/wXM3aujNBkXR7tq1URPQDL9wi3AUbI80NR74uF1TXHMm7po1AcnFk8iu2S2IeU/+/A+Q==", + "node_modules/@aws-crypto/sha1-browser": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha1-browser/-/sha1-browser-5.2.0.tgz", + "integrity": "sha512-OH6lveCFfcDjX4dbAvCFSYUjJZjDr/3XJ3xHtjn3Oj5b9RjojQo8npoLeA/bNwkOkrSQ0wgrHzXk4tDRxGKJeg==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "tslib": "^1.11.1" + "@aws-crypto/supports-web-crypto": "^5.2.0", + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" } }, - "node_modules/@aws-crypto/ie11-detection/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true + "node_modules/@aws-crypto/sha1-browser/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } }, - "node_modules/@aws-crypto/sha1-browser": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/sha1-browser/-/sha1-browser-3.0.0.tgz", - "integrity": "sha512-NJth5c997GLHs6nOYTzFKTbYdMNA6/1XlKVgnZoaZcQ7z7UJlOgj2JdbHE8tiYLS3fzXNCguct77SPGat2raSw==", + "node_modules/@aws-crypto/sha1-browser/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@aws-crypto/ie11-detection": "^3.0.0", - "@aws-crypto/supports-web-crypto": "^3.0.0", - "@aws-crypto/util": "^3.0.0", - "@aws-sdk/types": "^3.222.0", - "@aws-sdk/util-locate-window": "^3.0.0", - "@aws-sdk/util-utf8-browser": "^3.0.0", - "tslib": "^1.11.1" + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" } }, - "node_modules/@aws-crypto/sha1-browser/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true + "node_modules/@aws-crypto/sha1-browser/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } }, "node_modules/@aws-crypto/sha256-browser": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-3.0.0.tgz", - "integrity": "sha512-8VLmW2B+gjFbU5uMeqtQM6Nj0/F1bro80xQXCW6CQBWgosFWXTx77aeOF5CAIAmbOK64SdMBJdNr6J41yP5mvQ==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz", + "integrity": "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@aws-crypto/ie11-detection": "^3.0.0", - "@aws-crypto/sha256-js": "^3.0.0", - "@aws-crypto/supports-web-crypto": "^3.0.0", - "@aws-crypto/util": "^3.0.0", + "@aws-crypto/sha256-js": "^5.2.0", + "@aws-crypto/supports-web-crypto": "^5.2.0", + "@aws-crypto/util": "^5.2.0", "@aws-sdk/types": "^3.222.0", "@aws-sdk/util-locate-window": "^3.0.0", - "@aws-sdk/util-utf8-browser": "^3.0.0", - "tslib": "^1.11.1" + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" } }, - "node_modules/@aws-crypto/sha256-browser/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } }, "node_modules/@aws-crypto/sha256-js": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-3.0.0.tgz", - "integrity": "sha512-PnNN7os0+yd1XvXAy23CFOmTbMaDxgxXtTKHybrJ39Y8kGzBATgBFibWJKH6BhytLI/Zyszs87xCOBNyBig6vQ==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz", + "integrity": "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@aws-crypto/util": "^3.0.0", + "@aws-crypto/util": "^5.2.0", "@aws-sdk/types": "^3.222.0", - "tslib": "^1.11.1" + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@aws-crypto/sha256-js/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, "node_modules/@aws-crypto/supports-web-crypto": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-3.0.0.tgz", - "integrity": "sha512-06hBdMwUAb2WFTuGG73LSC0wfPu93xWwo5vL2et9eymgmu3Id5vFAHBbajVWiGhPO37qcsdCap/FqXvJGJWPIg==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-5.2.0.tgz", + "integrity": "sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "tslib": "^1.11.1" + "tslib": "^2.6.2" } }, - "node_modules/@aws-crypto/supports-web-crypto/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, "node_modules/@aws-crypto/util": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-3.0.0.tgz", - "integrity": "sha512-2OJlpeJpCR48CC8r+uKVChzs9Iungj9wkZrl8Z041DWEWvyIHILYKCPNzJghKsivj+S3mLo6BVc7mBNzdxA46w==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz", + "integrity": "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==", "dev": true, + "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "^3.222.0", - "@aws-sdk/util-utf8-browser": "^3.0.0", - "tslib": "^1.11.1" + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" } }, - "node_modules/@aws-crypto/util/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true + "node_modules/@aws-crypto/util/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } }, - "node_modules/@aws-sdk/client-s3": { - "version": "3.445.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.445.0.tgz", - "integrity": "sha512-2G+3MnO78irZRjlfkdvtlKRQ3yuOfrRMg8mztKpMw0q/9WHtwCcmaUUpl1bXwJ+BcNTVHopLQXdbzCeaxxI92w==", - "dev": true, - "dependencies": { - "@aws-crypto/sha1-browser": "3.0.0", - "@aws-crypto/sha256-browser": "3.0.0", - "@aws-crypto/sha256-js": "3.0.0", - "@aws-sdk/client-sts": "3.445.0", - "@aws-sdk/core": "3.445.0", - "@aws-sdk/credential-provider-node": "3.445.0", - "@aws-sdk/middleware-bucket-endpoint": "3.433.0", - "@aws-sdk/middleware-expect-continue": "3.433.0", - "@aws-sdk/middleware-flexible-checksums": "3.433.0", - "@aws-sdk/middleware-host-header": "3.433.0", - "@aws-sdk/middleware-location-constraint": "3.433.0", - "@aws-sdk/middleware-logger": "3.433.0", - "@aws-sdk/middleware-recursion-detection": "3.433.0", - "@aws-sdk/middleware-sdk-s3": "3.440.0", - "@aws-sdk/middleware-signing": "3.433.0", - "@aws-sdk/middleware-ssec": "3.433.0", - "@aws-sdk/middleware-user-agent": "3.438.0", - "@aws-sdk/region-config-resolver": "3.433.0", - "@aws-sdk/signature-v4-multi-region": "3.437.0", - "@aws-sdk/types": "3.433.0", - "@aws-sdk/util-endpoints": "3.438.0", - "@aws-sdk/util-user-agent-browser": "3.433.0", - "@aws-sdk/util-user-agent-node": "3.437.0", - "@aws-sdk/xml-builder": "3.310.0", - "@smithy/config-resolver": "^2.0.16", - "@smithy/eventstream-serde-browser": "^2.0.12", - "@smithy/eventstream-serde-config-resolver": "^2.0.12", - "@smithy/eventstream-serde-node": "^2.0.12", - "@smithy/fetch-http-handler": "^2.2.4", - "@smithy/hash-blob-browser": "^2.0.12", - "@smithy/hash-node": "^2.0.12", - "@smithy/hash-stream-node": "^2.0.12", - "@smithy/invalid-dependency": "^2.0.12", - "@smithy/md5-js": "^2.0.12", - "@smithy/middleware-content-length": "^2.0.14", - "@smithy/middleware-endpoint": "^2.1.3", - "@smithy/middleware-retry": "^2.0.18", - "@smithy/middleware-serde": "^2.0.12", - "@smithy/middleware-stack": "^2.0.6", - "@smithy/node-config-provider": "^2.1.3", - "@smithy/node-http-handler": "^2.1.8", - "@smithy/protocol-http": "^3.0.8", - "@smithy/smithy-client": "^2.1.12", - "@smithy/types": "^2.4.0", - "@smithy/url-parser": "^2.0.12", - "@smithy/util-base64": "^2.0.0", - "@smithy/util-body-length-browser": "^2.0.0", - "@smithy/util-body-length-node": "^2.1.0", - "@smithy/util-defaults-mode-browser": "^2.0.16", - "@smithy/util-defaults-mode-node": "^2.0.21", - "@smithy/util-endpoints": "^1.0.2", - "@smithy/util-retry": "^2.0.5", - "@smithy/util-stream": "^2.0.17", - "@smithy/util-utf8": "^2.0.0", - "@smithy/util-waiter": "^2.0.12", - "fast-xml-parser": "4.2.5", - "tslib": "^2.5.0" + "node_modules/@aws-crypto/util/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" }, "engines": { "node": ">=14.0.0" } }, - "node_modules/@aws-sdk/client-sso": { - "version": "3.445.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.445.0.tgz", - "integrity": "sha512-me4LvqNnu6kxi+sW7t0AgMv1Yi64ikas0x2+5jv23o6Csg32w0S0xOjCTKQYahOA5CMFunWvlkFIfxbqs+Uo7w==", - "dev": true, - "dependencies": { - "@aws-crypto/sha256-browser": "3.0.0", - "@aws-crypto/sha256-js": "3.0.0", - "@aws-sdk/core": "3.445.0", - "@aws-sdk/middleware-host-header": "3.433.0", - "@aws-sdk/middleware-logger": "3.433.0", - "@aws-sdk/middleware-recursion-detection": "3.433.0", - "@aws-sdk/middleware-user-agent": "3.438.0", - "@aws-sdk/region-config-resolver": "3.433.0", - "@aws-sdk/types": "3.433.0", - "@aws-sdk/util-endpoints": "3.438.0", - "@aws-sdk/util-user-agent-browser": "3.433.0", - "@aws-sdk/util-user-agent-node": "3.437.0", - "@smithy/config-resolver": "^2.0.16", - "@smithy/fetch-http-handler": "^2.2.4", - "@smithy/hash-node": "^2.0.12", - "@smithy/invalid-dependency": "^2.0.12", - "@smithy/middleware-content-length": "^2.0.14", - "@smithy/middleware-endpoint": "^2.1.3", - "@smithy/middleware-retry": "^2.0.18", - "@smithy/middleware-serde": "^2.0.12", - "@smithy/middleware-stack": "^2.0.6", - "@smithy/node-config-provider": "^2.1.3", - "@smithy/node-http-handler": "^2.1.8", - "@smithy/protocol-http": "^3.0.8", - "@smithy/smithy-client": "^2.1.12", - "@smithy/types": "^2.4.0", - "@smithy/url-parser": "^2.0.12", - "@smithy/util-base64": "^2.0.0", - "@smithy/util-body-length-browser": "^2.0.0", - "@smithy/util-body-length-node": "^2.1.0", - "@smithy/util-defaults-mode-browser": "^2.0.16", - "@smithy/util-defaults-mode-node": "^2.0.21", - "@smithy/util-endpoints": "^1.0.2", - "@smithy/util-retry": "^2.0.5", - "@smithy/util-utf8": "^2.0.0", - "tslib": "^2.5.0" + "node_modules/@aws-crypto/util/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" }, "engines": { "node": ">=14.0.0" } }, + "node_modules/@aws-sdk/client-s3": { + "version": "3.624.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.624.0.tgz", + "integrity": "sha512-A18tgTKC4ZTAwV8i3pkyAL1XDLgH7WGS5hZA/0FOntI5l+icztGZFF8CdeYWEAFnZA7SfHK6vmtEbIQDOzTTAA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha1-browser": "5.2.0", + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/client-sso-oidc": "3.624.0", + "@aws-sdk/client-sts": "3.624.0", + "@aws-sdk/core": "3.624.0", + "@aws-sdk/credential-provider-node": "3.624.0", + "@aws-sdk/middleware-bucket-endpoint": "3.620.0", + "@aws-sdk/middleware-expect-continue": "3.620.0", + "@aws-sdk/middleware-flexible-checksums": "3.620.0", + "@aws-sdk/middleware-host-header": "3.620.0", + "@aws-sdk/middleware-location-constraint": "3.609.0", + "@aws-sdk/middleware-logger": "3.609.0", + "@aws-sdk/middleware-recursion-detection": "3.620.0", + "@aws-sdk/middleware-sdk-s3": "3.624.0", + "@aws-sdk/middleware-ssec": "3.609.0", + "@aws-sdk/middleware-user-agent": "3.620.0", + "@aws-sdk/region-config-resolver": "3.614.0", + "@aws-sdk/signature-v4-multi-region": "3.624.0", + "@aws-sdk/types": "3.609.0", + "@aws-sdk/util-endpoints": "3.614.0", + "@aws-sdk/util-user-agent-browser": "3.609.0", + "@aws-sdk/util-user-agent-node": "3.614.0", + "@aws-sdk/xml-builder": "3.609.0", + "@smithy/config-resolver": "^3.0.5", + "@smithy/core": "^2.3.2", + "@smithy/eventstream-serde-browser": "^3.0.5", + "@smithy/eventstream-serde-config-resolver": "^3.0.3", + "@smithy/eventstream-serde-node": "^3.0.4", + "@smithy/fetch-http-handler": "^3.2.4", + "@smithy/hash-blob-browser": "^3.1.2", + "@smithy/hash-node": "^3.0.3", + "@smithy/hash-stream-node": "^3.1.2", + "@smithy/invalid-dependency": "^3.0.3", + "@smithy/md5-js": "^3.0.3", + "@smithy/middleware-content-length": "^3.0.5", + "@smithy/middleware-endpoint": "^3.1.0", + "@smithy/middleware-retry": "^3.0.14", + "@smithy/middleware-serde": "^3.0.3", + "@smithy/middleware-stack": "^3.0.3", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/node-http-handler": "^3.1.4", + "@smithy/protocol-http": "^4.1.0", + "@smithy/smithy-client": "^3.1.12", + "@smithy/types": "^3.3.0", + "@smithy/url-parser": "^3.0.3", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.14", + "@smithy/util-defaults-mode-node": "^3.0.14", + "@smithy/util-endpoints": "^2.0.5", + "@smithy/util-middleware": "^3.0.3", + "@smithy/util-retry": "^3.0.3", + "@smithy/util-stream": "^3.1.3", + "@smithy/util-utf8": "^3.0.0", + "@smithy/util-waiter": "^3.1.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sso": { + "version": "3.624.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.624.0.tgz", + "integrity": "sha512-EX6EF+rJzMPC5dcdsu40xSi2To7GSvdGQNIpe97pD9WvZwM9tRNQnNM4T6HA4gjV1L6Jwk8rBlG/CnveXtLEMw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.624.0", + "@aws-sdk/middleware-host-header": "3.620.0", + "@aws-sdk/middleware-logger": "3.609.0", + "@aws-sdk/middleware-recursion-detection": "3.620.0", + "@aws-sdk/middleware-user-agent": "3.620.0", + "@aws-sdk/region-config-resolver": "3.614.0", + "@aws-sdk/types": "3.609.0", + "@aws-sdk/util-endpoints": "3.614.0", + "@aws-sdk/util-user-agent-browser": "3.609.0", + "@aws-sdk/util-user-agent-node": "3.614.0", + "@smithy/config-resolver": "^3.0.5", + "@smithy/core": "^2.3.2", + "@smithy/fetch-http-handler": "^3.2.4", + "@smithy/hash-node": "^3.0.3", + "@smithy/invalid-dependency": "^3.0.3", + "@smithy/middleware-content-length": "^3.0.5", + "@smithy/middleware-endpoint": "^3.1.0", + "@smithy/middleware-retry": "^3.0.14", + "@smithy/middleware-serde": "^3.0.3", + "@smithy/middleware-stack": "^3.0.3", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/node-http-handler": "^3.1.4", + "@smithy/protocol-http": "^4.1.0", + "@smithy/smithy-client": "^3.1.12", + "@smithy/types": "^3.3.0", + "@smithy/url-parser": "^3.0.3", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.14", + "@smithy/util-defaults-mode-node": "^3.0.14", + "@smithy/util-endpoints": "^2.0.5", + "@smithy/util-middleware": "^3.0.3", + "@smithy/util-retry": "^3.0.3", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sso-oidc": { + "version": "3.624.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.624.0.tgz", + "integrity": "sha512-Ki2uKYJKKtfHxxZsiMTOvJoVRP6b2pZ1u3rcUb2m/nVgBPUfLdl8ZkGpqE29I+t5/QaS/sEdbn6cgMUZwl+3Dg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.624.0", + "@aws-sdk/credential-provider-node": "3.624.0", + "@aws-sdk/middleware-host-header": "3.620.0", + "@aws-sdk/middleware-logger": "3.609.0", + "@aws-sdk/middleware-recursion-detection": "3.620.0", + "@aws-sdk/middleware-user-agent": "3.620.0", + "@aws-sdk/region-config-resolver": "3.614.0", + "@aws-sdk/types": "3.609.0", + "@aws-sdk/util-endpoints": "3.614.0", + "@aws-sdk/util-user-agent-browser": "3.609.0", + "@aws-sdk/util-user-agent-node": "3.614.0", + "@smithy/config-resolver": "^3.0.5", + "@smithy/core": "^2.3.2", + "@smithy/fetch-http-handler": "^3.2.4", + "@smithy/hash-node": "^3.0.3", + "@smithy/invalid-dependency": "^3.0.3", + "@smithy/middleware-content-length": "^3.0.5", + "@smithy/middleware-endpoint": "^3.1.0", + "@smithy/middleware-retry": "^3.0.14", + "@smithy/middleware-serde": "^3.0.3", + "@smithy/middleware-stack": "^3.0.3", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/node-http-handler": "^3.1.4", + "@smithy/protocol-http": "^4.1.0", + "@smithy/smithy-client": "^3.1.12", + "@smithy/types": "^3.3.0", + "@smithy/url-parser": "^3.0.3", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.14", + "@smithy/util-defaults-mode-node": "^3.0.14", + "@smithy/util-endpoints": "^2.0.5", + "@smithy/util-middleware": "^3.0.3", + "@smithy/util-retry": "^3.0.3", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.624.0" + } + }, "node_modules/@aws-sdk/client-sts": { - "version": "3.445.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.445.0.tgz", - "integrity": "sha512-ogbdqrS8x9O5BTot826iLnTQ6i4/F5BSi/74gycneCxYmAnYnyUBNOWVnynv6XZiEWyDJQCU2UtMd52aNGW1GA==", - "dev": true, - "dependencies": { - "@aws-crypto/sha256-browser": "3.0.0", - "@aws-crypto/sha256-js": "3.0.0", - "@aws-sdk/core": "3.445.0", - "@aws-sdk/credential-provider-node": "3.445.0", - "@aws-sdk/middleware-host-header": "3.433.0", - "@aws-sdk/middleware-logger": "3.433.0", - "@aws-sdk/middleware-recursion-detection": "3.433.0", - "@aws-sdk/middleware-sdk-sts": "3.433.0", - "@aws-sdk/middleware-signing": "3.433.0", - "@aws-sdk/middleware-user-agent": "3.438.0", - "@aws-sdk/region-config-resolver": "3.433.0", - "@aws-sdk/types": "3.433.0", - "@aws-sdk/util-endpoints": "3.438.0", - "@aws-sdk/util-user-agent-browser": "3.433.0", - "@aws-sdk/util-user-agent-node": "3.437.0", - "@smithy/config-resolver": "^2.0.16", - "@smithy/fetch-http-handler": "^2.2.4", - "@smithy/hash-node": "^2.0.12", - "@smithy/invalid-dependency": "^2.0.12", - "@smithy/middleware-content-length": "^2.0.14", - "@smithy/middleware-endpoint": "^2.1.3", - "@smithy/middleware-retry": "^2.0.18", - "@smithy/middleware-serde": "^2.0.12", - "@smithy/middleware-stack": "^2.0.6", - "@smithy/node-config-provider": "^2.1.3", - "@smithy/node-http-handler": "^2.1.8", - "@smithy/protocol-http": "^3.0.8", - "@smithy/smithy-client": "^2.1.12", - "@smithy/types": "^2.4.0", - "@smithy/url-parser": "^2.0.12", - "@smithy/util-base64": "^2.0.0", - "@smithy/util-body-length-browser": "^2.0.0", - "@smithy/util-body-length-node": "^2.1.0", - "@smithy/util-defaults-mode-browser": "^2.0.16", - "@smithy/util-defaults-mode-node": "^2.0.21", - "@smithy/util-endpoints": "^1.0.2", - "@smithy/util-retry": "^2.0.5", - "@smithy/util-utf8": "^2.0.0", - "fast-xml-parser": "4.2.5", - "tslib": "^2.5.0" + "version": "3.624.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.624.0.tgz", + "integrity": "sha512-k36fLZCb2nfoV/DKK3jbRgO/Yf7/R80pgYfMiotkGjnZwDmRvNN08z4l06L9C+CieazzkgRxNUzyppsYcYsQaw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/client-sso-oidc": "3.624.0", + "@aws-sdk/core": "3.624.0", + "@aws-sdk/credential-provider-node": "3.624.0", + "@aws-sdk/middleware-host-header": "3.620.0", + "@aws-sdk/middleware-logger": "3.609.0", + "@aws-sdk/middleware-recursion-detection": "3.620.0", + "@aws-sdk/middleware-user-agent": "3.620.0", + "@aws-sdk/region-config-resolver": "3.614.0", + "@aws-sdk/types": "3.609.0", + "@aws-sdk/util-endpoints": "3.614.0", + "@aws-sdk/util-user-agent-browser": "3.609.0", + "@aws-sdk/util-user-agent-node": "3.614.0", + "@smithy/config-resolver": "^3.0.5", + "@smithy/core": "^2.3.2", + "@smithy/fetch-http-handler": "^3.2.4", + "@smithy/hash-node": "^3.0.3", + "@smithy/invalid-dependency": "^3.0.3", + "@smithy/middleware-content-length": "^3.0.5", + "@smithy/middleware-endpoint": "^3.1.0", + "@smithy/middleware-retry": "^3.0.14", + "@smithy/middleware-serde": "^3.0.3", + "@smithy/middleware-stack": "^3.0.3", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/node-http-handler": "^3.1.4", + "@smithy/protocol-http": "^4.1.0", + "@smithy/smithy-client": "^3.1.12", + "@smithy/types": "^3.3.0", + "@smithy/url-parser": "^3.0.3", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.14", + "@smithy/util-defaults-mode-node": "^3.0.14", + "@smithy/util-endpoints": "^2.0.5", + "@smithy/util-middleware": "^3.0.3", + "@smithy/util-retry": "^3.0.3", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=14.0.0" + "node": ">=16.0.0" } }, "node_modules/@aws-sdk/core": { - "version": "3.445.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.445.0.tgz", - "integrity": "sha512-6GYLElUG1QTOdmXG8zXa+Ull9IUeSeItKDYHKzHYfIkbsagMfYlf7wm9XIYlatjtgodNfZ3gPHAJfRyPmwKrsg==", - "dev": true, - "dependencies": { - "@smithy/smithy-client": "^2.1.12", - "tslib": "^2.5.0" + "version": "3.624.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.624.0.tgz", + "integrity": "sha512-WyFmPbhRIvtWi7hBp8uSFy+iPpj8ccNV/eX86hwF4irMjfc/FtsGVIAeBXxXM/vGCjkdfEzOnl+tJ2XACD4OXg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^2.3.2", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/protocol-http": "^4.1.0", + "@smithy/signature-v4": "^4.1.0", + "@smithy/smithy-client": "^3.1.12", + "@smithy/types": "^3.3.0", + "@smithy/util-middleware": "^3.0.3", + "fast-xml-parser": "4.4.1", + "tslib": "^2.6.2" }, "engines": { - "node": ">=14.0.0" + "node": ">=16.0.0" } }, "node_modules/@aws-sdk/credential-provider-env": { - "version": "3.433.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.433.0.tgz", - "integrity": "sha512-Vl7Qz5qYyxBurMn6hfSiNJeUHSqfVUlMt0C1Bds3tCkl3IzecRWwyBOlxtxO3VCrgVeW3HqswLzCvhAFzPH6nQ==", + "version": "3.620.1", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.620.1.tgz", + "integrity": "sha512-ExuILJ2qLW5ZO+rgkNRj0xiAipKT16Rk77buvPP8csR7kkCflT/gXTyzRe/uzIiETTxM7tr8xuO9MP/DQXqkfg==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.433.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/types": "^2.4.0", - "tslib": "^2.5.0" + "@aws-sdk/types": "3.609.0", + "@smithy/property-provider": "^3.1.3", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=14.0.0" + "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/credential-provider-ini": { - "version": "3.445.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.445.0.tgz", - "integrity": "sha512-R7IYSGjNZ5KKJwQJ2HNPemjpAMWvdce91i8w+/aHfqeGfTXrmYJu99PeGRyyBTKEumBaojyjTRvmO8HzS+/l7g==", + "node_modules/@aws-sdk/credential-provider-http": { + "version": "3.622.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.622.0.tgz", + "integrity": "sha512-VUHbr24Oll1RK3WR8XLUugLpgK9ZuxEm/NVeVqyFts1Ck9gsKpRg1x4eH7L7tW3SJ4TDEQNMbD7/7J+eoL2svg==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@aws-sdk/credential-provider-env": "3.433.0", - "@aws-sdk/credential-provider-process": "3.433.0", - "@aws-sdk/credential-provider-sso": "3.445.0", - "@aws-sdk/credential-provider-web-identity": "3.433.0", - "@aws-sdk/types": "3.433.0", - "@smithy/credential-provider-imds": "^2.0.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/shared-ini-file-loader": "^2.0.6", - "@smithy/types": "^2.4.0", - "tslib": "^2.5.0" + "@aws-sdk/types": "3.609.0", + "@smithy/fetch-http-handler": "^3.2.4", + "@smithy/node-http-handler": "^3.1.4", + "@smithy/property-provider": "^3.1.3", + "@smithy/protocol-http": "^4.1.0", + "@smithy/smithy-client": "^3.1.12", + "@smithy/types": "^3.3.0", + "@smithy/util-stream": "^3.1.3", + "tslib": "^2.6.2" }, "engines": { - "node": ">=14.0.0" + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.624.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.624.0.tgz", + "integrity": "sha512-mMoNIy7MO2WTBbdqMyLpbt6SZpthE6e0GkRYpsd0yozPt0RZopcBhEh+HG1U9Y1PVODo+jcMk353vAi61CfnhQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.620.1", + "@aws-sdk/credential-provider-http": "3.622.0", + "@aws-sdk/credential-provider-process": "3.620.1", + "@aws-sdk/credential-provider-sso": "3.624.0", + "@aws-sdk/credential-provider-web-identity": "3.621.0", + "@aws-sdk/types": "3.609.0", + "@smithy/credential-provider-imds": "^3.2.0", + "@smithy/property-provider": "^3.1.3", + "@smithy/shared-ini-file-loader": "^3.1.4", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.624.0" } }, "node_modules/@aws-sdk/credential-provider-node": { - "version": "3.445.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.445.0.tgz", - "integrity": "sha512-zI4k4foSjQRKNEsouculRcz7IbLfuqdFxypDLYwn+qPNMqJwWJ7VxOOeBSPUpHFcd7CLSfbHN2JAhQ7M02gPTA==", - "dev": true, - "dependencies": { - "@aws-sdk/credential-provider-env": "3.433.0", - "@aws-sdk/credential-provider-ini": "3.445.0", - "@aws-sdk/credential-provider-process": "3.433.0", - "@aws-sdk/credential-provider-sso": "3.445.0", - "@aws-sdk/credential-provider-web-identity": "3.433.0", - "@aws-sdk/types": "3.433.0", - "@smithy/credential-provider-imds": "^2.0.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/shared-ini-file-loader": "^2.0.6", - "@smithy/types": "^2.4.0", - "tslib": "^2.5.0" + "version": "3.624.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.624.0.tgz", + "integrity": "sha512-vYyGK7oNpd81BdbH5IlmQ6zfaQqU+rPwsKTDDBeLRjshtrGXOEpfoahVpG9PX0ibu32IOWp4ZyXBNyVrnvcMOw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.620.1", + "@aws-sdk/credential-provider-http": "3.622.0", + "@aws-sdk/credential-provider-ini": "3.624.0", + "@aws-sdk/credential-provider-process": "3.620.1", + "@aws-sdk/credential-provider-sso": "3.624.0", + "@aws-sdk/credential-provider-web-identity": "3.621.0", + "@aws-sdk/types": "3.609.0", + "@smithy/credential-provider-imds": "^3.2.0", + "@smithy/property-provider": "^3.1.3", + "@smithy/shared-ini-file-loader": "^3.1.4", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=14.0.0" + "node": ">=16.0.0" } }, "node_modules/@aws-sdk/credential-provider-process": { - "version": "3.433.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.433.0.tgz", - "integrity": "sha512-W7FcGlQjio9Y/PepcZGRyl5Bpwb0uWU7qIUCh+u4+q2mW4D5ZngXg8V/opL9/I/p4tUH9VXZLyLGwyBSkdhL+A==", + "version": "3.620.1", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.620.1.tgz", + "integrity": "sha512-hWqFMidqLAkaV9G460+1at6qa9vySbjQKKc04p59OT7lZ5cO5VH5S4aI05e+m4j364MBROjjk2ugNvfNf/8ILg==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.433.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/shared-ini-file-loader": "^2.0.6", - "@smithy/types": "^2.4.0", - "tslib": "^2.5.0" + "@aws-sdk/types": "3.609.0", + "@smithy/property-provider": "^3.1.3", + "@smithy/shared-ini-file-loader": "^3.1.4", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=14.0.0" + "node": ">=16.0.0" } }, "node_modules/@aws-sdk/credential-provider-sso": { - "version": "3.445.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.445.0.tgz", - "integrity": "sha512-gJz7kAiDecdhtApgXnxfZsXKsww8BnifDF9MAx9Dr4X6no47qYsCCS3XPuEyRiF9VebXvHOH0H260Zp3bVyniQ==", - "dev": true, - "dependencies": { - "@aws-sdk/client-sso": "3.445.0", - "@aws-sdk/token-providers": "3.438.0", - "@aws-sdk/types": "3.433.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/shared-ini-file-loader": "^2.0.6", - "@smithy/types": "^2.4.0", - "tslib": "^2.5.0" + "version": "3.624.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.624.0.tgz", + "integrity": "sha512-A02bayIjU9APEPKr3HudrFHEx0WfghoSPsPopckDkW7VBqO4wizzcxr75Q9A3vNX+cwg0wCN6UitTNe6pVlRaQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/client-sso": "3.624.0", + "@aws-sdk/token-providers": "3.614.0", + "@aws-sdk/types": "3.609.0", + "@smithy/property-provider": "^3.1.3", + "@smithy/shared-ini-file-loader": "^3.1.4", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=14.0.0" + "node": ">=16.0.0" } }, "node_modules/@aws-sdk/credential-provider-web-identity": { - "version": "3.433.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.433.0.tgz", - "integrity": "sha512-RlwjP1I5wO+aPpwyCp23Mk8nmRbRL33hqRASy73c4JA2z2YiRua+ryt6MalIxehhwQU6xvXUKulJnPG9VaMFZg==", + "version": "3.621.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.621.0.tgz", + "integrity": "sha512-w7ASSyfNvcx7+bYGep3VBgC3K6vEdLmlpjT7nSIHxxQf+WSdvy+HynwJosrpZax0sK5q0D1Jpn/5q+r5lwwW6w==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.433.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/types": "^2.4.0", - "tslib": "^2.5.0" + "@aws-sdk/types": "3.609.0", + "@smithy/property-provider": "^3.1.3", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=14.0.0" + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.621.0" } }, "node_modules/@aws-sdk/lib-storage": { - "version": "3.445.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/lib-storage/-/lib-storage-3.445.0.tgz", - "integrity": "sha512-sCP3lh71oMkx/B3+tSOGr81cff1Z1Yy5ejh5xa/YuH6OefQUFBM7/EC0CJiNfVXemh3D6O+biKETL+t2rAiZoQ==", + "version": "3.624.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/lib-storage/-/lib-storage-3.624.0.tgz", + "integrity": "sha512-MdK4eMWh7Nl5a2RB0jfbKtACUip6wMlAbUVSPexkwOpLEwL8KHtXW0kIv9PkwQd4cvZnuUV9b+CyQ7Y+GkTyuA==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/abort-controller": "^2.0.1", - "@smithy/middleware-endpoint": "^2.1.3", - "@smithy/smithy-client": "^2.1.12", + "@smithy/abort-controller": "^3.1.1", + "@smithy/middleware-endpoint": "^3.1.0", + "@smithy/smithy-client": "^3.1.12", "buffer": "5.6.0", "events": "3.3.0", "stream-browserify": "3.0.0", - "tslib": "^2.5.0" + "tslib": "^2.6.2" }, "engines": { - "node": ">=14.0.0" + "node": ">=16.0.0" }, "peerDependencies": { - "@aws-sdk/client-s3": "^3.0.0" + "@aws-sdk/client-s3": "^3.624.0" } }, "node_modules/@aws-sdk/middleware-bucket-endpoint": { - "version": "3.433.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.433.0.tgz", - "integrity": "sha512-Lk1xIu2tWTRa1zDw5hCF1RrpWQYSodUhrS/q3oKz8IAoFqEy+lNaD5jx+fycuZb5EkE4IzWysT+8wVkd0mAnOg==", - "dev": true, - "dependencies": { - "@aws-sdk/types": "3.433.0", - "@aws-sdk/util-arn-parser": "3.310.0", - "@smithy/node-config-provider": "^2.1.3", - "@smithy/protocol-http": "^3.0.8", - "@smithy/types": "^2.4.0", - "@smithy/util-config-provider": "^2.0.0", - "tslib": "^2.5.0" + "version": "3.620.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.620.0.tgz", + "integrity": "sha512-eGLL0W6L3HDb3OACyetZYOWpHJ+gLo0TehQKeQyy2G8vTYXqNTeqYhuI6up9HVjBzU9eQiULVQETmgQs7TFaRg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@aws-sdk/util-arn-parser": "3.568.0", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/protocol-http": "^4.1.0", + "@smithy/types": "^3.3.0", + "@smithy/util-config-provider": "^3.0.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=14.0.0" + "node": ">=16.0.0" } }, "node_modules/@aws-sdk/middleware-expect-continue": { - "version": "3.433.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.433.0.tgz", - "integrity": "sha512-Uq2rPIsjz0CR2sulM/HyYr5WiqiefrSRLdwUZuA7opxFSfE808w5DBWSprHxbH3rbDSQR4nFiOiVYIH8Eth7nA==", + "version": "3.620.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.620.0.tgz", + "integrity": "sha512-QXeRFMLfyQ31nAHLbiTLtk0oHzG9QLMaof5jIfqcUwnOkO8YnQdeqzakrg1Alpy/VQ7aqzIi8qypkBe2KXZz0A==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.433.0", - "@smithy/protocol-http": "^3.0.8", - "@smithy/types": "^2.4.0", - "tslib": "^2.5.0" + "@aws-sdk/types": "3.609.0", + "@smithy/protocol-http": "^4.1.0", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=14.0.0" + "node": ">=16.0.0" } }, "node_modules/@aws-sdk/middleware-flexible-checksums": { - "version": "3.433.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.433.0.tgz", - "integrity": "sha512-Ptssx373+I7EzFUWjp/i/YiNFt6I6sDuRHz6DOUR9nmmRTlHHqmdcBXlJL2d9wwFxoBRCN8/PXGsTc/DJ4c95Q==", - "dev": true, - "dependencies": { - "@aws-crypto/crc32": "3.0.0", - "@aws-crypto/crc32c": "3.0.0", - "@aws-sdk/types": "3.433.0", - "@smithy/is-array-buffer": "^2.0.0", - "@smithy/protocol-http": "^3.0.8", - "@smithy/types": "^2.4.0", - "@smithy/util-utf8": "^2.0.0", - "tslib": "^2.5.0" + "version": "3.620.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.620.0.tgz", + "integrity": "sha512-ftz+NW7qka2sVuwnnO1IzBku5ccP+s5qZGeRTPgrKB7OzRW85gthvIo1vQR2w+OwHFk7WJbbhhWwbCbktnP4UA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/crc32": "5.2.0", + "@aws-crypto/crc32c": "5.2.0", + "@aws-sdk/types": "3.609.0", + "@smithy/is-array-buffer": "^3.0.0", + "@smithy/protocol-http": "^4.1.0", + "@smithy/types": "^3.3.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=14.0.0" + "node": ">=16.0.0" } }, "node_modules/@aws-sdk/middleware-host-header": { - "version": "3.433.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.433.0.tgz", - "integrity": "sha512-mBTq3UWv1UzeHG+OfUQ2MB/5GEkt5LTKFaUqzL7ESwzW8XtpBgXnjZvIwu3Vcd3sEetMwijwaGiJhY0ae/YyaA==", + "version": "3.620.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.620.0.tgz", + "integrity": "sha512-VMtPEZwqYrII/oUkffYsNWY9PZ9xpNJpMgmyU0rlDQ25O1c0Hk3fJmZRe6pEkAJ0omD7kLrqGl1DUjQVxpd/Rg==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.433.0", - "@smithy/protocol-http": "^3.0.8", - "@smithy/types": "^2.4.0", - "tslib": "^2.5.0" + "@aws-sdk/types": "3.609.0", + "@smithy/protocol-http": "^4.1.0", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=14.0.0" + "node": ">=16.0.0" } }, "node_modules/@aws-sdk/middleware-location-constraint": { - "version": "3.433.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-location-constraint/-/middleware-location-constraint-3.433.0.tgz", - "integrity": "sha512-2YD860TGntwZifIUbxm+lFnNJJhByR/RB/+fV1I8oGKg+XX2rZU+94pRfHXRywoZKlCA0L+LGDA1I56jxrB9sw==", + "version": "3.609.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-location-constraint/-/middleware-location-constraint-3.609.0.tgz", + "integrity": "sha512-xzsdoTkszGVqGVPjUmgoP7TORiByLueMHieI1fhQL888WPdqctwAx3ES6d/bA9Q/i8jnc6hs+Fjhy8UvBTkE9A==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.433.0", - "@smithy/types": "^2.4.0", - "tslib": "^2.5.0" + "@aws-sdk/types": "3.609.0", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=14.0.0" + "node": ">=16.0.0" } }, "node_modules/@aws-sdk/middleware-logger": { - "version": "3.433.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.433.0.tgz", - "integrity": "sha512-We346Fb5xGonTGVZC9Nvqtnqy74VJzYuTLLiuuftA5sbNzftBDy/22QCfvYSTOAl3bvif+dkDUzQY2ihc5PwOQ==", + "version": "3.609.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.609.0.tgz", + "integrity": "sha512-S62U2dy4jMDhDFDK5gZ4VxFdWzCtLzwbYyFZx2uvPYTECkepLUfzLic2BHg2Qvtu4QjX+oGE3P/7fwaGIsGNuQ==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.433.0", - "@smithy/types": "^2.4.0", - "tslib": "^2.5.0" + "@aws-sdk/types": "3.609.0", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=14.0.0" + "node": ">=16.0.0" } }, "node_modules/@aws-sdk/middleware-recursion-detection": { - "version": "3.433.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.433.0.tgz", - "integrity": "sha512-HEvYC9PQlWY/ccUYtLvAlwwf1iCif2TSAmLNr3YTBRVa98x6jKL0hlCrHWYklFeqOGSKy6XhE+NGJMUII0/HaQ==", + "version": "3.620.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.620.0.tgz", + "integrity": "sha512-nh91S7aGK3e/o1ck64sA/CyoFw+gAYj2BDOnoNa6ouyCrVJED96ZXWbhye/fz9SgmNUZR2g7GdVpiLpMKZoI5w==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.433.0", - "@smithy/protocol-http": "^3.0.8", - "@smithy/types": "^2.4.0", - "tslib": "^2.5.0" + "@aws-sdk/types": "3.609.0", + "@smithy/protocol-http": "^4.1.0", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=14.0.0" + "node": ">=16.0.0" } }, "node_modules/@aws-sdk/middleware-sdk-s3": { - "version": "3.440.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.440.0.tgz", - "integrity": "sha512-DVTSr+82Z8jR9xTwDN3YHzxX7qvi0n96V92OfxvSRDq2BldCEx/KEL1orUZjw97SAXhINOlUWjRR7j4HpwWQtQ==", - "dev": true, - "dependencies": { - "@aws-sdk/types": "3.433.0", - "@aws-sdk/util-arn-parser": "3.310.0", - "@smithy/protocol-http": "^3.0.8", - "@smithy/smithy-client": "^2.1.12", - "@smithy/types": "^2.4.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/middleware-sdk-sts": { - "version": "3.433.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-sts/-/middleware-sdk-sts-3.433.0.tgz", - "integrity": "sha512-ORYbJnBejUyonFl5FwIqhvI3Cq6sAp9j+JpkKZtFNma9tFPdrhmYgfCeNH32H/wGTQV/tUoQ3luh0gA4cuk6DA==", - "dev": true, - "dependencies": { - "@aws-sdk/middleware-signing": "3.433.0", - "@aws-sdk/types": "3.433.0", - "@smithy/types": "^2.4.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/middleware-signing": { - "version": "3.433.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-signing/-/middleware-signing-3.433.0.tgz", - "integrity": "sha512-jxPvt59NZo/epMNLNTu47ikmP8v0q217I6bQFGJG7JVFnfl36zDktMwGw+0xZR80qiK47/2BWrNpta61Zd2FxQ==", - "dev": true, - "dependencies": { - "@aws-sdk/types": "3.433.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/protocol-http": "^3.0.8", - "@smithy/signature-v4": "^2.0.0", - "@smithy/types": "^2.4.0", - "@smithy/util-middleware": "^2.0.5", - "tslib": "^2.5.0" + "version": "3.624.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.624.0.tgz", + "integrity": "sha512-HUiaZ6+JXcG0qQda10ZxDGJvbT71YUp1zX+oikIsfTUeq0N75O82OY3Noqd7cyjEVtsGSo/y0e6U3aV1hO+wPw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.624.0", + "@aws-sdk/types": "3.609.0", + "@aws-sdk/util-arn-parser": "3.568.0", + "@smithy/core": "^2.3.2", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/protocol-http": "^4.1.0", + "@smithy/signature-v4": "^4.1.0", + "@smithy/smithy-client": "^3.1.12", + "@smithy/types": "^3.3.0", + "@smithy/util-config-provider": "^3.0.0", + "@smithy/util-middleware": "^3.0.3", + "@smithy/util-stream": "^3.1.3", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=14.0.0" + "node": ">=16.0.0" } }, "node_modules/@aws-sdk/middleware-ssec": { - "version": "3.433.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-ssec/-/middleware-ssec-3.433.0.tgz", - "integrity": "sha512-2AMaPx0kYfCiekxoL7aqFqSSoA9du+yI4zefpQNLr+1cZOerYiDxdsZ4mbqStR1CVFaX6U6hrYokXzjInsvETw==", + "version": "3.609.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-ssec/-/middleware-ssec-3.609.0.tgz", + "integrity": "sha512-GZSD1s7+JswWOTamVap79QiDaIV7byJFssBW68GYjyRS5EBjNfwA/8s+6uE6g39R3ojyTbYOmvcANoZEhSULXg==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.433.0", - "@smithy/types": "^2.4.0", - "tslib": "^2.5.0" + "@aws-sdk/types": "3.609.0", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=14.0.0" + "node": ">=16.0.0" } }, "node_modules/@aws-sdk/middleware-user-agent": { - "version": "3.438.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.438.0.tgz", - "integrity": "sha512-a+xHT1wOxT6EA6YyLmrfaroKWOkwwyiktUfXKM0FsUutGzNi4fKhb5NZ2al58NsXzHgHFrasSDp+Lqbd/X2cEw==", + "version": "3.620.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.620.0.tgz", + "integrity": "sha512-bvS6etn+KsuL32ubY5D3xNof1qkenpbJXf/ugGXbg0n98DvDFQ/F+SMLxHgbnER5dsKYchNnhmtI6/FC3HFu/A==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.433.0", - "@aws-sdk/util-endpoints": "3.438.0", - "@smithy/protocol-http": "^3.0.8", - "@smithy/types": "^2.4.0", - "tslib": "^2.5.0" + "@aws-sdk/types": "3.609.0", + "@aws-sdk/util-endpoints": "3.614.0", + "@smithy/protocol-http": "^4.1.0", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=14.0.0" + "node": ">=16.0.0" } }, "node_modules/@aws-sdk/region-config-resolver": { - "version": "3.433.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.433.0.tgz", - "integrity": "sha512-xpjRjCZW+CDFdcMmmhIYg81ST5UAnJh61IHziQEk0FXONrg4kjyYPZAOjEdzXQ+HxJQuGQLKPhRdzxmQnbX7pg==", + "version": "3.614.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.614.0.tgz", + "integrity": "sha512-vDCeMXvic/LU0KFIUjpC3RiSTIkkvESsEfbVHiHH0YINfl8HnEqR5rj+L8+phsCeVg2+LmYwYxd5NRz4PHxt5g==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/node-config-provider": "^2.1.3", - "@smithy/types": "^2.4.0", - "@smithy/util-config-provider": "^2.0.0", - "@smithy/util-middleware": "^2.0.5", - "tslib": "^2.5.0" + "@aws-sdk/types": "3.609.0", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/types": "^3.3.0", + "@smithy/util-config-provider": "^3.0.0", + "@smithy/util-middleware": "^3.0.3", + "tslib": "^2.6.2" }, "engines": { - "node": ">=14.0.0" + "node": ">=16.0.0" } }, "node_modules/@aws-sdk/signature-v4-multi-region": { - "version": "3.437.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.437.0.tgz", - "integrity": "sha512-MmrqudssOs87JgVg7HGVdvJws/t4kcOrJJd+975ki+DPeSoyK2U4zBDfDkJ+n0tFuZBs3sLwLh0QXE7BV28rRA==", + "version": "3.624.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.624.0.tgz", + "integrity": "sha512-gu1SfCyUPnq4s0AI1xdAl0whHwhkTyltg4QZWc4vnZvEVudCpJVVxEcroUHYQIO51YyVUT9jSMS1SVRe5VqPEw==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.433.0", - "@smithy/protocol-http": "^3.0.8", - "@smithy/signature-v4": "^2.0.0", - "@smithy/types": "^2.4.0", - "tslib": "^2.5.0" + "@aws-sdk/middleware-sdk-s3": "3.624.0", + "@aws-sdk/types": "3.609.0", + "@smithy/protocol-http": "^4.1.0", + "@smithy/signature-v4": "^4.1.0", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=14.0.0" + "node": ">=16.0.0" } }, "node_modules/@aws-sdk/token-providers": { - "version": "3.438.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.438.0.tgz", - "integrity": "sha512-G2fUfTtU6/1ayYRMu0Pd9Ln4qYSvwJOWCqJMdkDgvXSwdgcOSOLsnAIk1AHGJDAvgLikdCzuyOsdJiexr9Vnww==", - "dev": true, - "dependencies": { - "@aws-crypto/sha256-browser": "3.0.0", - "@aws-crypto/sha256-js": "3.0.0", - "@aws-sdk/middleware-host-header": "3.433.0", - "@aws-sdk/middleware-logger": "3.433.0", - "@aws-sdk/middleware-recursion-detection": "3.433.0", - "@aws-sdk/middleware-user-agent": "3.438.0", - "@aws-sdk/region-config-resolver": "3.433.0", - "@aws-sdk/types": "3.433.0", - "@aws-sdk/util-endpoints": "3.438.0", - "@aws-sdk/util-user-agent-browser": "3.433.0", - "@aws-sdk/util-user-agent-node": "3.437.0", - "@smithy/config-resolver": "^2.0.16", - "@smithy/fetch-http-handler": "^2.2.4", - "@smithy/hash-node": "^2.0.12", - "@smithy/invalid-dependency": "^2.0.12", - "@smithy/middleware-content-length": "^2.0.14", - "@smithy/middleware-endpoint": "^2.1.3", - "@smithy/middleware-retry": "^2.0.18", - "@smithy/middleware-serde": "^2.0.12", - "@smithy/middleware-stack": "^2.0.6", - "@smithy/node-config-provider": "^2.1.3", - "@smithy/node-http-handler": "^2.1.8", - "@smithy/property-provider": "^2.0.0", - "@smithy/protocol-http": "^3.0.8", - "@smithy/shared-ini-file-loader": "^2.0.6", - "@smithy/smithy-client": "^2.1.12", - "@smithy/types": "^2.4.0", - "@smithy/url-parser": "^2.0.12", - "@smithy/util-base64": "^2.0.0", - "@smithy/util-body-length-browser": "^2.0.0", - "@smithy/util-body-length-node": "^2.1.0", - "@smithy/util-defaults-mode-browser": "^2.0.16", - "@smithy/util-defaults-mode-node": "^2.0.21", - "@smithy/util-endpoints": "^1.0.2", - "@smithy/util-retry": "^2.0.5", - "@smithy/util-utf8": "^2.0.0", - "tslib": "^2.5.0" + "version": "3.614.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.614.0.tgz", + "integrity": "sha512-okItqyY6L9IHdxqs+Z116y5/nda7rHxLvROxtAJdLavWTYDydxrZstImNgGWTeVdmc0xX2gJCI77UYUTQWnhRw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/property-provider": "^3.1.3", + "@smithy/shared-ini-file-loader": "^3.1.4", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=14.0.0" + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sso-oidc": "^3.614.0" } }, "node_modules/@aws-sdk/types": { - "version": "3.433.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.433.0.tgz", - "integrity": "sha512-0jEE2mSrNDd8VGFjTc1otYrwYPIkzZJEIK90ZxisKvQ/EURGBhNzWn7ejWB9XCMFT6XumYLBR0V9qq5UPisWtA==", + "version": "3.609.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.609.0.tgz", + "integrity": "sha512-+Tqnh9w0h2LcrUsdXyT1F8mNhXz+tVYBtP19LpeEGntmvHwa2XzvLUCWpoIAIVsHp5+HdB2X9Sn0KAtmbFXc2Q==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^2.4.0", - "tslib": "^2.5.0" + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=14.0.0" + "node": ">=16.0.0" } }, "node_modules/@aws-sdk/util-arn-parser": { - "version": "3.310.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-arn-parser/-/util-arn-parser-3.310.0.tgz", - "integrity": "sha512-jL8509owp/xB9+Or0pvn3Fe+b94qfklc2yPowZZIFAkFcCSIdkIglz18cPDWnYAcy9JGewpMS1COXKIUhZkJsA==", + "version": "3.568.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-arn-parser/-/util-arn-parser-3.568.0.tgz", + "integrity": "sha512-XUKJWWo+KOB7fbnPP0+g/o5Ulku/X53t7i/h+sPHr5xxYTJJ9CYnbToo95mzxe7xWvkLrsNtJ8L+MnNn9INs2w==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "tslib": "^2.5.0" + "tslib": "^2.6.2" }, "engines": { - "node": ">=14.0.0" + "node": ">=16.0.0" } }, "node_modules/@aws-sdk/util-endpoints": { - "version": "3.438.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.438.0.tgz", - "integrity": "sha512-6VyPTq1kN3GWxwFt5DdZfOsr6cJZPLjWh0troY/0uUv3hK74C9o3Y0Xf/z8UAUvQFkVqZse12O0/BgPVMImvfA==", + "version": "3.614.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.614.0.tgz", + "integrity": "sha512-wK2cdrXHH4oz4IomV/yrGkftU9A+ITB6nFL+rxxyO78is2ifHJpFdV4aqk4LSkXYPi6CXWNru/Dqc7yiKXgJPw==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.433.0", - "@smithy/util-endpoints": "^1.0.2", - "tslib": "^2.5.0" + "@aws-sdk/types": "3.609.0", + "@smithy/types": "^3.3.0", + "@smithy/util-endpoints": "^2.0.5", + "tslib": "^2.6.2" }, "engines": { - "node": ">=14.0.0" + "node": ">=16.0.0" } }, "node_modules/@aws-sdk/util-locate-window": { - "version": "3.535.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.535.0.tgz", - "integrity": "sha512-PHJ3SL6d2jpcgbqdgiPxkXpu7Drc2PYViwxSIqvvMKhDwzSB1W3mMvtpzwKM4IE7zLFodZo0GKjJ9AsoXndXhA==", + "version": "3.568.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.568.0.tgz", + "integrity": "sha512-3nh4TINkXYr+H41QaPelCceEB2FXP3fxp93YZXB/kqJvX0U9j0N0Uk45gvsjmEPzG8XxkPEeLIfT2I1M7A6Lig==", "dev": true, + "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" }, "engines": { - "node": ">=14.0.0" + "node": ">=16.0.0" } }, "node_modules/@aws-sdk/util-user-agent-browser": { - "version": "3.433.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.433.0.tgz", - "integrity": "sha512-2Cf/Lwvxbt5RXvWFXrFr49vXv0IddiUwrZoAiwhDYxvsh+BMnh+NUFot+ZQaTrk/8IPZVDeLPWZRdVy00iaVXQ==", + "version": "3.609.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.609.0.tgz", + "integrity": "sha512-fojPU+mNahzQ0YHYBsx0ZIhmMA96H+ZIZ665ObU9tl+SGdbLneVZVikGve+NmHTQwHzwkFsZYYnVKAkreJLAtA==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.433.0", - "@smithy/types": "^2.4.0", + "@aws-sdk/types": "3.609.0", + "@smithy/types": "^3.3.0", "bowser": "^2.11.0", - "tslib": "^2.5.0" + "tslib": "^2.6.2" } }, "node_modules/@aws-sdk/util-user-agent-node": { - "version": "3.437.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.437.0.tgz", - "integrity": "sha512-JVEcvWaniamtYVPem4UthtCNoTBCfFTwYj7Y3CrWZ2Qic4TqrwLkAfaBGtI2TGrhIClVr77uzLI6exqMTN7orA==", + "version": "3.614.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.614.0.tgz", + "integrity": "sha512-15ElZT88peoHnq5TEoEtZwoXTXRxNrk60TZNdpl/TUBJ5oNJ9Dqb5Z4ryb8ofN6nm9aFf59GVAerFDz8iUoHBA==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.433.0", - "@smithy/node-config-provider": "^2.1.3", - "@smithy/types": "^2.4.0", - "tslib": "^2.5.0" + "@aws-sdk/types": "3.609.0", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=14.0.0" + "node": ">=16.0.0" }, "peerDependencies": { "aws-crt": ">=1.0.0" @@ -861,34 +1010,28 @@ } } }, - "node_modules/@aws-sdk/util-utf8-browser": { - "version": "3.259.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8-browser/-/util-utf8-browser-3.259.0.tgz", - "integrity": "sha512-UvFa/vR+e19XookZF8RzFZBrw2EUkQWxiBW0yYQAhvk3C+QVGl0H3ouca8LDBlBfQKXwmW3huo/59H8rwb1wJw==", - "dev": true, - "dependencies": { - "tslib": "^2.3.1" - } - }, "node_modules/@aws-sdk/xml-builder": { - "version": "3.310.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.310.0.tgz", - "integrity": "sha512-TqELu4mOuSIKQCqj63fGVs86Yh+vBx5nHRpWKNUNhB2nPTpfbziTs5c1X358be3peVWA4wPxW7Nt53KIg1tnNw==", + "version": "3.609.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.609.0.tgz", + "integrity": "sha512-l9XxNcA4HX98rwCC2/KoiWcmEiRfZe4G+mYwDbCFT87JIMj6GBhLDkAzr/W8KAaA2IDr8Vc6J8fZPgVulxxfMA==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "tslib": "^2.5.0" + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=14.0.0" + "node": ">=16.0.0" } }, "node_modules/@babel/code-frame": { - "version": "7.24.2", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.2.tgz", - "integrity": "sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz", + "integrity": "sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/highlight": "^7.24.2", + "@babel/highlight": "^7.24.7", "picocolors": "^1.0.0" }, "engines": { @@ -896,10 +1039,11 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.24.4", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.4.tgz", - "integrity": "sha512-vg8Gih2MLK+kOkHJp4gBEIkyaIi00jgWot2D9QOmmfLC8jINSOzmCLta6Bvz/JSBCqnegV0L80jhxkol5GWNfQ==", + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.25.2.tgz", + "integrity": "sha512-bYcppcpKBvX4znYaPEeFau03bp89ShqNMLs+rmdptMw+heSZh9+z84d2YG+K7cYLbWwzdjtDoW/uqZmPjulClQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } @@ -935,12 +1079,13 @@ } }, "node_modules/@babel/generator": { - "version": "7.24.4", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.4.tgz", - "integrity": "sha512-Xd6+v6SnjWVx/nus+y0l1sxMOTOMBkyL4+BIdbALyatQnAe/SRVjANeDPSCYaX+i1iJmuGSKf3Z+E+V/va1Hvw==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.0.tgz", + "integrity": "sha512-3LEEcj3PVW8pW2R1SR1M89g/qrYk/m/mB/tLqn7dn4sbBUQyTqnlod+II2U4dqiGtUmkcnAmkMDralTFZttRiw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/types": "^7.24.0", + "@babel/types": "^7.25.0", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^2.5.1" @@ -950,38 +1095,42 @@ } }, "node_modules/@babel/helper-annotate-as-pure": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz", - "integrity": "sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.24.7.tgz", + "integrity": "sha512-BaDeOonYvhdKw+JoMVkAixAAJzG2jVPIwWoKBPdYuY9b452e2rPuI9QPYh3KpofZ3pW2akOmwZLOiOsHMiqRAg==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/types": "^7.22.5" + "@babel/types": "^7.24.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.22.15.tgz", - "integrity": "sha512-QkBXwGgaoC2GtGZRoma6kv7Szfv06khvhFav67ZExau2RaXzy8MpHSMO2PNoP2XtmQphJQRHFfg77Bq731Yizw==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.24.7.tgz", + "integrity": "sha512-xZeCVVdwb4MsDBkkyZ64tReWYrLRHlMN72vP7Bdm3OUOuyFZExhsHUUnuWnm2/XOlAJzR0LfPpB56WXZn0X/lA==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/types": "^7.22.15" + "@babel/traverse": "^7.24.7", + "@babel/types": "^7.24.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz", - "integrity": "sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==", + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.2.tgz", + "integrity": "sha512-U2U5LsSaZ7TAt3cfaymQ8WHh0pxvdHoEk6HVpaexxixjyEquMh0L0YNJNM6CTGKMXV1iksi0iZkGw4AcFkPaaw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.23.5", - "@babel/helper-validator-option": "^7.23.5", - "browserslist": "^4.22.2", + "@babel/compat-data": "^7.25.2", + "@babel/helper-validator-option": "^7.24.8", + "browserslist": "^4.23.1", "lru-cache": "^5.1.1", "semver": "^6.3.1" }, @@ -990,19 +1139,18 @@ } }, "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.24.4", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.24.4.tgz", - "integrity": "sha512-lG75yeuUSVu0pIcbhiYMXBXANHrpUPaOfu7ryAzskCgKUHuAxRQI5ssrtmF0X9UXldPlvT0XM/A4F44OXRt6iQ==", - "dev": true, - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-member-expression-to-functions": "^7.23.0", - "@babel/helper-optimise-call-expression": "^7.22.5", - "@babel/helper-replace-supers": "^7.24.1", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.25.0.tgz", + "integrity": "sha512-GYM6BxeQsETc9mnct+nIIpf63SAyzvyYN7UB/IlTyd+MBg06afFGp0mIeUqGyWgS2mxad6vqbMrHVlaL3m70sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.24.7", + "@babel/helper-member-expression-to-functions": "^7.24.8", + "@babel/helper-optimise-call-expression": "^7.24.7", + "@babel/helper-replace-supers": "^7.25.0", + "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7", + "@babel/traverse": "^7.25.0", "semver": "^6.3.1" }, "engines": { @@ -1013,12 +1161,13 @@ } }, "node_modules/@babel/helper-create-regexp-features-plugin": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.15.tgz", - "integrity": "sha512-29FkPLFjn4TPEa3RE7GpW+qbE8tlsu3jntNYNfcGsc49LphF1PQIiD+vMZ1z1xVOKt+93khA9tc2JBs3kBjA7w==", + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.25.2.tgz", + "integrity": "sha512-+wqVGP+DFmqwFD3EH6TMTfUNeqDehV3E/dl+Sd54eaXqm17tEUNbEIn4sVivVowbvUpOtIGxdo3GoXyDH9N/9g==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-annotate-as-pure": "^7.24.7", "regexpu-core": "^5.3.1", "semver": "^6.3.1" }, @@ -1045,75 +1194,45 @@ "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, - "node_modules/@babel/helper-environment-visitor": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", - "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-function-name": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", - "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", - "dev": true, - "dependencies": { - "@babel/template": "^7.22.15", - "@babel/types": "^7.23.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-hoist-variables": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", - "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", - "dev": true, - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.23.0.tgz", - "integrity": "sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.24.8.tgz", + "integrity": "sha512-LABppdt+Lp/RlBxqrh4qgf1oEH/WxdzQNDJIu5gC/W1GyvPVrOBiItmmM8wan2fm4oYqFuFfkXmlGpLQhPY8CA==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/types": "^7.23.0" + "@babel/traverse": "^7.24.8", + "@babel/types": "^7.24.8" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-imports": { - "version": "7.24.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.3.tgz", - "integrity": "sha512-viKb0F9f2s0BCS22QSF308z/+1YWKV/76mwt61NBzS5izMzDPwdq1pTrzf+Li3npBWX9KdQbkeCt1jSAM7lZqg==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.7.tgz", + "integrity": "sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/types": "^7.24.0" + "@babel/traverse": "^7.24.7", + "@babel/types": "^7.24.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz", - "integrity": "sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==", + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.25.2.tgz", + "integrity": "sha512-BjyRAbix6j/wv83ftcVJmBt72QtHI56C7JXZoG2xATiLpmoC7dpd8WnkikExHDVPpi/3qCmO6WY1EaXOluiecQ==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-module-imports": "^7.22.15", - "@babel/helper-simple-access": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/helper-validator-identifier": "^7.22.20" + "@babel/helper-module-imports": "^7.24.7", + "@babel/helper-simple-access": "^7.24.7", + "@babel/helper-validator-identifier": "^7.24.7", + "@babel/traverse": "^7.25.2" }, "engines": { "node": ">=6.9.0" @@ -1123,35 +1242,38 @@ } }, "node_modules/@babel/helper-optimise-call-expression": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.22.5.tgz", - "integrity": "sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.24.7.tgz", + "integrity": "sha512-jKiTsW2xmWwxT1ixIdfXUZp+P5yURx2suzLZr5Hi64rURpDYdMW0pv+Uf17EYk2Rd428Lx4tLsnjGJzYKDM/6A==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/types": "^7.22.5" + "@babel/types": "^7.24.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.0.tgz", - "integrity": "sha512-9cUznXMG0+FxRuJfvL82QlTqIzhVW9sL0KjMPHhAOOvpQGL8QtdxnBKILjBqxlHyliz0yCa1G903ZXI/FuHy2w==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.8.tgz", + "integrity": "sha512-FFWx5142D8h2Mgr/iPVGH5G7w6jDn4jUSpZTyDnQO0Yn7Ks2Kuz6Pci8H6MPCoUJegd/UZQ3tAvfLCxQSnWWwg==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-remap-async-to-generator": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.20.tgz", - "integrity": "sha512-pBGyV4uBqOns+0UvhsTO8qgl8hO89PmiDYv+/COyp1aeMcmfrfruz+/nCMFiYyFF/Knn0yfrC85ZzNFjembFTw==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.25.0.tgz", + "integrity": "sha512-NhavI2eWEIz/H9dbrG0TuOicDhNexze43i5z7lEqwYm0WEZVTwnPpA0EafUTP7+6/W79HWIP2cTe3Z5NiSTVpw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-wrap-function": "^7.22.20" + "@babel/helper-annotate-as-pure": "^7.24.7", + "@babel/helper-wrap-function": "^7.25.0", + "@babel/traverse": "^7.25.0" }, "engines": { "node": ">=6.9.0" @@ -1161,14 +1283,15 @@ } }, "node_modules/@babel/helper-replace-supers": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.24.1.tgz", - "integrity": "sha512-QCR1UqC9BzG5vZl8BMicmZ28RuUBnHhAMddD8yHFHDRH9lLTZ9uUPehX8ctVPT8l0TKblJidqcgUUKGVrePleQ==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.25.0.tgz", + "integrity": "sha512-q688zIvQVYtZu+i2PsdIu/uWGRpfxzr5WESsfpShfZECkO+d2o+WROWezCi/Q6kJ0tfPa5+pUGUlfx2HhrA3Bg==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-member-expression-to-functions": "^7.23.0", - "@babel/helper-optimise-call-expression": "^7.22.5" + "@babel/helper-member-expression-to-functions": "^7.24.8", + "@babel/helper-optimise-call-expression": "^7.24.7", + "@babel/traverse": "^7.25.0" }, "engines": { "node": ">=6.9.0" @@ -1178,77 +1301,73 @@ } }, "node_modules/@babel/helper-simple-access": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", - "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.24.7.tgz", + "integrity": "sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/types": "^7.22.5" + "@babel/traverse": "^7.24.7", + "@babel/types": "^7.24.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.22.5.tgz", - "integrity": "sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q==", - "dev": true, - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-split-export-declaration": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", - "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.24.7.tgz", + "integrity": "sha512-IO+DLT3LQUElMbpzlatRASEyQtfhSE0+m465v++3jyyXeBTBUjtVZg28/gHeV5mrTJqvEKhKroBGAvhW+qPHiQ==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/types": "^7.22.5" + "@babel/traverse": "^7.24.7", + "@babel/types": "^7.24.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-string-parser": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.1.tgz", - "integrity": "sha512-2ofRCjnnA9y+wk8b9IAREroeUP02KHp431N2mhKniy2yKIDKpbrHv9eXwm8cBeWQYcJmzv5qKCu65P47eCF7CQ==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.8.tgz", + "integrity": "sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", - "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz", + "integrity": "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-option": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz", - "integrity": "sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.24.8.tgz", + "integrity": "sha512-xb8t9tD1MHLungh/AIoWYN+gVHaB9kwlu8gffXGSt3FFEIT7RjS+xWbc2vUD1UTZdIpKj/ab3rdqJ7ufngyi2Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-wrap-function": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.22.20.tgz", - "integrity": "sha512-pms/UwkOpnQe/PDAEdV/d7dVCoBbB+R4FvYoHGZz+4VPcg7RtYy2KP7S2lbuWM6FCSgob5wshfGESbC/hzNXZw==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.25.0.tgz", + "integrity": "sha512-s6Q1ebqutSiZnEjaofc/UKDyC4SbzV5n5SrA2Gq8UawLycr3i04f1dX4OzoQVnexm6aOCh37SQNYlJ/8Ku+PMQ==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-function-name": "^7.22.5", - "@babel/template": "^7.22.15", - "@babel/types": "^7.22.19" + "@babel/template": "^7.25.0", + "@babel/traverse": "^7.25.0", + "@babel/types": "^7.25.0" }, "engines": { "node": ">=6.9.0" @@ -1269,12 +1388,13 @@ } }, "node_modules/@babel/highlight": { - "version": "7.24.2", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.2.tgz", - "integrity": "sha512-Yac1ao4flkTxTteCDZLEvdxg2fZfz1v8M4QpaGypq/WPDqg3ijHYbDfs+LG5hvzSoqaSZ9/Z9lKSP3CjZjv+pA==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.7.tgz", + "integrity": "sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.22.20", + "@babel/helper-validator-identifier": "^7.24.7", "chalk": "^2.4.2", "js-tokens": "^4.0.0", "picocolors": "^1.0.0" @@ -1284,10 +1404,14 @@ } }, "node_modules/@babel/parser": { - "version": "7.24.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.4.tgz", - "integrity": "sha512-zTvEBcghmeBma9QIGunWevvBAp4/Qu9Bdq+2k0Ot4fVMD6v3dsC9WOcRSKk7tRRyBM/53yKMJko9xOatGQAwSg==", + "version": "7.25.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.3.tgz", + "integrity": "sha512-iLTJKDbJ4hMvFPgQwwsVoxtHyWpKKPBrxkANrSYewDPaPpT5py5yeVkgPIJ7XYXhndxJpaA3PyALSXQ7u8e/Dw==", "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.25.2" + }, "bin": { "parser": "bin/babel-parser.js" }, @@ -1295,13 +1419,47 @@ "node": ">=6.0.0" } }, + "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": { + "version": "7.25.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.25.3.tgz", + "integrity": "sha512-wUrcsxZg6rqBXG05HG1FPYgsP6EvwF4WpBbxIpWIIYnH8wG0gzx3yZY3dtEHas4sTAOGkbTsc9EGPxwff8lRoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.8", + "@babel/traverse": "^7.25.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-safari-class-field-initializer-scope": { + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.25.0.tgz", + "integrity": "sha512-Bm4bH2qsX880b/3ziJ8KD711LT7z4u8CFudmjqle65AZj/HNUFhEf90dqYv6O86buWvSBmeQDjv0Tn2aF/bIBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.8" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.24.1.tgz", - "integrity": "sha512-y4HqEnkelJIOQGd+3g1bTeKsA5c6qM7eOn7VggGVbBc0y8MLSKHacwcIE2PplNlQSj0PqS9rrXL/nkPVK+kUNg==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.25.0.tgz", + "integrity": "sha512-lXwdNZtTmeVOOFtwM/WDe7yg1PL8sYhRk/XH0FzbR2HDQ0xC+EnQ/JHeoMYSavtU115tnUk0q9CDyq8si+LMAA==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.24.8" }, "engines": { "node": ">=6.9.0" @@ -1311,14 +1469,15 @@ } }, "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.24.1.tgz", - "integrity": "sha512-Hj791Ii4ci8HqnaKHAlLNs+zaLXb0EzSDhiAWp5VNlyvCNymYfacs64pxTxbH1znW/NcArSmwpmG9IKE/TUVVQ==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.24.7.tgz", + "integrity": "sha512-+izXIbke1T33mY4MSNnrqhPXDz01WYhEf3yF5NbnUtkiNnm+XBZJl3kNfoK6NKmYlz/D07+l2GWVK/QfDkNCuQ==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", - "@babel/plugin-transform-optional-chaining": "^7.24.1" + "@babel/helper-plugin-utils": "^7.24.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7", + "@babel/plugin-transform-optional-chaining": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -1327,6 +1486,23 @@ "@babel/core": "^7.13.0" } }, + "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.25.0.tgz", + "integrity": "sha512-tggFrk1AIShG/RUQbEwt2Tr/E+ObkfwrPjR6BjbRvsx24+PSjK8zrq0GWPNCjo8qpRx4DuJzlcvWJqlm+0h3kw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.8", + "@babel/traverse": "^7.25.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, "node_modules/@babel/plugin-proposal-class-properties": { "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz", @@ -1397,6 +1573,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.14.5" }, @@ -1412,6 +1589,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, @@ -1424,6 +1602,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.3" }, @@ -1432,12 +1611,13 @@ } }, "node_modules/@babel/plugin-syntax-import-assertions": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.24.1.tgz", - "integrity": "sha512-IuwnI5XnuF189t91XbxmXeCDz3qs6iDRO7GJ++wcfgeXNs/8FmIlKcpDSXNVyuLQxlwvskmI3Ct73wUODkJBlQ==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.24.7.tgz", + "integrity": "sha512-Ec3NRUMoi8gskrkBe3fNmEQfxDvY8bgfQpz6jlk/41kX9eUjvpyqWU7PBP/pLAvMaSQjbMNKJmvX57jP+M6bPg==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -1447,12 +1627,13 @@ } }, "node_modules/@babel/plugin-syntax-import-attributes": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.24.1.tgz", - "integrity": "sha512-zhQTMH0X2nVLnb04tz+s7AMuasX8U0FnpE+nHTOhSOINjWMnopoZTxtIKsd45n4GQ/HIZLyfIpoul8e2m0DnRA==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.24.7.tgz", + "integrity": "sha512-hbX+lKKeUMGihnK8nvKqmXBInriT3GVjzXKFriV3YC6APGxMbP8RZNFwy91+hocLXq90Mta+HshoB31802bb8A==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -1577,6 +1758,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.14.5" }, @@ -1634,12 +1816,13 @@ } }, "node_modules/@babel/plugin-transform-arrow-functions": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.24.1.tgz", - "integrity": "sha512-ngT/3NkRhsaep9ck9uj2Xhv9+xB1zShY3tM3g6om4xxCELwCDN4g4Aq5dRn48+0hasAql7s2hdBOysCfNpr4fw==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.24.7.tgz", + "integrity": "sha512-Dt9LQs6iEY++gXUwY03DNFat5C2NbO48jj+j/bSAz6b3HgPs39qcPiYt77fDObIcFwj3/C2ICX9YMwGflUoSHQ==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -1649,15 +1832,16 @@ } }, "node_modules/@babel/plugin-transform-async-generator-functions": { - "version": "7.24.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.24.3.tgz", - "integrity": "sha512-Qe26CMYVjpQxJ8zxM1340JFNjZaF+ISWpr1Kt/jGo+ZTUzKkfw/pphEWbRCb+lmSM6k/TOgfYLvmbHkUQ0asIg==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.25.0.tgz", + "integrity": "sha512-uaIi2FdqzjpAMvVqvB51S42oC2JEVgh0LDsGfZVDysWE8LrJtQC2jvKmOqEYThKyB7bDEb7BP1GYWDm7tABA0Q==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/helper-remap-async-to-generator": "^7.22.20", - "@babel/plugin-syntax-async-generators": "^7.8.4" + "@babel/helper-plugin-utils": "^7.24.8", + "@babel/helper-remap-async-to-generator": "^7.25.0", + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/traverse": "^7.25.0" }, "engines": { "node": ">=6.9.0" @@ -1667,14 +1851,15 @@ } }, "node_modules/@babel/plugin-transform-async-to-generator": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.24.1.tgz", - "integrity": "sha512-AawPptitRXp1y0n4ilKcGbRYWfbbzFWz2NqNu7dacYDtFtz0CMjG64b3LQsb3KIgnf4/obcUL78hfaOS7iCUfw==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.24.7.tgz", + "integrity": "sha512-SQY01PcJfmQ+4Ash7NE+rpbLFbmqA2GPIgqzxfFTL4t1FKRq4zTms/7htKpoCUI9OcFYgzqfmCdH53s6/jn5fA==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-module-imports": "^7.24.1", - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/helper-remap-async-to-generator": "^7.22.20" + "@babel/helper-module-imports": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.7", + "@babel/helper-remap-async-to-generator": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -1684,12 +1869,13 @@ } }, "node_modules/@babel/plugin-transform-block-scoped-functions": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.24.1.tgz", - "integrity": "sha512-TWWC18OShZutrv9C6mye1xwtam+uNi2bnTOCBUd5sZxyHOiWbU6ztSROofIMrK84uweEZC219POICK/sTYwfgg==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.24.7.tgz", + "integrity": "sha512-yO7RAz6EsVQDaBH18IDJcMB1HnrUn2FJ/Jslc/WtPPWcjhpUJXU/rjbwmluzp7v/ZzWcEhTMXELnnsz8djWDwQ==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -1699,12 +1885,13 @@ } }, "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.24.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.24.4.tgz", - "integrity": "sha512-nIFUZIpGKDf9O9ttyRXpHFpKC+X3Y5mtshZONuEUYBomAKoM4y029Jr+uB1bHGPhNmK8YXHevDtKDOLmtRrp6g==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.25.0.tgz", + "integrity": "sha512-yBQjYoOjXlFv9nlXb3f1casSHOZkWr29NX+zChVanLg5Nc157CrbEX9D7hxxtTpuFy7Q0YzmmWfJxzvps4kXrQ==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.24.8" }, "engines": { "node": ">=6.9.0" @@ -1714,13 +1901,14 @@ } }, "node_modules/@babel/plugin-transform-class-properties": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.24.1.tgz", - "integrity": "sha512-OMLCXi0NqvJfORTaPQBwqLXHhb93wkBKZ4aNwMl6WtehO7ar+cmp+89iPEQPqxAnxsOKTaMcs3POz3rKayJ72g==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.24.7.tgz", + "integrity": "sha512-vKbfawVYayKcSeSR5YYzzyXvsDFWU2mD8U5TFeXtbCPLFUqe7GyCgvO6XDHzje862ODrOwy6WCPmKeWHbCFJ4w==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.24.1", - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-create-class-features-plugin": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -1730,13 +1918,14 @@ } }, "node_modules/@babel/plugin-transform-class-static-block": { - "version": "7.24.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.24.4.tgz", - "integrity": "sha512-B8q7Pz870Hz/q9UgP8InNpY01CSLDSCyqX7zcRuv3FcPl87A2G17lASroHWaCtbdIcbYzOZ7kWmXFKbijMSmFg==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.24.7.tgz", + "integrity": "sha512-HMXK3WbBPpZQufbMG4B46A90PkuuhN9vBCb5T8+VAHqvAqvcLi+2cKoukcpmUYkszLhScU3l1iudhrks3DggRQ==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.24.4", - "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-create-class-features-plugin": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.7", "@babel/plugin-syntax-class-static-block": "^7.14.5" }, "engines": { @@ -1747,18 +1936,17 @@ } }, "node_modules/@babel/plugin-transform-classes": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.24.1.tgz", - "integrity": "sha512-ZTIe3W7UejJd3/3R4p7ScyyOoafetUShSf4kCqV0O7F/RiHxVj/wRaRnQlrGwflvcehNA8M42HkAiEDYZu2F1Q==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.25.0.tgz", + "integrity": "sha512-xyi6qjr/fYU304fiRwFbekzkqVJZ6A7hOjWZd+89FVcBqPV3S9Wuozz82xdpLspckeaafntbzglaW4pqpzvtSw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-compilation-targets": "^7.23.6", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/helper-replace-supers": "^7.24.1", - "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/helper-annotate-as-pure": "^7.24.7", + "@babel/helper-compilation-targets": "^7.24.8", + "@babel/helper-plugin-utils": "^7.24.8", + "@babel/helper-replace-supers": "^7.25.0", + "@babel/traverse": "^7.25.0", "globals": "^11.1.0" }, "engines": { @@ -1769,13 +1957,14 @@ } }, "node_modules/@babel/plugin-transform-computed-properties": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.24.1.tgz", - "integrity": "sha512-5pJGVIUfJpOS+pAqBQd+QMaTD2vCL/HcePooON6pDpHgRp4gNRmzyHTPIkXntwKsq3ayUFVfJaIKPw2pOkOcTw==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.24.7.tgz", + "integrity": "sha512-25cS7v+707Gu6Ds2oY6tCkUwsJ9YIDbggd9+cu9jzzDgiNq7hR/8dkzxWfKWnTic26vsI3EsCXNd4iEB6e8esQ==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/template": "^7.24.0" + "@babel/helper-plugin-utils": "^7.24.7", + "@babel/template": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -1785,12 +1974,13 @@ } }, "node_modules/@babel/plugin-transform-destructuring": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.24.1.tgz", - "integrity": "sha512-ow8jciWqNxR3RYbSNVuF4U2Jx130nwnBnhRw6N6h1bOejNkABmcI5X5oz29K4alWX7vf1C+o6gtKXikzRKkVdw==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.24.8.tgz", + "integrity": "sha512-36e87mfY8TnRxc7yc6M9g9gOB7rKgSahqkIKwLpz4Ppk2+zC2Cy1is0uwtuSG6AE4zlTOUa+7JGz9jCJGLqQFQ==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.24.8" }, "engines": { "node": ">=6.9.0" @@ -1800,13 +1990,14 @@ } }, "node_modules/@babel/plugin-transform-dotall-regex": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.24.1.tgz", - "integrity": "sha512-p7uUxgSoZwZ2lPNMzUkqCts3xlp8n+o05ikjy7gbtFJSt9gdU88jAmtfmOxHM14noQXBxfgzf2yRWECiNVhTCw==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.24.7.tgz", + "integrity": "sha512-ZOA3W+1RRTSWvyqcMJDLqbchh7U4NRGqwRfFSVbOLS/ePIP4vHB5e8T8eXcuqyN1QkgKyj5wuW0lcS85v4CrSw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-create-regexp-features-plugin": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -1816,12 +2007,13 @@ } }, "node_modules/@babel/plugin-transform-duplicate-keys": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.24.1.tgz", - "integrity": "sha512-msyzuUnvsjsaSaocV6L7ErfNsa5nDWL1XKNnDePLgmz+WdU4w/J8+AxBMrWfi9m4IxfL5sZQKUPQKDQeeAT6lA==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.24.7.tgz", + "integrity": "sha512-JdYfXyCRihAe46jUIliuL2/s0x0wObgwwiGxw/UbgJBr20gQBThrokO4nYKgWkD7uBaqM7+9x5TU7NkExZJyzw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -1830,13 +2022,31 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-transform-duplicate-named-capturing-groups-regex": { + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.25.0.tgz", + "integrity": "sha512-YLpb4LlYSc3sCUa35un84poXoraOiQucUTTu8X1j18JV+gNa8E0nyUf/CjZ171IRGr4jEguF+vzJU66QZhn29g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.25.0", + "@babel/helper-plugin-utils": "^7.24.8" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, "node_modules/@babel/plugin-transform-dynamic-import": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.24.1.tgz", - "integrity": "sha512-av2gdSTyXcJVdI+8aFZsCAtR29xJt0S5tas+Ef8NvBNmD1a+N/3ecMLeMBgfcK+xzsjdLDT6oHt+DFPyeqUbDA==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.24.7.tgz", + "integrity": "sha512-sc3X26PhZQDb3JhORmakcbvkeInvxz+A8oda99lj7J60QRuPZvNAk9wQlTBS1ZynelDrDmTU4pw1tyc5d5ZMUg==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-plugin-utils": "^7.24.7", "@babel/plugin-syntax-dynamic-import": "^7.8.3" }, "engines": { @@ -1847,13 +2057,14 @@ } }, "node_modules/@babel/plugin-transform-exponentiation-operator": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.24.1.tgz", - "integrity": "sha512-U1yX13dVBSwS23DEAqU+Z/PkwE9/m7QQy8Y9/+Tdb8UWYaGNDYwTLi19wqIAiROr8sXVum9A/rtiH5H0boUcTw==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.24.7.tgz", + "integrity": "sha512-Rqe/vSc9OYgDajNIK35u7ot+KeCoetqQYFXM4Epf7M7ez3lWlOjrDjrwMei6caCVhfdw+mIKD4cgdGNy5JQotQ==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.22.15", - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -1863,12 +2074,13 @@ } }, "node_modules/@babel/plugin-transform-export-namespace-from": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.24.1.tgz", - "integrity": "sha512-Ft38m/KFOyzKw2UaJFkWG9QnHPG/Q/2SkOrRk4pNBPg5IPZ+dOxcmkK5IyuBcxiNPyyYowPGUReyBvrvZs7IlQ==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.24.7.tgz", + "integrity": "sha512-v0K9uNYsPL3oXZ/7F9NNIbAj2jv1whUEtyA6aujhekLs56R++JDQuzRcP2/z4WX5Vg/c5lE9uWZA0/iUoFhLTA==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-plugin-utils": "^7.24.7", "@babel/plugin-syntax-export-namespace-from": "^7.8.3" }, "engines": { @@ -1879,13 +2091,14 @@ } }, "node_modules/@babel/plugin-transform-for-of": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.24.1.tgz", - "integrity": "sha512-OxBdcnF04bpdQdR3i4giHZNZQn7cm8RQKcSwA17wAAqEELo1ZOwp5FFgeptWUQXFyT9kwHo10aqqauYkRZPCAg==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.24.7.tgz", + "integrity": "sha512-wo9ogrDG1ITTTBsy46oGiN1dS9A7MROBTcYsfS8DtsImMkHk9JXJ3EWQM6X2SUw4x80uGPlwj0o00Uoc6nEE3g==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -1895,14 +2108,15 @@ } }, "node_modules/@babel/plugin-transform-function-name": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.24.1.tgz", - "integrity": "sha512-BXmDZpPlh7jwicKArQASrj8n22/w6iymRnvHYYd2zO30DbE277JO20/7yXJT3QxDPtiQiOxQBbZH4TpivNXIxA==", + "version": "7.25.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.25.1.tgz", + "integrity": "sha512-TVVJVdW9RKMNgJJlLtHsKDTydjZAbwIsn6ySBPQaEAUU5+gVvlJt/9nRmqVbsV/IBanRjzWoaAQKLoamWVOUuA==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-compilation-targets": "^7.23.6", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-compilation-targets": "^7.24.8", + "@babel/helper-plugin-utils": "^7.24.8", + "@babel/traverse": "^7.25.1" }, "engines": { "node": ">=6.9.0" @@ -1912,12 +2126,13 @@ } }, "node_modules/@babel/plugin-transform-json-strings": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.24.1.tgz", - "integrity": "sha512-U7RMFmRvoasscrIFy5xA4gIp8iWnWubnKkKuUGJjsuOH7GfbMkB+XZzeslx2kLdEGdOJDamEmCqOks6e8nv8DQ==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.24.7.tgz", + "integrity": "sha512-2yFnBGDvRuxAaE/f0vfBKvtnvvqU8tGpMHqMNpTN2oWMKIR3NqFkjaAgGwawhqK/pIN2T3XdjGPdaG0vDhOBGw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-plugin-utils": "^7.24.7", "@babel/plugin-syntax-json-strings": "^7.8.3" }, "engines": { @@ -1928,12 +2143,13 @@ } }, "node_modules/@babel/plugin-transform-literals": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.24.1.tgz", - "integrity": "sha512-zn9pwz8U7nCqOYIiBaOxoQOtYmMODXTJnkxG4AtX8fPmnCRYWBOHD0qcpwS9e2VDSp1zNJYpdnFMIKb8jmwu6g==", + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.25.2.tgz", + "integrity": "sha512-HQI+HcTbm9ur3Z2DkO+jgESMAMcYLuN/A7NRw9juzxAezN9AvqvUTnpKP/9kkYANz6u7dFlAyOu44ejuGySlfw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.24.8" }, "engines": { "node": ">=6.9.0" @@ -1943,12 +2159,13 @@ } }, "node_modules/@babel/plugin-transform-logical-assignment-operators": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.24.1.tgz", - "integrity": "sha512-OhN6J4Bpz+hIBqItTeWJujDOfNP+unqv/NJgyhlpSqgBTPm37KkMmZV6SYcOj+pnDbdcl1qRGV/ZiIjX9Iy34w==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.24.7.tgz", + "integrity": "sha512-4D2tpwlQ1odXmTEIFWy9ELJcZHqrStlzK/dAOWYyxX3zT0iXQB6banjgeOJQXzEc4S0E0a5A+hahxPaEFYftsw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-plugin-utils": "^7.24.7", "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" }, "engines": { @@ -1959,12 +2176,13 @@ } }, "node_modules/@babel/plugin-transform-member-expression-literals": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.24.1.tgz", - "integrity": "sha512-4ojai0KysTWXzHseJKa1XPNXKRbuUrhkOPY4rEGeR+7ChlJVKxFa3H3Bz+7tWaGKgJAXUWKOGmltN+u9B3+CVg==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.24.7.tgz", + "integrity": "sha512-T/hRC1uqrzXMKLQ6UCwMT85S3EvqaBXDGf0FaMf4446Qx9vKwlghvee0+uuZcDUCZU5RuNi4781UQ7R308zzBw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -1974,13 +2192,14 @@ } }, "node_modules/@babel/plugin-transform-modules-amd": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.24.1.tgz", - "integrity": "sha512-lAxNHi4HVtjnHd5Rxg3D5t99Xm6H7b04hUS7EHIXcUl2EV4yl1gWdqZrNzXnSrHveL9qMdbODlLF55mvgjAfaQ==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.24.7.tgz", + "integrity": "sha512-9+pB1qxV3vs/8Hdmz/CulFB8w2tuu6EB94JZFsjdqxQokwGa9Unap7Bo2gGBGIvPmDIVvQrom7r5m/TCDMURhg==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-module-transforms": "^7.23.3", - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-module-transforms": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -1990,14 +2209,15 @@ } }, "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.22.15.tgz", - "integrity": "sha512-jWL4eh90w0HQOTKP2MoXXUpVxilxsB2Vl4ji69rSjS3EcZ/v4sBmn+A3NpepuJzBhOaEBbR7udonlHHn5DWidg==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.24.8.tgz", + "integrity": "sha512-WHsk9H8XxRs3JXKWFiqtQebdh9b/pTk4EgueygFzYlTKAg0Ud985mSevdNjdXdFBATSKVJGQXP1tv6aGbssLKA==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-module-transforms": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-simple-access": "^7.22.5" + "@babel/helper-module-transforms": "^7.24.8", + "@babel/helper-plugin-utils": "^7.24.8", + "@babel/helper-simple-access": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -2007,15 +2227,16 @@ } }, "node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.24.1.tgz", - "integrity": "sha512-mqQ3Zh9vFO1Tpmlt8QPnbwGHzNz3lpNEMxQb1kAemn/erstyqw1r9KeOlOfo3y6xAnFEcOv2tSyrXfmMk+/YZA==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.25.0.tgz", + "integrity": "sha512-YPJfjQPDXxyQWg/0+jHKj1llnY5f/R6a0p/vP4lPymxLu7Lvl4k2WMitqi08yxwQcCVUUdG9LCUj4TNEgAp3Jw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-module-transforms": "^7.23.3", - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/helper-validator-identifier": "^7.22.20" + "@babel/helper-module-transforms": "^7.25.0", + "@babel/helper-plugin-utils": "^7.24.8", + "@babel/helper-validator-identifier": "^7.24.7", + "@babel/traverse": "^7.25.0" }, "engines": { "node": ">=6.9.0" @@ -2025,13 +2246,14 @@ } }, "node_modules/@babel/plugin-transform-modules-umd": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.24.1.tgz", - "integrity": "sha512-tuA3lpPj+5ITfcCluy6nWonSL7RvaG0AOTeAuvXqEKS34lnLzXpDb0dcP6K8jD0zWZFNDVly90AGFJPnm4fOYg==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.24.7.tgz", + "integrity": "sha512-3aytQvqJ/h9z4g8AsKPLvD4Zqi2qT+L3j7XoFFu1XBlZWEl2/1kWnhmAbxpLgPrHSY0M6UA02jyTiwUVtiKR6A==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-module-transforms": "^7.23.3", - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-module-transforms": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -2041,13 +2263,14 @@ } }, "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.22.5.tgz", - "integrity": "sha512-YgLLKmS3aUBhHaxp5hi1WJTgOUb/NCuDHzGT9z9WTt3YG+CPRhJs6nprbStx6DnWM4dh6gt7SU3sZodbZ08adQ==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.24.7.tgz", + "integrity": "sha512-/jr7h/EWeJtk1U/uz2jlsCioHkZk1JJZVcc8oQsJ1dUlaJD83f4/6Zeh2aHt9BIFokHIsSeDfhUmju0+1GPd6g==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-create-regexp-features-plugin": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -2057,12 +2280,13 @@ } }, "node_modules/@babel/plugin-transform-new-target": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.24.1.tgz", - "integrity": "sha512-/rurytBM34hYy0HKZQyA0nHbQgQNFm4Q/BOc9Hflxi2X3twRof7NaE5W46j4kQitm7SvACVRXsa6N/tSZxvPug==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.24.7.tgz", + "integrity": "sha512-RNKwfRIXg4Ls/8mMTza5oPF5RkOW8Wy/WgMAp1/F1yZ8mMbtwXW+HDoJiOsagWrAhI5f57Vncrmr9XeT4CVapA==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -2072,12 +2296,13 @@ } }, "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.24.1.tgz", - "integrity": "sha512-iQ+caew8wRrhCikO5DrUYx0mrmdhkaELgFa+7baMcVuhxIkN7oxt06CZ51D65ugIb1UWRQ8oQe+HXAVM6qHFjw==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.24.7.tgz", + "integrity": "sha512-Ts7xQVk1OEocqzm8rHMXHlxvsfZ0cEF2yomUqpKENHWMF4zKk175Y4q8H5knJes6PgYad50uuRmt3UJuhBw8pQ==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-plugin-utils": "^7.24.7", "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" }, "engines": { @@ -2088,12 +2313,13 @@ } }, "node_modules/@babel/plugin-transform-numeric-separator": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.24.1.tgz", - "integrity": "sha512-7GAsGlK4cNL2OExJH1DzmDeKnRv/LXq0eLUSvudrehVA5Rgg4bIrqEUW29FbKMBRT0ztSqisv7kjP+XIC4ZMNw==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.24.7.tgz", + "integrity": "sha512-e6q1TiVUzvH9KRvicuxdBTUj4AdKSRwzIyFFnfnezpCfP2/7Qmbb8qbU2j7GODbl4JMkblitCQjKYUaX/qkkwA==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-plugin-utils": "^7.24.7", "@babel/plugin-syntax-numeric-separator": "^7.10.4" }, "engines": { @@ -2104,15 +2330,16 @@ } }, "node_modules/@babel/plugin-transform-object-rest-spread": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.24.1.tgz", - "integrity": "sha512-XjD5f0YqOtebto4HGISLNfiNMTTs6tbkFf2TOqJlYKYmbo+mN9Dnpl4SRoofiziuOWMIyq3sZEUqLo3hLITFEA==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.24.7.tgz", + "integrity": "sha512-4QrHAr0aXQCEFni2q4DqKLD31n2DL+RxcwnNjDFkSG0eNQ/xCavnRkfCUjsyqGC2OviNJvZOF/mQqZBw7i2C5Q==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-compilation-targets": "^7.23.6", - "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-compilation-targets": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.7", "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.24.1" + "@babel/plugin-transform-parameters": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -2122,13 +2349,14 @@ } }, "node_modules/@babel/plugin-transform-object-super": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.24.1.tgz", - "integrity": "sha512-oKJqR3TeI5hSLRxudMjFQ9re9fBVUU0GICqM3J1mi8MqlhVr6hC/ZN4ttAyMuQR6EZZIY6h/exe5swqGNNIkWQ==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.24.7.tgz", + "integrity": "sha512-A/vVLwN6lBrMFmMDmPPz0jnE6ZGx7Jq7d6sT/Ev4H65RER6pZ+kczlf1DthF5N0qaPHBsI7UXiE8Zy66nmAovg==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/helper-replace-supers": "^7.24.1" + "@babel/helper-plugin-utils": "^7.24.7", + "@babel/helper-replace-supers": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -2138,12 +2366,13 @@ } }, "node_modules/@babel/plugin-transform-optional-catch-binding": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.24.1.tgz", - "integrity": "sha512-oBTH7oURV4Y+3EUrf6cWn1OHio3qG/PVwO5J03iSJmBg6m2EhKjkAu/xuaXaYwWW9miYtvbWv4LNf0AmR43LUA==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.24.7.tgz", + "integrity": "sha512-uLEndKqP5BfBbC/5jTwPxLh9kqPWWgzN/f8w6UwAIirAEqiIVJWWY312X72Eub09g5KF9+Zn7+hT7sDxmhRuKA==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-plugin-utils": "^7.24.7", "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" }, "engines": { @@ -2154,13 +2383,14 @@ } }, "node_modules/@babel/plugin-transform-optional-chaining": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.24.1.tgz", - "integrity": "sha512-n03wmDt+987qXwAgcBlnUUivrZBPZ8z1plL0YvgQalLm+ZE5BMhGm94jhxXtA1wzv1Cu2aaOv1BM9vbVttrzSg==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.24.8.tgz", + "integrity": "sha512-5cTOLSMs9eypEy8JUVvIKOu6NgvbJMnpG62VpIHrTmROdQ+L5mDAaI40g25k5vXti55JWNX5jCkq3HZxXBQANw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", + "@babel/helper-plugin-utils": "^7.24.8", + "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7", "@babel/plugin-syntax-optional-chaining": "^7.8.3" }, "engines": { @@ -2171,12 +2401,13 @@ } }, "node_modules/@babel/plugin-transform-parameters": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.24.1.tgz", - "integrity": "sha512-8Jl6V24g+Uw5OGPeWNKrKqXPDw2YDjLc53ojwfMcKwlEoETKU9rU0mHUtcg9JntWI/QYzGAXNWEcVHZ+fR+XXg==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.24.7.tgz", + "integrity": "sha512-yGWW5Rr+sQOhK0Ot8hjDJuxU3XLRQGflvT4lhlSY0DFvdb3TwKaY26CJzHtYllU0vT9j58hc37ndFPsqT1SrzA==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -2186,13 +2417,14 @@ } }, "node_modules/@babel/plugin-transform-private-methods": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.24.1.tgz", - "integrity": "sha512-tGvisebwBO5em4PaYNqt4fkw56K2VALsAbAakY0FjTYqJp7gfdrgr7YX76Or8/cpik0W6+tj3rZ0uHU9Oil4tw==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.24.7.tgz", + "integrity": "sha512-COTCOkG2hn4JKGEKBADkA8WNb35TGkkRbI5iT845dB+NyqgO8Hn+ajPbSnIQznneJTa3d30scb6iz/DhH8GsJQ==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.24.1", - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-create-class-features-plugin": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -2202,14 +2434,15 @@ } }, "node_modules/@babel/plugin-transform-private-property-in-object": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.24.1.tgz", - "integrity": "sha512-pTHxDVa0BpUbvAgX3Gat+7cSciXqUcY9j2VZKTbSB6+VQGpNgNO9ailxTGHSXlqOnX1Hcx1Enme2+yv7VqP9bg==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.24.7.tgz", + "integrity": "sha512-9z76mxwnwFxMyxZWEgdgECQglF2Q7cFLm0kMf8pGwt+GSJsY0cONKj/UuO4bOH0w/uAel3ekS4ra5CEAyJRmDA==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-create-class-features-plugin": "^7.24.1", - "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-annotate-as-pure": "^7.24.7", + "@babel/helper-create-class-features-plugin": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.7", "@babel/plugin-syntax-private-property-in-object": "^7.14.5" }, "engines": { @@ -2220,12 +2453,13 @@ } }, "node_modules/@babel/plugin-transform-property-literals": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.24.1.tgz", - "integrity": "sha512-LetvD7CrHmEx0G442gOomRr66d7q8HzzGGr4PMHGr+5YIm6++Yke+jxj246rpvsbyhJwCLxcTn6zW1P1BSenqA==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.24.7.tgz", + "integrity": "sha512-EMi4MLQSHfd2nrCqQEWxFdha2gBCqU4ZcCng4WBGZ5CJL4bBRW0ptdqqDdeirGZcpALazVVNJqRmsO8/+oNCBA==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -2235,12 +2469,13 @@ } }, "node_modules/@babel/plugin-transform-regenerator": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.24.1.tgz", - "integrity": "sha512-sJwZBCzIBE4t+5Q4IGLaaun5ExVMRY0lYwos/jNecjMrVCygCdph3IKv0tkP5Fc87e/1+bebAmEAGBfnRD+cnw==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.24.7.tgz", + "integrity": "sha512-lq3fvXPdimDrlg6LWBoqj+r/DEWgONuwjuOuQCSYgRroXDH/IdM1C0IZf59fL5cHLpjEH/O6opIRBbqv7ELnuA==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-plugin-utils": "^7.24.7", "regenerator-transform": "^0.15.2" }, "engines": { @@ -2251,12 +2486,13 @@ } }, "node_modules/@babel/plugin-transform-reserved-words": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.24.1.tgz", - "integrity": "sha512-JAclqStUfIwKN15HrsQADFgeZt+wexNQ0uLhuqvqAUFoqPMjEcFCYZBhq0LUdz6dZK/mD+rErhW71fbx8RYElg==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.24.7.tgz", + "integrity": "sha512-0DUq0pHcPKbjFZCfTss/pGkYMfy3vFWydkUBd9r0GHpIyfs2eCDENvqadMycRS9wZCXR41wucAfJHJmwA0UmoQ==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -2266,16 +2502,17 @@ } }, "node_modules/@babel/plugin-transform-runtime": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.22.15.tgz", - "integrity": "sha512-tEVLhk8NRZSmwQ0DJtxxhTrCht1HVo8VaMzYT4w6lwyKBuHsgoioAUA7/6eT2fRfc5/23fuGdlwIxXhRVgWr4g==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.24.7.tgz", + "integrity": "sha512-YqXjrk4C+a1kZjewqt+Mmu2UuV1s07y8kqcUf4qYLnoqemhR4gRQikhdAhSVJioMjVTu6Mo6pAbaypEA3jY6fw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-module-imports": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5", - "babel-plugin-polyfill-corejs2": "^0.4.5", - "babel-plugin-polyfill-corejs3": "^0.8.3", - "babel-plugin-polyfill-regenerator": "^0.5.2", + "@babel/helper-module-imports": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.7", + "babel-plugin-polyfill-corejs2": "^0.4.10", + "babel-plugin-polyfill-corejs3": "^0.10.1", + "babel-plugin-polyfill-regenerator": "^0.6.1", "semver": "^6.3.1" }, "engines": { @@ -2286,12 +2523,13 @@ } }, "node_modules/@babel/plugin-transform-shorthand-properties": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.24.1.tgz", - "integrity": "sha512-LyjVB1nsJ6gTTUKRjRWx9C1s9hE7dLfP/knKdrfeH9UPtAGjYGgxIbFfx7xyLIEWs7Xe1Gnf8EWiUqfjLhInZA==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.24.7.tgz", + "integrity": "sha512-KsDsevZMDsigzbA09+vacnLpmPH4aWjcZjXdyFKGzpplxhbeB4wYtury3vglQkg6KM/xEPKt73eCjPPf1PgXBA==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -2301,13 +2539,14 @@ } }, "node_modules/@babel/plugin-transform-spread": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.24.1.tgz", - "integrity": "sha512-KjmcIM+fxgY+KxPVbjelJC6hrH1CgtPmTvdXAfn3/a9CnWGSTY7nH4zm5+cjmWJybdcPSsD0++QssDsjcpe47g==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.24.7.tgz", + "integrity": "sha512-x96oO0I09dgMDxJaANcRyD4ellXFLLiWhuwDxKZX5g2rWP1bTPkBSwCYv96VDXVT1bD9aPj8tppr5ITIh8hBng==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -2317,12 +2556,13 @@ } }, "node_modules/@babel/plugin-transform-sticky-regex": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.24.1.tgz", - "integrity": "sha512-9v0f1bRXgPVcPrngOQvLXeGNNVLc8UjMVfebo9ka0WF3/7+aVUHmaJVT3sa0XCzEFioPfPHZiOcYG9qOsH63cw==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.24.7.tgz", + "integrity": "sha512-kHPSIJc9v24zEml5geKg9Mjx5ULpfncj0wRpYtxbvKyTtHCYDkVE3aHQ03FrpEo4gEe2vrJJS1Y9CJTaThA52g==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -2332,12 +2572,13 @@ } }, "node_modules/@babel/plugin-transform-template-literals": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.24.1.tgz", - "integrity": "sha512-WRkhROsNzriarqECASCNu/nojeXCDTE/F2HmRgOzi7NGvyfYGq1NEjKBK3ckLfRgGc6/lPAqP0vDOSw3YtG34g==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.24.7.tgz", + "integrity": "sha512-AfDTQmClklHCOLxtGoP7HkeMw56k1/bTQjwsfhL6pppo/M4TOBSq+jjBUBLmV/4oeFg4GWMavIl44ZeCtmmZTw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -2347,12 +2588,13 @@ } }, "node_modules/@babel/plugin-transform-typeof-symbol": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.24.1.tgz", - "integrity": "sha512-CBfU4l/A+KruSUoW+vTQthwcAdwuqbpRNB8HQKlZABwHRhsdHZ9fezp4Sn18PeAlYxTNiLMlx4xUBV3AWfg1BA==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.24.8.tgz", + "integrity": "sha512-adNTUpDCVnmAE58VEqKlAA6ZBlNkMnWD0ZcW76lyNFN3MJniyGFZfNwERVk8Ap56MCnXztmDr19T4mPTztcuaw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.24.8" }, "engines": { "node": ">=6.9.0" @@ -2362,12 +2604,13 @@ } }, "node_modules/@babel/plugin-transform-unicode-escapes": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.24.1.tgz", - "integrity": "sha512-RlkVIcWT4TLI96zM660S877E7beKlQw7Ig+wqkKBiWfj0zH5Q4h50q6er4wzZKRNSYpfo6ILJ+hrJAGSX2qcNw==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.24.7.tgz", + "integrity": "sha512-U3ap1gm5+4edc2Q/P+9VrBNhGkfnf+8ZqppY71Bo/pzZmXhhLdqgaUl6cuB07O1+AQJtCLfaOmswiNbSQ9ivhw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -2377,13 +2620,14 @@ } }, "node_modules/@babel/plugin-transform-unicode-property-regex": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.24.1.tgz", - "integrity": "sha512-Ss4VvlfYV5huWApFsF8/Sq0oXnGO+jB+rijFEFugTd3cwSObUSnUi88djgR5528Csl0uKlrI331kRqe56Ov2Ng==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.24.7.tgz", + "integrity": "sha512-uH2O4OV5M9FZYQrwc7NdVmMxQJOCCzFeYudlZSzUAHRFeOujQefa92E74TQDVskNHCzOXoigEuoyzHDhaEaK5w==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-create-regexp-features-plugin": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -2393,13 +2637,14 @@ } }, "node_modules/@babel/plugin-transform-unicode-regex": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.24.1.tgz", - "integrity": "sha512-2A/94wgZgxfTsiLaQ2E36XAOdcZmGAaEEgVmxQWwZXWkGhvoHbaqXcKnU8zny4ycpu3vNqg0L/PcCiYtHtA13g==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.24.7.tgz", + "integrity": "sha512-hlQ96MBZSAXUq7ltkjtu3FJCCSMx/j629ns3hA3pXnBXjanNP0LHi+JpPeA81zaWgVK1VGH95Xuy7u0RyQ8kMg==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-create-regexp-features-plugin": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -2409,13 +2654,14 @@ } }, "node_modules/@babel/plugin-transform-unicode-sets-regex": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.24.1.tgz", - "integrity": "sha512-fqj4WuzzS+ukpgerpAoOnMfQXwUHFxXUZUE84oL2Kao2N8uSlvcpnAidKASgsNgzZHBsHWvcm8s9FPWUhAb8fA==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.24.7.tgz", + "integrity": "sha512-2G8aAvF4wy1w/AGZkemprdGMRg5o6zPNhbHVImRz3lss55TYCBd6xStN19rt8XJHq20sqV0JbyWjOWwQRwV/wg==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-create-regexp-features-plugin": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -2425,25 +2671,29 @@ } }, "node_modules/@babel/preset-env": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.22.20.tgz", - "integrity": "sha512-11MY04gGC4kSzlPHRfvVkNAZhUxOvm7DCJ37hPDnUENwe06npjIRAfInEMTGSb4LZK5ZgDFkv5hw0lGebHeTyg==", - "dev": true, - "dependencies": { - "@babel/compat-data": "^7.22.20", - "@babel/helper-compilation-targets": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-validator-option": "^7.22.15", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.22.15", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.22.15", + "version": "7.25.3", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.25.3.tgz", + "integrity": "sha512-QsYW7UeAaXvLPX9tdVliMJE7MD7M6MLYVTovRTIwhoYQVFHR1rM4wO8wqAezYi3/BpSD+NzVCZ69R6smWiIi8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.25.2", + "@babel/helper-compilation-targets": "^7.25.2", + "@babel/helper-plugin-utils": "^7.24.8", + "@babel/helper-validator-option": "^7.24.8", + "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.25.3", + "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.25.0", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.25.0", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.24.7", + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.25.0", "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", "@babel/plugin-syntax-async-generators": "^7.8.4", "@babel/plugin-syntax-class-properties": "^7.12.13", "@babel/plugin-syntax-class-static-block": "^7.14.5", "@babel/plugin-syntax-dynamic-import": "^7.8.3", "@babel/plugin-syntax-export-namespace-from": "^7.8.3", - "@babel/plugin-syntax-import-assertions": "^7.22.5", - "@babel/plugin-syntax-import-attributes": "^7.22.5", + "@babel/plugin-syntax-import-assertions": "^7.24.7", + "@babel/plugin-syntax-import-attributes": "^7.24.7", "@babel/plugin-syntax-import-meta": "^7.10.4", "@babel/plugin-syntax-json-strings": "^7.8.3", "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", @@ -2455,60 +2705,60 @@ "@babel/plugin-syntax-private-property-in-object": "^7.14.5", "@babel/plugin-syntax-top-level-await": "^7.14.5", "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", - "@babel/plugin-transform-arrow-functions": "^7.22.5", - "@babel/plugin-transform-async-generator-functions": "^7.22.15", - "@babel/plugin-transform-async-to-generator": "^7.22.5", - "@babel/plugin-transform-block-scoped-functions": "^7.22.5", - "@babel/plugin-transform-block-scoping": "^7.22.15", - "@babel/plugin-transform-class-properties": "^7.22.5", - "@babel/plugin-transform-class-static-block": "^7.22.11", - "@babel/plugin-transform-classes": "^7.22.15", - "@babel/plugin-transform-computed-properties": "^7.22.5", - "@babel/plugin-transform-destructuring": "^7.22.15", - "@babel/plugin-transform-dotall-regex": "^7.22.5", - "@babel/plugin-transform-duplicate-keys": "^7.22.5", - "@babel/plugin-transform-dynamic-import": "^7.22.11", - "@babel/plugin-transform-exponentiation-operator": "^7.22.5", - "@babel/plugin-transform-export-namespace-from": "^7.22.11", - "@babel/plugin-transform-for-of": "^7.22.15", - "@babel/plugin-transform-function-name": "^7.22.5", - "@babel/plugin-transform-json-strings": "^7.22.11", - "@babel/plugin-transform-literals": "^7.22.5", - "@babel/plugin-transform-logical-assignment-operators": "^7.22.11", - "@babel/plugin-transform-member-expression-literals": "^7.22.5", - "@babel/plugin-transform-modules-amd": "^7.22.5", - "@babel/plugin-transform-modules-commonjs": "^7.22.15", - "@babel/plugin-transform-modules-systemjs": "^7.22.11", - "@babel/plugin-transform-modules-umd": "^7.22.5", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.22.5", - "@babel/plugin-transform-new-target": "^7.22.5", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.22.11", - "@babel/plugin-transform-numeric-separator": "^7.22.11", - "@babel/plugin-transform-object-rest-spread": "^7.22.15", - "@babel/plugin-transform-object-super": "^7.22.5", - "@babel/plugin-transform-optional-catch-binding": "^7.22.11", - "@babel/plugin-transform-optional-chaining": "^7.22.15", - "@babel/plugin-transform-parameters": "^7.22.15", - "@babel/plugin-transform-private-methods": "^7.22.5", - "@babel/plugin-transform-private-property-in-object": "^7.22.11", - "@babel/plugin-transform-property-literals": "^7.22.5", - "@babel/plugin-transform-regenerator": "^7.22.10", - "@babel/plugin-transform-reserved-words": "^7.22.5", - "@babel/plugin-transform-shorthand-properties": "^7.22.5", - "@babel/plugin-transform-spread": "^7.22.5", - "@babel/plugin-transform-sticky-regex": "^7.22.5", - "@babel/plugin-transform-template-literals": "^7.22.5", - "@babel/plugin-transform-typeof-symbol": "^7.22.5", - "@babel/plugin-transform-unicode-escapes": "^7.22.10", - "@babel/plugin-transform-unicode-property-regex": "^7.22.5", - "@babel/plugin-transform-unicode-regex": "^7.22.5", - "@babel/plugin-transform-unicode-sets-regex": "^7.22.5", + "@babel/plugin-transform-arrow-functions": "^7.24.7", + "@babel/plugin-transform-async-generator-functions": "^7.25.0", + "@babel/plugin-transform-async-to-generator": "^7.24.7", + "@babel/plugin-transform-block-scoped-functions": "^7.24.7", + "@babel/plugin-transform-block-scoping": "^7.25.0", + "@babel/plugin-transform-class-properties": "^7.24.7", + "@babel/plugin-transform-class-static-block": "^7.24.7", + "@babel/plugin-transform-classes": "^7.25.0", + "@babel/plugin-transform-computed-properties": "^7.24.7", + "@babel/plugin-transform-destructuring": "^7.24.8", + "@babel/plugin-transform-dotall-regex": "^7.24.7", + "@babel/plugin-transform-duplicate-keys": "^7.24.7", + "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.25.0", + "@babel/plugin-transform-dynamic-import": "^7.24.7", + "@babel/plugin-transform-exponentiation-operator": "^7.24.7", + "@babel/plugin-transform-export-namespace-from": "^7.24.7", + "@babel/plugin-transform-for-of": "^7.24.7", + "@babel/plugin-transform-function-name": "^7.25.1", + "@babel/plugin-transform-json-strings": "^7.24.7", + "@babel/plugin-transform-literals": "^7.25.2", + "@babel/plugin-transform-logical-assignment-operators": "^7.24.7", + "@babel/plugin-transform-member-expression-literals": "^7.24.7", + "@babel/plugin-transform-modules-amd": "^7.24.7", + "@babel/plugin-transform-modules-commonjs": "^7.24.8", + "@babel/plugin-transform-modules-systemjs": "^7.25.0", + "@babel/plugin-transform-modules-umd": "^7.24.7", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.24.7", + "@babel/plugin-transform-new-target": "^7.24.7", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.24.7", + "@babel/plugin-transform-numeric-separator": "^7.24.7", + "@babel/plugin-transform-object-rest-spread": "^7.24.7", + "@babel/plugin-transform-object-super": "^7.24.7", + "@babel/plugin-transform-optional-catch-binding": "^7.24.7", + "@babel/plugin-transform-optional-chaining": "^7.24.8", + "@babel/plugin-transform-parameters": "^7.24.7", + "@babel/plugin-transform-private-methods": "^7.24.7", + "@babel/plugin-transform-private-property-in-object": "^7.24.7", + "@babel/plugin-transform-property-literals": "^7.24.7", + "@babel/plugin-transform-regenerator": "^7.24.7", + "@babel/plugin-transform-reserved-words": "^7.24.7", + "@babel/plugin-transform-shorthand-properties": "^7.24.7", + "@babel/plugin-transform-spread": "^7.24.7", + "@babel/plugin-transform-sticky-regex": "^7.24.7", + "@babel/plugin-transform-template-literals": "^7.24.7", + "@babel/plugin-transform-typeof-symbol": "^7.24.8", + "@babel/plugin-transform-unicode-escapes": "^7.24.7", + "@babel/plugin-transform-unicode-property-regex": "^7.24.7", + "@babel/plugin-transform-unicode-regex": "^7.24.7", + "@babel/plugin-transform-unicode-sets-regex": "^7.24.7", "@babel/preset-modules": "0.1.6-no-external-plugins", - "@babel/types": "^7.22.19", - "babel-plugin-polyfill-corejs2": "^0.4.5", - "babel-plugin-polyfill-corejs3": "^0.8.3", - "babel-plugin-polyfill-regenerator": "^0.5.2", - "core-js-compat": "^3.31.0", + "babel-plugin-polyfill-corejs2": "^0.4.10", + "babel-plugin-polyfill-corejs3": "^0.10.4", + "babel-plugin-polyfill-regenerator": "^0.6.1", + "core-js-compat": "^3.37.1", "semver": "^6.3.1" }, "engines": { @@ -2539,10 +2789,11 @@ "dev": true }, "node_modules/@babel/runtime": { - "version": "7.24.4", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.4.tgz", - "integrity": "sha512-dkxf7+hn8mFBwKjs9bvBlArzLVxVbS8usaPUDd5p2a9JCL9tB8OaOVN1isD4+Xyk4ns89/xeOmbQvgdK7IIVdA==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.0.tgz", + "integrity": "sha512-7dRy4DwXwtzBrPbZflqxnvfxLF8kdZXPkhymtDeFoFqE6ldzjQFgYTtYIFARcLEYDrqfBfYcZt1WqFxRoyC9Rw==", "dev": true, + "license": "MIT", "dependencies": { "regenerator-runtime": "^0.14.0" }, @@ -2551,33 +2802,32 @@ } }, "node_modules/@babel/template": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.0.tgz", - "integrity": "sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.0.tgz", + "integrity": "sha512-aOOgh1/5XzKvg1jvVz7AVrx2piJ2XBi227DHmbY6y+bM9H2FlN+IfecYu4Xl0cNiiVejlsCri89LUsbj8vJD9Q==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.23.5", - "@babel/parser": "^7.24.0", - "@babel/types": "^7.24.0" + "@babel/code-frame": "^7.24.7", + "@babel/parser": "^7.25.0", + "@babel/types": "^7.25.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.1.tgz", - "integrity": "sha512-xuU6o9m68KeqZbQuDt2TcKSxUw/mrsvavlEqQ1leZ/B+C9tk6E4sRWy97WaXgvq5E+nU3cXMxv3WKOCanVMCmQ==", + "version": "7.25.3", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.3.tgz", + "integrity": "sha512-HefgyP1x754oGCsKmV5reSmtV7IXj/kpaE1XYY+D9G5PvKKoFfSbiS4M77MdjuwlZKDIKFCffq9rPU+H/s3ZdQ==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.24.1", - "@babel/generator": "^7.24.1", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.24.1", - "@babel/types": "^7.24.0", + "@babel/code-frame": "^7.24.7", + "@babel/generator": "^7.25.0", + "@babel/parser": "^7.25.3", + "@babel/template": "^7.25.0", + "@babel/types": "^7.25.2", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -2586,13 +2836,14 @@ } }, "node_modules/@babel/types": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.0.tgz", - "integrity": "sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==", + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.2.tgz", + "integrity": "sha512-YTnYtra7W9e6/oAZEHj0bJehPRUlLH9/fbpT5LfB0NhQXyALCRkRs3zH9v07IYhkgpqX6Z78FnuccZr/l4Fs4Q==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.23.4", - "@babel/helper-validator-identifier": "^7.22.20", + "@babel/helper-string-parser": "^7.24.8", + "@babel/helper-validator-identifier": "^7.24.7", "to-fast-properties": "^2.0.0" }, "engines": { @@ -2609,7 +2860,8 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/@flatten-js/interval-tree/-/interval-tree-1.1.3.tgz", "integrity": "sha512-xhFWUBoHJFF77cJO1D6REjdgJEMRf2Y2Z+eKEPav8evGKcLSnj1ud5pLXQSbGuxF3VSvT1rWhMfVpXEKJLTL+A==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", @@ -3414,6 +3666,7 @@ "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", "dev": true, + "license": "MIT", "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" @@ -3427,6 +3680,7 @@ "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", "dev": true, + "license": "MIT", "engines": { "node": ">= 8" } @@ -3436,6 +3690,7 @@ "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", "dev": true, + "license": "MIT", "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" @@ -3534,537 +3789,612 @@ } }, "node_modules/@smithy/abort-controller": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-2.2.0.tgz", - "integrity": "sha512-wRlta7GuLWpTqtFfGo+nZyOO1vEvewdNR1R4rTxpC8XU6vG/NDyrFBhwLZsqg1NUoR1noVaXJPC/7ZK47QCySw==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-3.1.1.tgz", + "integrity": "sha512-MBJBiidoe+0cTFhyxT8g+9g7CeVccLM0IOKKUMCNQ1CNMJ/eIfoo0RTfVrXOONEI1UCN1W+zkiHSbzUNE9dZtQ==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^2.12.0", + "@smithy/types": "^3.3.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=14.0.0" + "node": ">=16.0.0" } }, "node_modules/@smithy/chunked-blob-reader": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader/-/chunked-blob-reader-2.2.0.tgz", - "integrity": "sha512-3GJNvRwXBGdkDZZOGiziVYzDpn4j6zfyULHMDKAGIUo72yHALpE9CbhfQp/XcLNVoc1byfMpn6uW5H2BqPjgaQ==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader/-/chunked-blob-reader-3.0.0.tgz", + "integrity": "sha512-sbnURCwjF0gSToGlsBiAmd1lRCmSn72nu9axfJu5lIx6RUEgHu6GwTMbqCdhQSi0Pumcm5vFxsi9XWXb2mTaoA==", "dev": true, + "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" } }, "node_modules/@smithy/chunked-blob-reader-native": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader-native/-/chunked-blob-reader-native-2.2.0.tgz", - "integrity": "sha512-VNB5+1oCgX3Fzs072yuRsUoC2N4Zg/LJ11DTxX3+Qu+Paa6AmbIF0E9sc2wthz9Psrk/zcOlTCyuposlIhPjZQ==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader-native/-/chunked-blob-reader-native-3.0.0.tgz", + "integrity": "sha512-VDkpCYW+peSuM4zJip5WDfqvg2Mo/e8yxOv3VF1m11y7B8KKMKVFtmZWDe36Fvk8rGuWrPZHHXZ7rR7uM5yWyg==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/util-base64": "^2.3.0", + "@smithy/util-base64": "^3.0.0", "tslib": "^2.6.2" } }, "node_modules/@smithy/config-resolver": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-2.2.0.tgz", - "integrity": "sha512-fsiMgd8toyUba6n1WRmr+qACzXltpdDkPTAaDqc8QqPBUzO+/JKwL6bUBseHVi8tu9l+3JOK+tSf7cay+4B3LA==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-3.0.5.tgz", + "integrity": "sha512-SkW5LxfkSI1bUC74OtfBbdz+grQXYiPYolyu8VfpLIjEoN/sHVBlLeGXMQ1vX4ejkgfv6sxVbQJ32yF2cl1veA==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/node-config-provider": "^2.3.0", - "@smithy/types": "^2.12.0", - "@smithy/util-config-provider": "^2.3.0", - "@smithy/util-middleware": "^2.2.0", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/types": "^3.3.0", + "@smithy/util-config-provider": "^3.0.0", + "@smithy/util-middleware": "^3.0.3", "tslib": "^2.6.2" }, "engines": { - "node": ">=14.0.0" + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/core": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-2.3.2.tgz", + "integrity": "sha512-in5wwt6chDBcUv1Lw1+QzZxN9fBffi+qOixfb65yK4sDuKG7zAUO9HAFqmVzsZM3N+3tTyvZjtnDXePpvp007Q==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/middleware-endpoint": "^3.1.0", + "@smithy/middleware-retry": "^3.0.14", + "@smithy/middleware-serde": "^3.0.3", + "@smithy/protocol-http": "^4.1.0", + "@smithy/smithy-client": "^3.1.12", + "@smithy/types": "^3.3.0", + "@smithy/util-middleware": "^3.0.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, "node_modules/@smithy/credential-provider-imds": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-2.3.0.tgz", - "integrity": "sha512-BWB9mIukO1wjEOo1Ojgl6LrG4avcaC7T/ZP6ptmAaW4xluhSIPZhY+/PI5YKzlk+jsm+4sQZB45Bt1OfMeQa3w==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-3.2.0.tgz", + "integrity": "sha512-0SCIzgd8LYZ9EJxUjLXBmEKSZR/P/w6l7Rz/pab9culE/RWuqelAKGJvn5qUOl8BgX8Yj5HWM50A5hiB/RzsgA==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/node-config-provider": "^2.3.0", - "@smithy/property-provider": "^2.2.0", - "@smithy/types": "^2.12.0", - "@smithy/url-parser": "^2.2.0", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/property-provider": "^3.1.3", + "@smithy/types": "^3.3.0", + "@smithy/url-parser": "^3.0.3", "tslib": "^2.6.2" }, "engines": { - "node": ">=14.0.0" + "node": ">=16.0.0" } }, "node_modules/@smithy/eventstream-codec": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-2.2.0.tgz", - "integrity": "sha512-8janZoJw85nJmQZc4L8TuePp2pk1nxLgkxIR0TUjKJ5Dkj5oelB9WtiSSGXCQvNsJl0VSTvK/2ueMXxvpa9GVw==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-3.1.2.tgz", + "integrity": "sha512-0mBcu49JWt4MXhrhRAlxASNy0IjDRFU+aWNDRal9OtUJvJNiwDuyKMUONSOjLjSCeGwZaE0wOErdqULer8r7yw==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@aws-crypto/crc32": "3.0.0", - "@smithy/types": "^2.12.0", - "@smithy/util-hex-encoding": "^2.2.0", + "@aws-crypto/crc32": "5.2.0", + "@smithy/types": "^3.3.0", + "@smithy/util-hex-encoding": "^3.0.0", "tslib": "^2.6.2" } }, "node_modules/@smithy/eventstream-serde-browser": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-2.2.0.tgz", - "integrity": "sha512-UaPf8jKbcP71BGiO0CdeLmlg+RhWnlN8ipsMSdwvqBFigl5nil3rHOI/5GE3tfiuX8LvY5Z9N0meuU7Rab7jWw==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-3.0.5.tgz", + "integrity": "sha512-dEyiUYL/ekDfk+2Ra4GxV+xNnFoCmk1nuIXg+fMChFTrM2uI/1r9AdiTYzPqgb72yIv/NtAj6C3dG//1wwgakQ==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/eventstream-serde-universal": "^2.2.0", - "@smithy/types": "^2.12.0", + "@smithy/eventstream-serde-universal": "^3.0.4", + "@smithy/types": "^3.3.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=14.0.0" + "node": ">=16.0.0" } }, "node_modules/@smithy/eventstream-serde-config-resolver": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-2.2.0.tgz", - "integrity": "sha512-RHhbTw/JW3+r8QQH7PrganjNCiuiEZmpi6fYUAetFfPLfZ6EkiA08uN3EFfcyKubXQxOwTeJRZSQmDDCdUshaA==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-3.0.3.tgz", + "integrity": "sha512-NVTYjOuYpGfrN/VbRQgn31x73KDLfCXCsFdad8DiIc3IcdxL+dYA9zEQPyOP7Fy2QL8CPy2WE4WCUD+ZsLNfaQ==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^2.12.0", + "@smithy/types": "^3.3.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=14.0.0" + "node": ">=16.0.0" } }, "node_modules/@smithy/eventstream-serde-node": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-node/-/eventstream-serde-node-2.2.0.tgz", - "integrity": "sha512-zpQMtJVqCUMn+pCSFcl9K/RPNtQE0NuMh8sKpCdEHafhwRsjP50Oq/4kMmvxSRy6d8Jslqd8BLvDngrUtmN9iA==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-node/-/eventstream-serde-node-3.0.4.tgz", + "integrity": "sha512-mjlG0OzGAYuUpdUpflfb9zyLrBGgmQmrobNT8b42ZTsGv/J03+t24uhhtVEKG/b2jFtPIHF74Bq+VUtbzEKOKg==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/eventstream-serde-universal": "^2.2.0", - "@smithy/types": "^2.12.0", + "@smithy/eventstream-serde-universal": "^3.0.4", + "@smithy/types": "^3.3.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=14.0.0" + "node": ">=16.0.0" } }, "node_modules/@smithy/eventstream-serde-universal": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-2.2.0.tgz", - "integrity": "sha512-pvoe/vvJY0mOpuF84BEtyZoYfbehiFj8KKWk1ds2AT0mTLYFVs+7sBJZmioOFdBXKd48lfrx1vumdPdmGlCLxA==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-3.0.4.tgz", + "integrity": "sha512-Od9dv8zh3PgOD7Vj4T3HSuox16n0VG8jJIM2gvKASL6aCtcS8CfHZDWe1Ik3ZXW6xBouU+45Q5wgoliWDZiJ0A==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/eventstream-codec": "^2.2.0", - "@smithy/types": "^2.12.0", + "@smithy/eventstream-codec": "^3.1.2", + "@smithy/types": "^3.3.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=14.0.0" + "node": ">=16.0.0" } }, "node_modules/@smithy/fetch-http-handler": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-2.5.0.tgz", - "integrity": "sha512-BOWEBeppWhLn/no/JxUL/ghTfANTjT7kg3Ww2rPqTUY9R4yHPXxJ9JhMe3Z03LN3aPwiwlpDIUcVw1xDyHqEhw==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-3.2.4.tgz", + "integrity": "sha512-kBprh5Gs5h7ug4nBWZi1FZthdqSM+T7zMmsZxx0IBvWUn7dK3diz2SHn7Bs4dQGFDk8plDv375gzenDoNwrXjg==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/protocol-http": "^3.3.0", - "@smithy/querystring-builder": "^2.2.0", - "@smithy/types": "^2.12.0", - "@smithy/util-base64": "^2.3.0", + "@smithy/protocol-http": "^4.1.0", + "@smithy/querystring-builder": "^3.0.3", + "@smithy/types": "^3.3.0", + "@smithy/util-base64": "^3.0.0", "tslib": "^2.6.2" } }, "node_modules/@smithy/hash-blob-browser": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/hash-blob-browser/-/hash-blob-browser-2.2.0.tgz", - "integrity": "sha512-SGPoVH8mdXBqrkVCJ1Hd1X7vh1zDXojNN1yZyZTZsCno99hVue9+IYzWDjq/EQDDXxmITB0gBmuyPh8oAZSTcg==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@smithy/hash-blob-browser/-/hash-blob-browser-3.1.2.tgz", + "integrity": "sha512-hAbfqN2UbISltakCC2TP0kx4LqXBttEv2MqSPE98gVuDFMf05lU+TpC41QtqGP3Ff5A3GwZMPfKnEy0VmEUpmg==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/chunked-blob-reader": "^2.2.0", - "@smithy/chunked-blob-reader-native": "^2.2.0", - "@smithy/types": "^2.12.0", + "@smithy/chunked-blob-reader": "^3.0.0", + "@smithy/chunked-blob-reader-native": "^3.0.0", + "@smithy/types": "^3.3.0", "tslib": "^2.6.2" } }, "node_modules/@smithy/hash-node": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-2.2.0.tgz", - "integrity": "sha512-zLWaC/5aWpMrHKpoDF6nqpNtBhlAYKF/7+9yMN7GpdR8CzohnWfGtMznPybnwSS8saaXBMxIGwJqR4HmRp6b3g==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-3.0.3.tgz", + "integrity": "sha512-2ctBXpPMG+B3BtWSGNnKELJ7SH9e4TNefJS0cd2eSkOOROeBnnVBnAy9LtJ8tY4vUEoe55N4CNPxzbWvR39iBw==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^2.12.0", - "@smithy/util-buffer-from": "^2.2.0", - "@smithy/util-utf8": "^2.3.0", + "@smithy/types": "^3.3.0", + "@smithy/util-buffer-from": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=14.0.0" + "node": ">=16.0.0" } }, "node_modules/@smithy/hash-stream-node": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/hash-stream-node/-/hash-stream-node-2.2.0.tgz", - "integrity": "sha512-aT+HCATOSRMGpPI7bi7NSsTNVZE/La9IaxLXWoVAYMxHT5hGO3ZOGEMZQg8A6nNL+pdFGtZQtND1eoY084HgHQ==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@smithy/hash-stream-node/-/hash-stream-node-3.1.2.tgz", + "integrity": "sha512-PBgDMeEdDzi6JxKwbfBtwQG9eT9cVwsf0dZzLXoJF4sHKHs5HEo/3lJWpn6jibfJwT34I1EBXpBnZE8AxAft6g==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^2.12.0", - "@smithy/util-utf8": "^2.3.0", + "@smithy/types": "^3.3.0", + "@smithy/util-utf8": "^3.0.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=14.0.0" + "node": ">=16.0.0" } }, "node_modules/@smithy/invalid-dependency": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-2.2.0.tgz", - "integrity": "sha512-nEDASdbKFKPXN2O6lOlTgrEEOO9NHIeO+HVvZnkqc8h5U9g3BIhWsvzFo+UcUbliMHvKNPD/zVxDrkP1Sbgp8Q==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-3.0.3.tgz", + "integrity": "sha512-ID1eL/zpDULmHJbflb864k72/SNOZCADRc9i7Exq3RUNJw6raWUSlFEQ+3PX3EYs++bTxZB2dE9mEHTQLv61tw==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^2.12.0", + "@smithy/types": "^3.3.0", "tslib": "^2.6.2" } }, "node_modules/@smithy/is-array-buffer": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", - "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-3.0.0.tgz", + "integrity": "sha512-+Fsu6Q6C4RSJiy81Y8eApjEB5gVtM+oFKTffg+jSuwtvomJJrhUJBu2zS8wjXSgH/g1MKEWrzyChTBe6clb5FQ==", "dev": true, + "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" }, "engines": { - "node": ">=14.0.0" + "node": ">=16.0.0" } }, "node_modules/@smithy/md5-js": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/md5-js/-/md5-js-2.2.0.tgz", - "integrity": "sha512-M26XTtt9IIusVMOWEAhIvFIr9jYj4ISPPGJROqw6vXngO3IYJCnVVSMFn4Tx1rUTG5BiKJNg9u2nxmBiZC5IlQ==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@smithy/md5-js/-/md5-js-3.0.3.tgz", + "integrity": "sha512-O/SAkGVwpWmelpj/8yDtsaVe6sINHLB1q8YE/+ZQbDxIw3SRLbTZuRaI10K12sVoENdnHqzPp5i3/H+BcZ3m3Q==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^2.12.0", - "@smithy/util-utf8": "^2.3.0", + "@smithy/types": "^3.3.0", + "@smithy/util-utf8": "^3.0.0", "tslib": "^2.6.2" } }, "node_modules/@smithy/middleware-content-length": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-2.2.0.tgz", - "integrity": "sha512-5bl2LG1Ah/7E5cMSC+q+h3IpVHMeOkG0yLRyQT1p2aMJkSrZG7RlXHPuAgb7EyaFeidKEnnd/fNaLLaKlHGzDQ==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-3.0.5.tgz", + "integrity": "sha512-ILEzC2eyxx6ncej3zZSwMpB5RJ0zuqH7eMptxC4KN3f+v9bqT8ohssKbhNR78k/2tWW+KS5Spw+tbPF4Ejyqvw==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/protocol-http": "^3.3.0", - "@smithy/types": "^2.12.0", + "@smithy/protocol-http": "^4.1.0", + "@smithy/types": "^3.3.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=14.0.0" + "node": ">=16.0.0" } }, "node_modules/@smithy/middleware-endpoint": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-2.5.1.tgz", - "integrity": "sha512-1/8kFp6Fl4OsSIVTWHnNjLnTL8IqpIb/D3sTSczrKFnrE9VMNWxnrRKNvpUHOJ6zpGD5f62TPm7+17ilTJpiCQ==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-3.1.0.tgz", + "integrity": "sha512-5y5aiKCEwg9TDPB4yFE7H6tYvGFf1OJHNczeY10/EFF8Ir8jZbNntQJxMWNfeQjC1mxPsaQ6mR9cvQbf+0YeMw==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/middleware-serde": "^2.3.0", - "@smithy/node-config-provider": "^2.3.0", - "@smithy/shared-ini-file-loader": "^2.4.0", - "@smithy/types": "^2.12.0", - "@smithy/url-parser": "^2.2.0", - "@smithy/util-middleware": "^2.2.0", + "@smithy/middleware-serde": "^3.0.3", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/shared-ini-file-loader": "^3.1.4", + "@smithy/types": "^3.3.0", + "@smithy/url-parser": "^3.0.3", + "@smithy/util-middleware": "^3.0.3", "tslib": "^2.6.2" }, "engines": { - "node": ">=14.0.0" + "node": ">=16.0.0" } }, "node_modules/@smithy/middleware-retry": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-2.3.1.tgz", - "integrity": "sha512-P2bGufFpFdYcWvqpyqqmalRtwFUNUA8vHjJR5iGqbfR6mp65qKOLcUd6lTr4S9Gn/enynSrSf3p3FVgVAf6bXA==", - "dev": true, - "dependencies": { - "@smithy/node-config-provider": "^2.3.0", - "@smithy/protocol-http": "^3.3.0", - "@smithy/service-error-classification": "^2.1.5", - "@smithy/smithy-client": "^2.5.1", - "@smithy/types": "^2.12.0", - "@smithy/util-middleware": "^2.2.0", - "@smithy/util-retry": "^2.2.0", + "version": "3.0.14", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-3.0.14.tgz", + "integrity": "sha512-7ZaWZJOjUxa5hgmuMspyt8v/zVsh0GXYuF7OvCmdcbVa/xbnKQoYC+uYKunAqRGTkxjOyuOCw9rmFUFOqqC0eQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^3.1.4", + "@smithy/protocol-http": "^4.1.0", + "@smithy/service-error-classification": "^3.0.3", + "@smithy/smithy-client": "^3.1.12", + "@smithy/types": "^3.3.0", + "@smithy/util-middleware": "^3.0.3", + "@smithy/util-retry": "^3.0.3", "tslib": "^2.6.2", "uuid": "^9.0.1" }, "engines": { - "node": ">=14.0.0" + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/middleware-retry/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "dev": true, + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" } }, "node_modules/@smithy/middleware-serde": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-2.3.0.tgz", - "integrity": "sha512-sIADe7ojwqTyvEQBe1nc/GXB9wdHhi9UwyX0lTyttmUWDJLP655ZYE1WngnNyXREme8I27KCaUhyhZWRXL0q7Q==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-3.0.3.tgz", + "integrity": "sha512-puUbyJQBcg9eSErFXjKNiGILJGtiqmuuNKEYNYfUD57fUl4i9+mfmThtQhvFXU0hCVG0iEJhvQUipUf+/SsFdA==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^2.12.0", + "@smithy/types": "^3.3.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=14.0.0" + "node": ">=16.0.0" } }, "node_modules/@smithy/middleware-stack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-2.2.0.tgz", - "integrity": "sha512-Qntc3jrtwwrsAC+X8wms8zhrTr0sFXnyEGhZd9sLtsJ/6gGQKFzNB+wWbOcpJd7BR8ThNCoKt76BuQahfMvpeA==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-3.0.3.tgz", + "integrity": "sha512-r4klY9nFudB0r9UdSMaGSyjyQK5adUyPnQN/ZM6M75phTxOdnc/AhpvGD1fQUvgmqjQEBGCwpnPbDm8pH5PapA==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^2.12.0", + "@smithy/types": "^3.3.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=14.0.0" + "node": ">=16.0.0" } }, "node_modules/@smithy/node-config-provider": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-2.3.0.tgz", - "integrity": "sha512-0elK5/03a1JPWMDPaS726Iw6LpQg80gFut1tNpPfxFuChEEklo2yL823V94SpTZTxmKlXFtFgsP55uh3dErnIg==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-3.1.4.tgz", + "integrity": "sha512-YvnElQy8HR4vDcAjoy7Xkx9YT8xZP4cBXcbJSgm/kxmiQu08DwUwj8rkGnyoJTpfl/3xYHH+d8zE+eHqoDCSdQ==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/property-provider": "^2.2.0", - "@smithy/shared-ini-file-loader": "^2.4.0", - "@smithy/types": "^2.12.0", + "@smithy/property-provider": "^3.1.3", + "@smithy/shared-ini-file-loader": "^3.1.4", + "@smithy/types": "^3.3.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=14.0.0" + "node": ">=16.0.0" } }, "node_modules/@smithy/node-http-handler": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-2.5.0.tgz", - "integrity": "sha512-mVGyPBzkkGQsPoxQUbxlEfRjrj6FPyA3u3u2VXGr9hT8wilsoQdZdvKpMBFMB8Crfhv5dNkKHIW0Yyuc7eABqA==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-3.1.4.tgz", + "integrity": "sha512-+UmxgixgOr/yLsUxcEKGH0fMNVteJFGkmRltYFHnBMlogyFdpzn2CwqWmxOrfJELhV34v0WSlaqG1UtE1uXlJg==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/abort-controller": "^2.2.0", - "@smithy/protocol-http": "^3.3.0", - "@smithy/querystring-builder": "^2.2.0", - "@smithy/types": "^2.12.0", + "@smithy/abort-controller": "^3.1.1", + "@smithy/protocol-http": "^4.1.0", + "@smithy/querystring-builder": "^3.0.3", + "@smithy/types": "^3.3.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=14.0.0" + "node": ">=16.0.0" } }, "node_modules/@smithy/property-provider": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-2.2.0.tgz", - "integrity": "sha512-+xiil2lFhtTRzXkx8F053AV46QnIw6e7MV8od5Mi68E1ICOjCeCHw2XfLnDEUHnT9WGUIkwcqavXjfwuJbGlpg==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-3.1.3.tgz", + "integrity": "sha512-zahyOVR9Q4PEoguJ/NrFP4O7SMAfYO1HLhB18M+q+Z4KFd4V2obiMnlVoUFzFLSPeVt1POyNWneHHrZaTMoc/g==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^2.12.0", + "@smithy/types": "^3.3.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=14.0.0" + "node": ">=16.0.0" } }, "node_modules/@smithy/protocol-http": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-3.3.0.tgz", - "integrity": "sha512-Xy5XK1AFWW2nlY/biWZXu6/krgbaf2dg0q492D8M5qthsnU2H+UgFeZLbM76FnH7s6RO/xhQRkj+T6KBO3JzgQ==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-4.1.0.tgz", + "integrity": "sha512-dPVoHYQ2wcHooGXg3LQisa1hH0e4y0pAddPMeeUPipI1tEOqL6A4N0/G7abeq+K8wrwSgjk4C0wnD1XZpJm5aA==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^2.12.0", + "@smithy/types": "^3.3.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=14.0.0" + "node": ">=16.0.0" } }, "node_modules/@smithy/querystring-builder": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-2.2.0.tgz", - "integrity": "sha512-L1kSeviUWL+emq3CUVSgdogoM/D9QMFaqxL/dd0X7PCNWmPXqt+ExtrBjqT0V7HLN03Vs9SuiLrG3zy3JGnE5A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-3.0.3.tgz", + "integrity": "sha512-vyWckeUeesFKzCDaRwWLUA1Xym9McaA6XpFfAK5qI9DKJ4M33ooQGqvM4J+LalH4u/Dq9nFiC8U6Qn1qi0+9zw==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^2.12.0", - "@smithy/util-uri-escape": "^2.2.0", + "@smithy/types": "^3.3.0", + "@smithy/util-uri-escape": "^3.0.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=14.0.0" + "node": ">=16.0.0" } }, "node_modules/@smithy/querystring-parser": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-2.2.0.tgz", - "integrity": "sha512-BvHCDrKfbG5Yhbpj4vsbuPV2GgcpHiAkLeIlcA1LtfpMz3jrqizP1+OguSNSj1MwBHEiN+jwNisXLGdajGDQJA==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-3.0.3.tgz", + "integrity": "sha512-zahM1lQv2YjmznnfQsWbYojFe55l0SLG/988brlLv1i8z3dubloLF+75ATRsqPBboUXsW6I9CPGE5rQgLfY0vQ==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^2.12.0", + "@smithy/types": "^3.3.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=14.0.0" + "node": ">=16.0.0" } }, "node_modules/@smithy/service-error-classification": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-2.1.5.tgz", - "integrity": "sha512-uBDTIBBEdAQryvHdc5W8sS5YX7RQzF683XrHePVdFmAgKiMofU15FLSM0/HU03hKTnazdNRFa0YHS7+ArwoUSQ==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-3.0.3.tgz", + "integrity": "sha512-Jn39sSl8cim/VlkLsUhRFq/dKDnRUFlfRkvhOJaUbLBXUsLRLNf9WaxDv/z9BjuQ3A6k/qE8af1lsqcwm7+DaQ==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^2.12.0" + "@smithy/types": "^3.3.0" }, "engines": { - "node": ">=14.0.0" + "node": ">=16.0.0" } }, "node_modules/@smithy/shared-ini-file-loader": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-2.4.0.tgz", - "integrity": "sha512-WyujUJL8e1B6Z4PBfAqC/aGY1+C7T0w20Gih3yrvJSk97gpiVfB+y7c46T4Nunk+ZngLq0rOIdeVeIklk0R3OA==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-3.1.4.tgz", + "integrity": "sha512-qMxS4hBGB8FY2GQqshcRUy1K6k8aBWP5vwm8qKkCT3A9K2dawUwOIJfqh9Yste/Bl0J2lzosVyrXDj68kLcHXQ==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^2.12.0", + "@smithy/types": "^3.3.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=14.0.0" + "node": ">=16.0.0" } }, "node_modules/@smithy/signature-v4": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-2.3.0.tgz", - "integrity": "sha512-ui/NlpILU+6HAQBfJX8BBsDXuKSNrjTSuOYArRblcrErwKFutjrCNb/OExfVRyj9+26F9J+ZmfWT+fKWuDrH3Q==", - "dev": true, - "dependencies": { - "@smithy/is-array-buffer": "^2.2.0", - "@smithy/types": "^2.12.0", - "@smithy/util-hex-encoding": "^2.2.0", - "@smithy/util-middleware": "^2.2.0", - "@smithy/util-uri-escape": "^2.2.0", - "@smithy/util-utf8": "^2.3.0", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-4.1.0.tgz", + "integrity": "sha512-aRryp2XNZeRcOtuJoxjydO6QTaVhxx/vjaR+gx7ZjaFgrgPRyZ3HCTbfwqYj6ZWEBHkCSUfcaymKPURaByukag==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^3.0.0", + "@smithy/protocol-http": "^4.1.0", + "@smithy/types": "^3.3.0", + "@smithy/util-hex-encoding": "^3.0.0", + "@smithy/util-middleware": "^3.0.3", + "@smithy/util-uri-escape": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=14.0.0" + "node": ">=16.0.0" } }, "node_modules/@smithy/smithy-client": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-2.5.1.tgz", - "integrity": "sha512-jrbSQrYCho0yDaaf92qWgd+7nAeap5LtHTI51KXqmpIFCceKU3K9+vIVTUH72bOJngBMqa4kyu1VJhRcSrk/CQ==", + "version": "3.1.12", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-3.1.12.tgz", + "integrity": "sha512-wtm8JtsycthkHy1YA4zjIh2thJgIQ9vGkoR639DBx5lLlLNU0v4GARpQZkr2WjXue74nZ7MiTSWfVrLkyD8RkA==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/middleware-endpoint": "^2.5.1", - "@smithy/middleware-stack": "^2.2.0", - "@smithy/protocol-http": "^3.3.0", - "@smithy/types": "^2.12.0", - "@smithy/util-stream": "^2.2.0", + "@smithy/middleware-endpoint": "^3.1.0", + "@smithy/middleware-stack": "^3.0.3", + "@smithy/protocol-http": "^4.1.0", + "@smithy/types": "^3.3.0", + "@smithy/util-stream": "^3.1.3", "tslib": "^2.6.2" }, "engines": { - "node": ">=14.0.0" + "node": ">=16.0.0" } }, "node_modules/@smithy/types": { - "version": "2.12.0", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-2.12.0.tgz", - "integrity": "sha512-QwYgloJ0sVNBeBuBs65cIkTbfzV/Q6ZNPCJ99EICFEdJYG50nGIY/uYXp+TbsdJReIuPr0a0kXmCvren3MbRRw==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-3.3.0.tgz", + "integrity": "sha512-IxvBBCTFDHbVoK7zIxqA1ZOdc4QfM5HM7rGleCuHi7L1wnKv5Pn69xXJQ9hgxH60ZVygH9/JG0jRgtUncE3QUA==", "dev": true, + "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" }, "engines": { - "node": ">=14.0.0" + "node": ">=16.0.0" } }, "node_modules/@smithy/url-parser": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-2.2.0.tgz", - "integrity": "sha512-hoA4zm61q1mNTpksiSWp2nEl1dt3j726HdRhiNgVJQMj7mLp7dprtF57mOB6JvEk/x9d2bsuL5hlqZbBuHQylQ==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-3.0.3.tgz", + "integrity": "sha512-pw3VtZtX2rg+s6HMs6/+u9+hu6oY6U7IohGhVNnjbgKy86wcIsSZwgHrFR+t67Uyxvp4Xz3p3kGXXIpTNisq8A==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/querystring-parser": "^2.2.0", - "@smithy/types": "^2.12.0", + "@smithy/querystring-parser": "^3.0.3", + "@smithy/types": "^3.3.0", "tslib": "^2.6.2" } }, "node_modules/@smithy/util-base64": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-2.3.0.tgz", - "integrity": "sha512-s3+eVwNeJuXUwuMbusncZNViuhv2LjVJ1nMwTqSA0XAC7gjKhqqxRdJPhR8+YrkoZ9IiIbFk/yK6ACe/xlF+hw==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-3.0.0.tgz", + "integrity": "sha512-Kxvoh5Qtt0CDsfajiZOCpJxgtPHXOKwmM+Zy4waD43UoEMA+qPxxa98aE/7ZhdnBFZFXMOiBR5xbcaMhLtznQQ==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/util-buffer-from": "^2.2.0", - "@smithy/util-utf8": "^2.3.0", + "@smithy/util-buffer-from": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=14.0.0" + "node": ">=16.0.0" } }, "node_modules/@smithy/util-body-length-browser": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-2.2.0.tgz", - "integrity": "sha512-dtpw9uQP7W+n3vOtx0CfBD5EWd7EPdIdsQnWTDoFf77e3VUf05uA7R7TGipIo8e4WL2kuPdnsr3hMQn9ziYj5w==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-3.0.0.tgz", + "integrity": "sha512-cbjJs2A1mLYmqmyVl80uoLTJhAcfzMOyPgjwAYusWKMdLeNtzmMz9YxNl3/jRLoxSS3wkqkf0jwNdtXWtyEBaQ==", "dev": true, + "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" } }, "node_modules/@smithy/util-body-length-node": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-2.3.0.tgz", - "integrity": "sha512-ITWT1Wqjubf2CJthb0BuT9+bpzBfXeMokH/AAa5EJQgbv9aPMVfnM76iFIZVFf50hYXGbtiV71BHAthNWd6+dw==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-3.0.0.tgz", + "integrity": "sha512-Tj7pZ4bUloNUP6PzwhN7K386tmSmEET9QtQg0TgdNOnxhZvCssHji+oZTUIuzxECRfG8rdm2PMw2WCFs6eIYkA==", "dev": true, + "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" }, "engines": { - "node": ">=14.0.0" + "node": ">=16.0.0" } }, "node_modules/@smithy/util-buffer-from": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", - "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-3.0.0.tgz", + "integrity": "sha512-aEOHCgq5RWFbP+UDPvPot26EJHjOC+bRgse5A8V3FSShqd5E5UN4qc7zkwsvJPPAVsf73QwYcHN1/gt/rtLwQA==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/is-array-buffer": "^2.2.0", + "@smithy/is-array-buffer": "^3.0.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=14.0.0" + "node": ">=16.0.0" } }, "node_modules/@smithy/util-config-provider": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-2.3.0.tgz", - "integrity": "sha512-HZkzrRcuFN1k70RLqlNK4FnPXKOpkik1+4JaBoHNJn+RnJGYqaa3c5/+XtLOXhlKzlRgNvyaLieHTW2VwGN0VQ==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-3.0.0.tgz", + "integrity": "sha512-pbjk4s0fwq3Di/ANL+rCvJMKM5bzAQdE5S/6RL5NXgMExFAi6UgQMPOm5yPaIWPpr+EOXKXRonJ3FoxKf4mCJQ==", "dev": true, + "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" }, "engines": { - "node": ">=14.0.0" + "node": ">=16.0.0" } }, "node_modules/@smithy/util-defaults-mode-browser": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-2.2.1.tgz", - "integrity": "sha512-RtKW+8j8skk17SYowucwRUjeh4mCtnm5odCL0Lm2NtHQBsYKrNW0od9Rhopu9wF1gHMfHeWF7i90NwBz/U22Kw==", + "version": "3.0.14", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-3.0.14.tgz", + "integrity": "sha512-0iwTgKKmAIf+vFLV8fji21Jb2px11ktKVxbX6LIDPAUJyWQqGqBVfwba7xwa1f2FZUoolYQgLvxQEpJycXuQ5w==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/property-provider": "^2.2.0", - "@smithy/smithy-client": "^2.5.1", - "@smithy/types": "^2.12.0", + "@smithy/property-provider": "^3.1.3", + "@smithy/smithy-client": "^3.1.12", + "@smithy/types": "^3.3.0", "bowser": "^2.11.0", "tslib": "^2.6.2" }, @@ -4073,17 +4403,18 @@ } }, "node_modules/@smithy/util-defaults-mode-node": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-2.3.1.tgz", - "integrity": "sha512-vkMXHQ0BcLFysBMWgSBLSk3+leMpFSyyFj8zQtv5ZyUBx8/owVh1/pPEkzmW/DR/Gy/5c8vjLDD9gZjXNKbrpA==", - "dev": true, - "dependencies": { - "@smithy/config-resolver": "^2.2.0", - "@smithy/credential-provider-imds": "^2.3.0", - "@smithy/node-config-provider": "^2.3.0", - "@smithy/property-provider": "^2.2.0", - "@smithy/smithy-client": "^2.5.1", - "@smithy/types": "^2.12.0", + "version": "3.0.14", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-3.0.14.tgz", + "integrity": "sha512-e9uQarJKfXApkTMMruIdxHprhcXivH1flYCe8JRDTzkkLx8dA3V5J8GZlST9yfDiRWkJpZJlUXGN9Rc9Ade3OQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/config-resolver": "^3.0.5", + "@smithy/credential-provider-imds": "^3.2.0", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/property-provider": "^3.1.3", + "@smithy/smithy-client": "^3.1.12", + "@smithy/types": "^3.3.0", "tslib": "^2.6.2" }, "engines": { @@ -4091,114 +4422,122 @@ } }, "node_modules/@smithy/util-endpoints": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-1.2.0.tgz", - "integrity": "sha512-BuDHv8zRjsE5zXd3PxFXFknzBG3owCpjq8G3FcsXW3CykYXuEqM3nTSsmLzw5q+T12ZYuDlVUZKBdpNbhVtlrQ==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-2.0.5.tgz", + "integrity": "sha512-ReQP0BWihIE68OAblC/WQmDD40Gx+QY1Ez8mTdFMXpmjfxSyz2fVQu3A4zXRfQU9sZXtewk3GmhfOHswvX+eNg==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/node-config-provider": "^2.3.0", - "@smithy/types": "^2.12.0", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/types": "^3.3.0", "tslib": "^2.6.2" }, "engines": { - "node": ">= 14.0.0" + "node": ">=16.0.0" } }, "node_modules/@smithy/util-hex-encoding": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-2.2.0.tgz", - "integrity": "sha512-7iKXR+/4TpLK194pVjKiasIyqMtTYJsgKgM242Y9uzt5dhHnUDvMNb+3xIhRJ9QhvqGii/5cRUt4fJn3dtXNHQ==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-3.0.0.tgz", + "integrity": "sha512-eFndh1WEK5YMUYvy3lPlVmYY/fZcQE1D8oSf41Id2vCeIkKJXPcYDCZD+4+xViI6b1XSd7tE+s5AmXzz5ilabQ==", "dev": true, + "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" }, "engines": { - "node": ">=14.0.0" + "node": ">=16.0.0" } }, "node_modules/@smithy/util-middleware": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-2.2.0.tgz", - "integrity": "sha512-L1qpleXf9QD6LwLCJ5jddGkgWyuSvWBkJwWAZ6kFkdifdso+sk3L3O1HdmPvCdnCK3IS4qWyPxev01QMnfHSBw==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-3.0.3.tgz", + "integrity": "sha512-l+StyYYK/eO3DlVPbU+4Bi06Jjal+PFLSMmlWM1BEwyLxZ3aKkf1ROnoIakfaA7mC6uw3ny7JBkau4Yc+5zfWw==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^2.12.0", + "@smithy/types": "^3.3.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=14.0.0" + "node": ">=16.0.0" } }, "node_modules/@smithy/util-retry": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-2.2.0.tgz", - "integrity": "sha512-q9+pAFPTfftHXRytmZ7GzLFFrEGavqapFc06XxzZFcSIGERXMerXxCitjOG1prVDR9QdjqotF40SWvbqcCpf8g==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-3.0.3.tgz", + "integrity": "sha512-AFw+hjpbtVApzpNDhbjNG5NA3kyoMs7vx0gsgmlJF4s+yz1Zlepde7J58zpIRIsdjc+emhpAITxA88qLkPF26w==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/service-error-classification": "^2.1.5", - "@smithy/types": "^2.12.0", + "@smithy/service-error-classification": "^3.0.3", + "@smithy/types": "^3.3.0", "tslib": "^2.6.2" }, "engines": { - "node": ">= 14.0.0" + "node": ">=16.0.0" } }, "node_modules/@smithy/util-stream": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-2.2.0.tgz", - "integrity": "sha512-17faEXbYWIRst1aU9SvPZyMdWmqIrduZjVOqCPMIsWFNxs5yQQgFrJL6b2SdiCzyW9mJoDjFtgi53xx7EH+BXA==", - "dev": true, - "dependencies": { - "@smithy/fetch-http-handler": "^2.5.0", - "@smithy/node-http-handler": "^2.5.0", - "@smithy/types": "^2.12.0", - "@smithy/util-base64": "^2.3.0", - "@smithy/util-buffer-from": "^2.2.0", - "@smithy/util-hex-encoding": "^2.2.0", - "@smithy/util-utf8": "^2.3.0", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-3.1.3.tgz", + "integrity": "sha512-FIv/bRhIlAxC0U7xM1BCnF2aDRPq0UaelqBHkM2lsCp26mcBbgI0tCVTv+jGdsQLUmAMybua/bjDsSu8RQHbmw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/fetch-http-handler": "^3.2.4", + "@smithy/node-http-handler": "^3.1.4", + "@smithy/types": "^3.3.0", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-buffer-from": "^3.0.0", + "@smithy/util-hex-encoding": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=14.0.0" + "node": ">=16.0.0" } }, "node_modules/@smithy/util-uri-escape": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-2.2.0.tgz", - "integrity": "sha512-jtmJMyt1xMD/d8OtbVJ2gFZOSKc+ueYJZPW20ULW1GOp/q/YIM0wNh+u8ZFao9UaIGz4WoPW8hC64qlWLIfoDA==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-3.0.0.tgz", + "integrity": "sha512-LqR7qYLgZTD7nWLBecUi4aqolw8Mhza9ArpNEQ881MJJIU2sE5iHCK6TdyqqzcDLy0OPe10IY4T8ctVdtynubg==", "dev": true, + "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" }, "engines": { - "node": ">=14.0.0" + "node": ">=16.0.0" } }, "node_modules/@smithy/util-utf8": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", - "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-3.0.0.tgz", + "integrity": "sha512-rUeT12bxFnplYDe815GXbq/oixEGHfRFFtcTF3YdDi/JaENIM6aSYYLJydG83UNzLXeRI5K8abYd/8Sp/QM0kA==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/util-buffer-from": "^2.2.0", + "@smithy/util-buffer-from": "^3.0.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=14.0.0" + "node": ">=16.0.0" } }, "node_modules/@smithy/util-waiter": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-2.2.0.tgz", - "integrity": "sha512-IHk53BVw6MPMi2Gsn+hCng8rFA3ZmR3Rk7GllxDUW9qFJl/hiSvskn7XldkECapQVkIg/1dHpMAxI9xSTaLLSA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-3.1.2.tgz", + "integrity": "sha512-4pP0EV3iTsexDx+8PPGAKCQpd/6hsQBaQhqWzU4hqKPHN5epPsxKbvUTIiYIHTxaKt6/kEaqPBpu/ufvfbrRzw==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/abort-controller": "^2.2.0", - "@smithy/types": "^2.12.0", + "@smithy/abort-controller": "^3.1.1", + "@smithy/types": "^3.3.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=14.0.0" + "node": ">=16.0.0" } }, "node_modules/@types/babel__core": { @@ -4276,10 +4615,11 @@ } }, "node_modules/@types/jest": { - "version": "29.5.5", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.5.tgz", - "integrity": "sha512-ebylz2hnsWR9mYvmBFbXJXr+33UPc4+ZdxyDXh5w0FlPBTfCVN3wPL+kuOiQt3xvrK419v7XWeAs+AeOksafXg==", + "version": "29.5.12", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.12.tgz", + "integrity": "sha512-eDC8bTvT/QhYdxJAulQikueigY5AsdBRH2yDKW3yveW7svY3+DzN84/2NUgkw10RTiJbWqZrTtoGVdYlvFJdLw==", "dev": true, + "license": "MIT", "dependencies": { "expect": "^29.0.0", "pretty-format": "^29.0.0" @@ -4301,16 +4641,18 @@ "dev": true }, "node_modules/@types/tough-cookie": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.3.tgz", - "integrity": "sha512-THo502dA5PzG/sfQH+42Lw3fvmYkceefOspdCwpHRul8ik2Jv1K8I5OZz1AT3/rs46kwgMCe9bSBmDLYkkOMGg==", - "dev": true + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", + "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==", + "dev": true, + "license": "MIT" }, "node_modules/@types/uuid": { - "version": "9.0.4", - "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.4.tgz", - "integrity": "sha512-zAuJWQflfx6dYJM62vna+Sn5aeSWhh3OB+wfUEACNcqUSc0AGc5JKl+ycL1vrH7frGTXhJchYjE1Hak8L819dA==", - "dev": true + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==", + "dev": true, + "license": "MIT" }, "node_modules/@types/yargs": { "version": "17.0.32", @@ -4338,6 +4680,7 @@ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", "dev": true, + "license": "MIT", "dependencies": { "debug": "^4.3.4" }, @@ -4396,6 +4739,7 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, + "license": "MIT", "dependencies": { "color-convert": "^1.9.0" }, @@ -4438,26 +4782,28 @@ "dev": true }, "node_modules/axios": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.5.0.tgz", - "integrity": "sha512-D4DdjDo5CY50Qms0qGQTTw6Q44jl7zRwY7bthds06pUGfChBCTcQs+N743eFWGEd6pRTMd6A+I87aWyFV5wiZQ==", + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.3.tgz", + "integrity": "sha512-Ar7ND9pU99eJ9GpoGQKhKf58GpUOgnzuaB7ueNQ5BMi0p+LZ5oaEnfF999fAArcTIBwXTCHAmGcHOZJaWPq9Nw==", "dev": true, + "license": "MIT", "dependencies": { - "follow-redirects": "^1.15.0", + "follow-redirects": "^1.15.6", "form-data": "^4.0.0", "proxy-from-env": "^1.1.0" } }, "node_modules/axios-cookiejar-support": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/axios-cookiejar-support/-/axios-cookiejar-support-4.0.7.tgz", - "integrity": "sha512-9vpE3y/a2l2Vs2XEJE4L2z0GWnlpJ4Xj+kDaoCtrpPfS1J3oikXBrxRJX6H62/ZcelOGe+519yW7mqXCIoPXuw==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/axios-cookiejar-support/-/axios-cookiejar-support-5.0.2.tgz", + "integrity": "sha512-4KwFqiH62Ry/J+ue1NwqLvBSaK3Bp/Owc9jTe7BS9c22uN8/4MB5pY7HV9QwQjV3ZRonCoSj5V8Pjx911qQqAw==", "dev": true, + "license": "MIT", "dependencies": { - "http-cookie-agent": "^5.0.4" + "http-cookie-agent": "^6.0.5" }, "engines": { - "node": ">=14.18.0 <15.0.0 || >=16.0.0" + "node": ">=18.0.0" }, "funding": { "url": "https://github.com/sponsors/3846masa" @@ -4590,19 +4936,52 @@ } }, "node_modules/babel-plugin-module-resolver": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/babel-plugin-module-resolver/-/babel-plugin-module-resolver-5.0.0.tgz", - "integrity": "sha512-g0u+/ChLSJ5+PzYwLwP8Rp8Rcfowz58TJNCe+L/ui4rpzE/mg//JVX0EWBUYoxaextqnwuGHzfGp2hh0PPV25Q==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/babel-plugin-module-resolver/-/babel-plugin-module-resolver-5.0.2.tgz", + "integrity": "sha512-9KtaCazHee2xc0ibfqsDeamwDps6FZNo5S0Q81dUqEuFzVwPhcT4J5jOqIVvgCA3Q/wO9hKYxN/Ds3tIsp5ygg==", "dev": true, + "license": "MIT", "dependencies": { - "find-babel-config": "^2.0.0", - "glob": "^8.0.3", + "find-babel-config": "^2.1.1", + "glob": "^9.3.3", "pkg-up": "^3.1.0", "reselect": "^4.1.7", - "resolve": "^1.22.1" + "resolve": "^1.22.8" + } + }, + "node_modules/babel-plugin-module-resolver/node_modules/glob": { + "version": "9.3.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-9.3.5.tgz", + "integrity": "sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q==", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "minimatch": "^8.0.2", + "minipass": "^4.2.4", + "path-scurry": "^1.6.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/babel-plugin-module-resolver/node_modules/minimatch": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-8.0.4.tgz", + "integrity": "sha512-W0Wvr9HyFXZRGIDgCicunpQ299OKXs9RgZfaukz4qAW/pJhcpUfupc9c+OObPOFueNy8VSrZgEmDtk6Kh4WzDA==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" }, "engines": { - "node": ">= 16" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/babel-plugin-polyfill-corejs2": { @@ -4620,57 +4999,27 @@ } }, "node_modules/babel-plugin-polyfill-corejs3": { - "version": "0.8.7", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.8.7.tgz", - "integrity": "sha512-KyDvZYxAzkC0Aj2dAPyDzi2Ym15e5JKZSK+maI7NAwSqofvuFglbSsxE7wUOvTg9oFVnHMzVzBKcqEb4PJgtOA==", - "dev": true, - "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.4.4", - "core-js-compat": "^3.33.1" - }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" - } - }, - "node_modules/babel-plugin-polyfill-corejs3/node_modules/@babel/helper-define-polyfill-provider": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.4.4.tgz", - "integrity": "sha512-QcJMILQCu2jm5TFPGA3lCpJJTeEP+mqeXooG/NZbg/h5FTFi6V0+99ahlRsW8/kRLyb24LZVCCiclDedhLKcBA==", + "version": "0.10.6", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.10.6.tgz", + "integrity": "sha512-b37+KR2i/khY5sKmWNVQAnitvquQbNdWy6lJdsr0kmquCKEEUgMKK4SboVM3HtfnZilfjr4MMQ7vY58FVWDtIA==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-compilation-targets": "^7.22.6", - "@babel/helper-plugin-utils": "^7.22.5", - "debug": "^4.1.1", - "lodash.debounce": "^4.0.8", - "resolve": "^1.14.2" + "@babel/helper-define-polyfill-provider": "^0.6.2", + "core-js-compat": "^3.38.0" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "node_modules/babel-plugin-polyfill-regenerator": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.5.tgz", - "integrity": "sha512-OJGYZlhLqBh2DDHeqAxWB1XIvr49CxiJ2gIt61/PU55CQK4Z58OzMqjDe1zwQdQk+rBYsRc+1rJmdajM3gimHg==", - "dev": true, - "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.5.0" - }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" - } - }, - "node_modules/babel-plugin-polyfill-regenerator/node_modules/@babel/helper-define-polyfill-provider": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.5.0.tgz", - "integrity": "sha512-NovQquuQLAQ5HuyjCz7WQP9MjRj7dx++yspwiyUiGl9ZyadHRSql1HZh5ogRd8W8w6YM6EQ/NTB8rgjLt5W65Q==", + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.2.tgz", + "integrity": "sha512-2R25rQZWP63nGwaAswvDazbPXfrM3HwVoBXK6HcqeKrSrL/JqcC/rDcf95l4r7LXLyxDXc8uQDa064GubtCABg==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-compilation-targets": "^7.22.6", - "@babel/helper-plugin-utils": "^7.22.5", - "debug": "^4.1.1", - "lodash.debounce": "^4.0.8", - "resolve": "^1.14.2" + "@babel/helper-define-polyfill-provider": "^0.6.2" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" @@ -4770,7 +5119,8 @@ "version": "2.11.0", "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz", "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/brace-expansion": { "version": "2.0.1", @@ -4782,12 +5132,13 @@ } }, "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, + "license": "MIT", "dependencies": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" }, "engines": { "node": ">=8" @@ -4797,12 +5148,13 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==", - "dev": true + "dev": true, + "license": "BSD-2-Clause" }, "node_modules/browserslist": { - "version": "4.23.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz", - "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==", + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.3.tgz", + "integrity": "sha512-btwCFJVjI4YWDNfau8RhZ+B1Q/VLoUITrm3RlP6y1tYGWIOa+InuYiRGXUBXo8nA1qKmHMyLB/iVQg5TT4eFoA==", "dev": true, "funding": [ { @@ -4818,11 +5170,12 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001587", - "electron-to-chromium": "^1.4.668", - "node-releases": "^2.0.14", - "update-browserslist-db": "^1.0.13" + "caniuse-lite": "^1.0.30001646", + "electron-to-chromium": "^1.5.4", + "node-releases": "^2.0.18", + "update-browserslist-db": "^1.1.0" }, "bin": { "browserslist": "cli.js" @@ -4869,10 +5222,11 @@ "dev": true }, "node_modules/bunyamin": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/bunyamin/-/bunyamin-1.6.1.tgz", - "integrity": "sha512-gm44niEx19vp7XYB8nATtQ8WOK9lCU8fN3F2c9nXnD5TYr0qoxNZB1jxfzOYTz5he7lDBxrtvswSDrbiFf7WiA==", + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/bunyamin/-/bunyamin-1.6.3.tgz", + "integrity": "sha512-m1hAijFhu8pFiidsVc0XEDic46uxPK+mKNLqkb5mluNx0nTolNzx/DjwMqHChQWCgfOLMjKYJJ2uPTQLE6t4Ng==", "dev": true, + "license": "MIT", "dependencies": { "@flatten-js/interval-tree": "^1.1.2", "multi-sort-stream": "^1.0.4", @@ -4903,6 +5257,7 @@ "engines": [ "node >=0.10.0" ], + "license": "MIT", "bin": { "bunyan": "bin/bunyan" }, @@ -4918,6 +5273,7 @@ "resolved": "https://registry.npmjs.org/bunyan-debug-stream/-/bunyan-debug-stream-3.1.0.tgz", "integrity": "sha512-VaFYbDVdiSn3ZpdozrjZ8mFpxHXl26t11C1DKRQtbo0EgffqeFNrRLOGIESKVeGEvVu4qMxMSSxzNlSw7oTj7w==", "dev": true, + "license": "MIT", "dependencies": { "chalk": "^4.1.2" }, @@ -4933,6 +5289,7 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, + "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -4948,6 +5305,7 @@ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -4964,6 +5322,7 @@ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, + "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, @@ -4975,13 +5334,15 @@ "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 + "dev": true, + "license": "MIT" }, "node_modules/bunyan-debug-stream/node_modules/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, + "license": "MIT", "engines": { "node": ">=8" } @@ -4991,6 +5352,7 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, + "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -5029,9 +5391,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001612", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001612.tgz", - "integrity": "sha512-lFgnZ07UhaCcsSZgWW0K5j4e69dK1u/ltrL9lTUiFOwNHs12S3UMIEYgBV0Z6C6hRDev7iRnMzzYmKabYdXF9g==", + "version": "1.0.30001649", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001649.tgz", + "integrity": "sha512-fJegqZZ0ZX8HOWr6rcafGr72+xcgJKI9oWfDW5DrD7ExUtgZC7a7R7ZYmZqplh7XDocFdGeIFn7roAxhOeYrPQ==", "dev": true, "funding": [ { @@ -5046,13 +5408,15 @@ "type": "github", "url": "https://github.com/sponsors/ai" } - ] + ], + "license": "CC-BY-4.0" }, "node_modules/chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -5163,6 +5527,7 @@ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "dev": true, + "license": "MIT", "dependencies": { "color-name": "1.1.3" } @@ -5171,7 +5536,8 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/combined-stream": { "version": "1.0.8", @@ -5186,12 +5552,13 @@ } }, "node_modules/commander": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", - "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.0.0.tgz", + "integrity": "sha512-MwVNWlYjDTtOjX5PiD7o5pK0UrFU/OYgcJfjjK4RaHZETNtjJqrZa9Y9ds88+A+f+d5lv+561eZ+yCKoS3gbAA==", "dev": true, + "license": "MIT", "engines": { - "node": ">=14" + "node": ">=18" } }, "node_modules/concat-map": { @@ -5207,12 +5574,13 @@ "dev": true }, "node_modules/core-js-compat": { - "version": "3.37.0", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.37.0.tgz", - "integrity": "sha512-vYq4L+T8aS5UuFg4UwDhc7YNRWVeVZwltad9C/jV3R2LgVOpS9BDr7l/WL6BN0dbV3k1XejPTHqqEzJgsa0frA==", + "version": "3.38.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.38.0.tgz", + "integrity": "sha512-75LAicdLa4OJVwFxFbQR3NdnZjNgX6ILpVcVzcC4T2smerB5lELMrJQQQoWV6TiuC/vlaFqgU2tKQx9w5s0e0A==", "dev": true, + "license": "MIT", "dependencies": { - "browserslist": "^4.23.0" + "browserslist": "^4.23.3" }, "funding": { "type": "opencollective", @@ -5422,11 +5790,12 @@ } }, "node_modules/detox": { - "version": "20.19.3", - "resolved": "https://registry.npmjs.org/detox/-/detox-20.19.3.tgz", - "integrity": "sha512-5axCZVfVNB4SgKej4ONIAVXkPgvWXd5ULSTemmWGA8oLRmlZpELrhBXgAcTP4MEY/tArG1v0Cx4NWyoda/Lyiw==", + "version": "20.25.2", + "resolved": "https://registry.npmjs.org/detox/-/detox-20.25.2.tgz", + "integrity": "sha512-2M6jeVw5mEEg9Pk4GmmzxuASZ0dL138/fycyKAZ3mS/kF8ohnjg6Om/pw1gQaWWmFQ2BEwp6vHQtZMT0yTqdbQ==", "dev": true, "hasInstallScript": true, + "license": "MIT", "dependencies": { "ajv": "^8.6.3", "bunyan": "^1.8.12", @@ -5440,7 +5809,7 @@ "funpermaproxy": "^1.1.0", "glob": "^8.0.3", "ini": "^1.3.4", - "jest-environment-emit": "^1.0.5", + "jest-environment-emit": "^1.0.8", "json-cycle": "^1.3.0", "lodash": "^4.17.11", "multi-sort-stream": "^1.0.3", @@ -5468,7 +5837,7 @@ "detox": "local-cli/cli.js" }, "engines": { - "node": ">=18" + "node": ">=14" }, "peerDependencies": { "jest": "29.x.x || 28.x.x || ^27.2.5" @@ -5668,6 +6037,7 @@ "integrity": "sha512-b7Z7cNtHPhH9EJhNNbbeqTcXB8LGFFZhq1PGgEvpeHlzd36bhbdTWoE/Ba/YguqpBSlAPKnARWhVlhunCMwfxg==", "dev": true, "hasInstallScript": true, + "license": "BSD-2-Clause", "optional": true, "dependencies": { "nan": "^2.14.0" @@ -5690,15 +6060,33 @@ "resolved": "https://registry.npmjs.org/easy-stack/-/easy-stack-1.0.1.tgz", "integrity": "sha512-wK2sCs4feiiJeFXn3zvY0p41mdU5VUgbgs1rNsc/y5ngFUijdWd+iIN8eoyuZHKB8xN6BL4PdWmzqFmxNg6V2w==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.0.0" } }, + "node_modules/ejs": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", + "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "jake": "^10.8.5" + }, + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/electron-to-chromium": { - "version": "1.4.748", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.748.tgz", - "integrity": "sha512-VWqjOlPZn70UZ8FTKUOkUvBLeTQ0xpty66qV0yJcAGY2/CthI4xyW9aEozRVtuwv3Kpf5xTesmJUcPwuJmgP4A==", - "dev": true + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.4.tgz", + "integrity": "sha512-orzA81VqLyIGUEA77YkVA1D+N+nNfl2isJVjjmOyrlxuooZ19ynb+dOlaDTqd/idKRS9lDCSBmtzM+kyCsMnkA==", + "dev": true, + "license": "ISC" }, "node_modules/emittery": { "version": "0.13.1", @@ -5741,6 +6129,7 @@ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.8.0" } @@ -5772,6 +6161,7 @@ "resolved": "https://registry.npmjs.org/event-pubsub/-/event-pubsub-4.3.0.tgz", "integrity": "sha512-z7IyloorXvKbFx9Bpie2+vMJKKx1fH1EN5yiTfp8CiLOTptSYy1g8H4yDpGlEdshL1PBiFtBHepF2cNsqeEeFQ==", "dev": true, + "license": "Unlicense", "engines": { "node": ">=4.0.0" } @@ -5842,6 +6232,7 @@ "resolved": "https://registry.npmjs.org/exeunt/-/exeunt-1.1.0.tgz", "integrity": "sha512-dd++Yn/0Fp+gtJ04YHov7MeAii+LFivJc6KqnJNfplzLVUkUDrfKoQDTLlCgzcW15vY5hKlHasWeIsQJ8agHsw==", "dev": true, + "license": "MPL-2.0", "engines": { "node": ">=0.10" } @@ -5878,10 +6269,11 @@ "dev": true }, "node_modules/fast-glob": { - "version": "3.2.12", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", - "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", "dev": true, + "license": "MIT", "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", @@ -5900,20 +6292,21 @@ "dev": true }, "node_modules/fast-xml-parser": { - "version": "4.2.5", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.2.5.tgz", - "integrity": "sha512-B9/wizE4WngqQftFPmdaMYlXoJlJOYxGQOanC77fq9k8+Z0v5dDSVh+3glErdIROP//s/jgb7ZuxKfB8nVyo0g==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.4.1.tgz", + "integrity": "sha512-xkjOecfnKGkSsOwtZ5Pz7Us/T6mrbPQrq0nh+aCO5V9nk5NLWmasAHumTKjiPJPWANe+kAZ84Jc8ooJkzZ88Sw==", "dev": true, "funding": [ - { - "type": "paypal", - "url": "https://paypal.me/naturalintelligence" - }, { "type": "github", "url": "https://github.com/sponsors/NaturalIntelligence" + }, + { + "type": "paypal", + "url": "https://paypal.me/naturalintelligence" } ], + "license": "MIT", "dependencies": { "strnum": "^1.0.5" }, @@ -5926,6 +6319,7 @@ "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", "dev": true, + "license": "ISC", "dependencies": { "reusify": "^1.0.4" } @@ -5939,11 +6333,22 @@ "bser": "2.1.1" } }, + "node_modules/filelist": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", + "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "minimatch": "^5.0.1" + } + }, "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, + "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" }, @@ -6065,6 +6470,7 @@ "resolved": "https://registry.npmjs.org/funpermaproxy/-/funpermaproxy-1.1.0.tgz", "integrity": "sha512-2Sp1hWuO8m5fqeFDusyhKqYPT+7rGLw34N3qonDcdRP8+n7M7Gl/yKp/q7oCxnnJ6pWCectOmLFJpsMU/++KrQ==", "dev": true, + "license": "Apache-2.0", "engines": { "node": ">=8.3.0" } @@ -6132,6 +6538,7 @@ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, + "license": "ISC", "dependencies": { "is-glob": "^4.0.1" }, @@ -6159,6 +6566,7 @@ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } @@ -6221,28 +6629,25 @@ "dev": true }, "node_modules/http-cookie-agent": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/http-cookie-agent/-/http-cookie-agent-5.0.4.tgz", - "integrity": "sha512-OtvikW69RvfyP6Lsequ0fN5R49S+8QcS9zwd58k6VSr6r57T8G29BkPdyrBcSwLq6ExLs9V+rBlfxu7gDstJag==", + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/http-cookie-agent/-/http-cookie-agent-6.0.5.tgz", + "integrity": "sha512-sfZ8fDgDP3B1YB+teqSnAK1aPgBu8reUUGxSsndP2XnYN6cM29EURXWXZqQQiaRdor3B4QjpkUNfv21syaO4DA==", "dev": true, + "license": "MIT", "dependencies": { - "agent-base": "^7.1.0" + "agent-base": "^7.1.1" }, "engines": { - "node": ">=14.18.0 <15.0.0 || >=16.0.0" + "node": ">=18.0.0" }, "funding": { "url": "https://github.com/sponsors/3846masa" }, "peerDependencies": { - "deasync": "^0.1.26", "tough-cookie": "^4.0.0", - "undici": "^5.11.0" + "undici": "^5.11.0 || ^6.0.0" }, "peerDependenciesMeta": { - "deasync": { - "optional": true - }, "undici": { "optional": true } @@ -6374,6 +6779,7 @@ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -6401,6 +6807,7 @@ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, + "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" }, @@ -6413,6 +6820,7 @@ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.12.0" } @@ -6549,6 +6957,125 @@ "node": ">=8" } }, + "node_modules/jake": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.2.tgz", + "integrity": "sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "async": "^3.2.3", + "chalk": "^4.0.2", + "filelist": "^1.0.4", + "minimatch": "^3.1.2" + }, + "bin": { + "jake": "bin/cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jake/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jake/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/jake/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jake/node_modules/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, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jake/node_modules/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, + "license": "MIT" + }, + "node_modules/jake/node_modules/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, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/jake/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/jake/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/jest": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", @@ -7134,10 +7661,11 @@ } }, "node_modules/jest-environment-emit": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/jest-environment-emit/-/jest-environment-emit-1.0.7.tgz", - "integrity": "sha512-/0AYqbL3zrfRTtGyzTZwgRxQZiDXEM8ZUfY7Uscla/XGs9vszx4f0XTSZqAk3CQaiwYAoKvFZkB2vSKm1Q08fQ==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/jest-environment-emit/-/jest-environment-emit-1.0.8.tgz", + "integrity": "sha512-WNqvxBLH0yNojHJQ99Y21963aT7UTavxV3PgiBQFi8zwrlnKU6HvkB6LOvQrbk5I8mI8JEKvcoOrQOvBVMLIXQ==", "dev": true, + "license": "MIT", "dependencies": { "bunyamin": "^1.5.2", "bunyan": "^2.0.5", @@ -7184,6 +7712,7 @@ "engines": [ "node >=0.10.0" ], + "license": "MIT", "dependencies": { "exeunt": "1.1.0" }, @@ -7249,10 +7778,11 @@ } }, "node_modules/jest-html-reporters": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/jest-html-reporters/-/jest-html-reporters-3.1.4.tgz", - "integrity": "sha512-7lLrKDKDNBNDprd5lP241HRx2mRXb/XQOuYFxX/MxydgHtYRE/lEtK2+J5XLiNTs9JL/rUjWsWhIBOBs9j3wcg==", + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/jest-html-reporters/-/jest-html-reporters-3.1.7.tgz", + "integrity": "sha512-GTmjqK6muQ0S0Mnksf9QkL9X9z2FGIpNSxC52E0PHDzjPQ1XDu2+XTI3B3FS43ZiUzD1f354/5FfwbNIBzT7ew==", "dev": true, + "license": "MIT", "dependencies": { "fs-extra": "^10.0.0", "open": "^8.0.3" @@ -8009,10 +8539,11 @@ "dev": true }, "node_modules/jest-stare": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/jest-stare/-/jest-stare-2.5.1.tgz", - "integrity": "sha512-++3JWdY2zJNPFCN6ao1oeW0Qg8oKVYT9XaMUr8RaNDHDGKOQMNjmMrVz9E/4E43ZDU2mPTtk9U8pS+KjSuxPKg==", + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jest-stare/-/jest-stare-2.5.2.tgz", + "integrity": "sha512-dvxHXOsiJlvBi0n2dK9pz6RWFTdPB6njc2ZoMpyjmWI+aIL+X1W8OW5mTm1pkv/quy2ocKO/G+GsTe7Bv07xkQ==", "dev": true, + "license": "MIT", "dependencies": { "@jest/reporters": "^29.0.0", "@jest/test-result": "^29.0.0", @@ -8433,6 +8964,7 @@ "resolved": "https://registry.npmjs.org/js-message/-/js-message-1.0.7.tgz", "integrity": "sha512-efJLHhLjIyKRewNS9EGZ4UpI8NguuL6fKkhRxVuMmrGV2xN/0APGdQYwLFky5w9naebSZ0OwAGp0G6/2Cg90rA==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.6.0" } @@ -8442,6 +8974,7 @@ "resolved": "https://registry.npmjs.org/js-queue/-/js-queue-2.0.2.tgz", "integrity": "sha512-pbKLsbCfi7kriM3s1J4DDCo7jQkI58zPLHi0heXPzPlj0hjUsm+FesPUbE0DSbIVIK503A36aUBoCN7eMFedkA==", "dev": true, + "license": "MIT", "dependencies": { "easy-stack": "^1.0.1" }, @@ -8453,7 +8986,8 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/js-yaml": { "version": "3.14.1", @@ -8526,13 +9060,14 @@ } }, "node_modules/junit-report-merger": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/junit-report-merger/-/junit-report-merger-6.0.2.tgz", - "integrity": "sha512-Jk9PXeaJhbgo3aUNza2r24JgxYzLUtCk2kwrub8fbmDuWUdXhT/nfbM2MlU3JQiFbjVud1bzBWdzr9/GGBWfmA==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/junit-report-merger/-/junit-report-merger-7.0.0.tgz", + "integrity": "sha512-i7IYPpwVFpju+UKdxYIG9UTMb6NjfRGzWzE/lRExdEf4K3agqXtVBnJWhL9aMM2lNX7uWW/rhVidiDBsG4n5cw==", "dev": true, + "license": "MIT", "dependencies": { - "commander": "~10.0.0", - "fast-glob": "~3.2.11", + "commander": "~12.0.0", + "fast-glob": "~3.3.0", "xmlbuilder2": "3.1.1" }, "bin": { @@ -8540,7 +9075,7 @@ "junit-report-merger": "cli.js" }, "engines": { - "node": ">=14" + "node": ">=18" } }, "node_modules/kleur": { @@ -8601,7 +9136,8 @@ "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/lru-cache": { "version": "5.1.1", @@ -8695,6 +9231,7 @@ "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 8" } @@ -8759,11 +9296,22 @@ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", "dev": true, + "license": "MIT", "optional": true, "funding": { "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/minipass": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.2.8.tgz", + "integrity": "sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=8" + } + }, "node_modules/mkdirp": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", @@ -8786,10 +9334,11 @@ } }, "node_modules/moment-timezone": { - "version": "0.5.43", - "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.43.tgz", - "integrity": "sha512-72j3aNyuIsDxdF1i7CEgV2FfxM1r6aaqJyLB2vwb33mXYyoyLly+F1zbWqhA3/bVIoJ4szlUoMbUnVdid32NUQ==", + "version": "0.5.45", + "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.45.tgz", + "integrity": "sha512-HIWmqA86KcmCAhnMAN0wuDOARV/525R2+lOLotuGFzn4HO+FH+/645z2wx0Dt3iDv6/p61SIvKnDstISainhLQ==", "dev": true, + "license": "MIT", "dependencies": { "moment": "^2.29.4" }, @@ -8807,7 +9356,8 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/multi-sort-stream/-/multi-sort-stream-1.0.4.tgz", "integrity": "sha512-hAZ8JOEQFbgdLe8HWZbb7gdZg0/yAIHF00Qfo3kd0rXFv96nXe+/bPTrKHZ2QMHugGX4FiAyET1Lt+jiB+7Qlg==", - "dev": true + "dev": true, + "license": "bsd" }, "node_modules/multipipe": { "version": "4.0.0", @@ -8833,6 +9383,7 @@ "resolved": "https://registry.npmjs.org/mv/-/mv-2.1.1.tgz", "integrity": "sha512-at/ZndSy3xEGJ8i0ygALh8ru9qy7gWW1cmkaqBN29JmMlIvM//MEO9y1sk/avxuwnPcfhkejkLsuPxH81BrkSg==", "dev": true, + "license": "MIT", "optional": true, "dependencies": { "mkdirp": "~0.5.1", @@ -8848,6 +9399,7 @@ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", "dev": true, + "license": "MIT", "optional": true, "dependencies": { "minimist": "^1.2.6" @@ -8857,10 +9409,11 @@ } }, "node_modules/nan": { - "version": "2.19.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.19.0.tgz", - "integrity": "sha512-nO1xXxfh/RWNxfd/XPfbIfFk5vgLsAxUR9y5O0cHMJu/AW9U95JLXqthYHjEp+8gQ5p96K9jUp8nbVOxCdRbtw==", + "version": "2.20.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.20.0.tgz", + "integrity": "sha512-bk3gXBZDGILuuo/6sKtr0DQmSThYHLtNCdSdXk9YkxD/jK6X2vmCyyXBBxyqZ4XcnzTyYEAThfX3DCEnLf6igw==", "dev": true, + "license": "MIT", "optional": true }, "node_modules/natural-compare": { @@ -8874,6 +9427,7 @@ "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", "integrity": "sha512-zIdGUrPRFTUELUvr3Gmc7KZ2Sw/h1PiVM0Af/oHB6zgnV1ikqSfRk+TOufi79aHYCW3NiOXmr1BP5nWbzojLaA==", "dev": true, + "license": "MIT", "optional": true, "bin": { "ncp": "bin/ncp" @@ -8890,6 +9444,7 @@ "resolved": "https://registry.npmjs.org/node-ipc/-/node-ipc-9.2.1.tgz", "integrity": "sha512-mJzaM6O3xHf9VT8BULvJSbdVbmHUKRNOH7zDDkCrA1/T+CVjq2WVIDfLt0azZRXpgArJtl3rtmEozrbXPZ9GaQ==", "dev": true, + "license": "MIT", "dependencies": { "event-pubsub": "4.3.0", "js-message": "1.0.7", @@ -8900,10 +9455,11 @@ } }, "node_modules/node-releases": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", - "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", - "dev": true + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", + "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", + "dev": true, + "license": "MIT" }, "node_modules/node-version": { "version": "1.2.0", @@ -9102,11 +9658,46 @@ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/path-scurry/node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", + "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", + "dev": true, + "license": "ISC" }, "node_modules/picomatch": { "version": "2.3.1", @@ -9437,7 +10028,8 @@ "type": "consulting", "url": "https://feross.org/support" } - ] + ], + "license": "MIT" }, "node_modules/react-is": { "version": "18.2.0", @@ -9534,13 +10126,15 @@ "version": "0.14.1", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/regenerator-transform": { "version": "0.15.2", "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.2.tgz", "integrity": "sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==", "dev": true, + "license": "MIT", "dependencies": { "@babel/runtime": "^7.8.4" } @@ -9674,6 +10268,7 @@ "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", "dev": true, + "license": "MIT", "engines": { "iojs": ">=1.0.0", "node": ">=0.10.0" @@ -9683,7 +10278,9 @@ "version": "2.4.5", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz", "integrity": "sha512-J5xnxTyqaiw06JjMftq7L9ouA448dw/E7dKghkP9WpKNuwmARNNg+Gk8/u5ryb9N/Yo2+z3MCwuqFK/+qPOPfQ==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", "dev": true, + "license": "ISC", "optional": true, "dependencies": { "glob": "^6.0.1" @@ -9697,6 +10294,7 @@ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, + "license": "MIT", "optional": true, "dependencies": { "balanced-match": "^1.0.0", @@ -9707,7 +10305,9 @@ "version": "6.0.4", "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", "integrity": "sha512-MKZeRNyYZAVVVG1oZeLaWie1uweH40m9AZwIwxyPbTSX4hHrVYSzLg0Ro5Z5R7XKkIX+Cc6oD1rqeDJnwsB8/A==", + "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, + "license": "ISC", "optional": true, "dependencies": { "inflight": "^1.0.4", @@ -9725,6 +10325,7 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, + "license": "ISC", "optional": true, "dependencies": { "brace-expansion": "^1.1.7" @@ -9752,6 +10353,7 @@ "url": "https://feross.org/support" } ], + "license": "MIT", "dependencies": { "queue-microtask": "^1.2.2" } @@ -9781,6 +10383,7 @@ "resolved": "https://registry.npmjs.org/safe-json-stringify/-/safe-json-stringify-1.2.0.tgz", "integrity": "sha512-gH8eh2nZudPQO6TytOvbxnuhYBOvDBBLW52tz5q6X58lJcd/tkmqFR+5Z9adS8aJtURSXWThWy/xJtJwixErvg==", "dev": true, + "license": "MIT", "optional": true }, "node_modules/sanitize-filename": { @@ -10029,13 +10632,15 @@ "version": "2.2.5", "resolved": "https://registry.npmjs.org/stream-chain/-/stream-chain-2.2.5.tgz", "integrity": "sha512-1TJmBx6aSWqZ4tx7aTpBDXK0/e2hhcNSTV8+CbFJtDjbb+I1mZ8lHit0Grw9GRT+6JbIrrDd8esncgBi8aBXGA==", - "dev": true + "dev": true, + "license": "BSD-3-Clause" }, "node_modules/stream-json": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/stream-json/-/stream-json-1.8.0.tgz", "integrity": "sha512-HZfXngYHUAr1exT4fxlbc1IOce1RYxp2ldeaf97LYCOPSoOqY/1Psp7iGvpb+6JIOgkra9zDYnPX01hGAHzEPw==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "stream-chain": "^2.2.5" } @@ -10128,13 +10733,15 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, + "license": "MIT", "dependencies": { "has-flag": "^3.0.0" }, @@ -10281,6 +10888,7 @@ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, + "license": "MIT", "dependencies": { "is-number": "^7.0.0" }, @@ -10289,10 +10897,11 @@ } }, "node_modules/tough-cookie": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz", - "integrity": "sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==", + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz", + "integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "psl": "^1.1.33", "punycode": "^2.1.1", @@ -10317,6 +10926,7 @@ "resolved": "https://registry.npmjs.org/trace-event-lib/-/trace-event-lib-1.4.1.tgz", "integrity": "sha512-TOgFolKG8JFY+9d5EohGWMvwvteRafcyfPWWNIqcuD1W/FUvxWcy2MSCZ/beYHM63oYPHYHCd3tkbgCctHVP7w==", "dev": true, + "license": "MIT", "dependencies": { "browser-process-hrtime": "^1.0.0" }, @@ -10340,12 +10950,14 @@ "dev": true }, "node_modules/ts-jest": { - "version": "29.1.1", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.1.1.tgz", - "integrity": "sha512-D6xjnnbP17cC85nliwGiL+tpoKN0StpgE0TeOjXQTU6MVCfsB4v7aW05CgQ/1OywGb0x/oy9hHFnN+sczTiRaA==", + "version": "29.2.4", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.2.4.tgz", + "integrity": "sha512-3d6tgDyhCI29HlpwIq87sNuI+3Q6GLTTCeYRHCs7vDz+/3GCMwEtV9jezLyl4ZtnBgx00I7hm8PCP8cTksMGrw==", "dev": true, + "license": "MIT", "dependencies": { "bs-logger": "0.x", + "ejs": "^3.1.10", "fast-json-stable-stringify": "2.x", "jest-util": "^29.0.0", "json5": "^2.2.3", @@ -10358,10 +10970,11 @@ "ts-jest": "cli.js" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": "^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0" }, "peerDependencies": { "@babel/core": ">=7.0.0-beta.0 <8", + "@jest/transform": "^29.0.0", "@jest/types": "^29.0.0", "babel-jest": "^29.0.0", "jest": "^29.0.0", @@ -10371,6 +10984,9 @@ "@babel/core": { "optional": true }, + "@jest/transform": { + "optional": true + }, "@jest/types": { "optional": true }, @@ -10416,10 +11032,11 @@ "dev": true }, "node_modules/tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "dev": true + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==", + "dev": true, + "license": "0BSD" }, "node_modules/type-detect": { "version": "4.0.8", @@ -10443,10 +11060,11 @@ } }, "node_modules/typescript": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", - "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", + "version": "5.5.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz", + "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==", "dev": true, + "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -10511,9 +11129,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.0.13", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", - "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz", + "integrity": "sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==", "dev": true, "funding": [ { @@ -10529,9 +11147,10 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0" + "escalade": "^3.1.2", + "picocolors": "^1.0.1" }, "bin": { "update-browserslist-db": "cli.js" @@ -10572,14 +11191,15 @@ "dev": true }, "node_modules/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", "dev": true, "funding": [ "https://github.com/sponsors/broofa", "https://github.com/sponsors/ctavan" ], + "license": "MIT", "bin": { "uuid": "dist/bin/uuid" } @@ -10689,10 +11309,11 @@ } }, "node_modules/ws": { - "version": "7.5.9", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", - "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", + "version": "7.5.10", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", + "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=8.3.0" }, diff --git a/detox/package.json b/detox/package.json index 9e4536b070b..8c2510f7f34 100644 --- a/detox/package.json +++ b/detox/package.json @@ -4,41 +4,41 @@ "repository": "git@github.com:mattermost/mattermost-mobile.git", "author": "Mattermost, Inc.", "devDependencies": { - "@aws-sdk/client-s3": "3.445.0", - "@aws-sdk/lib-storage": "3.445.0", + "@aws-sdk/client-s3": "3.624.0", + "@aws-sdk/lib-storage": "3.624.0", "@babel/plugin-proposal-class-properties": "7.18.6", - "@babel/plugin-transform-modules-commonjs": "7.22.15", - "@babel/plugin-transform-runtime": "7.22.15", - "@babel/preset-env": "7.22.20", + "@babel/plugin-transform-modules-commonjs": "7.24.8", + "@babel/plugin-transform-runtime": "7.24.7", + "@babel/preset-env": "7.25.3", "@jest/test-sequencer": "29.7.0", - "@types/jest": "29.5.5", - "@types/tough-cookie": "4.0.3", - "@types/uuid": "9.0.4", + "@types/jest": "29.5.12", + "@types/tough-cookie": "4.0.5", + "@types/uuid": "10.0.0", "async": "3.2.5", - "axios": "1.5.0", - "axios-cookiejar-support": "4.0.7", + "axios": "1.7.3", + "axios-cookiejar-support": "5.0.2", "babel-jest": "29.7.0", - "babel-plugin-module-resolver": "5.0.0", + "babel-plugin-module-resolver": "5.0.2", "client-oauth2": "4.3.3", "deepmerge": "4.3.1", - "detox": "20.19.3", + "detox": "20.25.2", "form-data": "4.0.0", "jest": "29.7.0", "jest-circus": "29.7.0", "jest-cli": "29.7.0", - "jest-html-reporters": "3.1.4", + "jest-html-reporters": "3.1.7", "jest-junit": "16.0.0", - "jest-stare": "2.5.1", - "junit-report-merger": "6.0.2", - "moment-timezone": "0.5.43", + "jest-stare": "2.5.2", + "junit-report-merger": "7.0.0", + "moment-timezone": "0.5.45", "recursive-readdir": "2.2.3", "sanitize-filename": "1.6.3", "shelljs": "0.8.5", - "tough-cookie": "4.1.3", - "ts-jest": "29.1.1", - "tslib": "2.6.2", - "typescript": "5.2.2", - "uuid": "9.0.1", + "tough-cookie": "4.1.4", + "ts-jest": "29.2.4", + "tslib": "2.6.3", + "typescript": "5.5.4", + "uuid": "10.0.0", "xml2js": "0.6.2" }, "scripts": { diff --git a/detox/save_report.js b/detox/save_report.js index 8845d5b22a4..0b59d8829be 100644 --- a/detox/save_report.js +++ b/detox/save_report.js @@ -139,9 +139,12 @@ const saveReport = async () => { // Create or use an existing test cycle let testCycle = {}; - if (ZEPHYR_ENABLE === true) { + if (ZEPHYR_ENABLE === 'true') { const {start, end} = summary.stats; testCycle = ZEPHYR_CYCLE_KEY ? {key: ZEPHYR_CYCLE_KEY} : await createTestCycle(start, end); + if (!testCycle?.key) { + console.log('Failed to create test cycle'); + } } // Send test report to "QA: Mobile Test Automation Report" channel via webhook diff --git a/docs/architecture-diagram.monopic b/docs/architecture-diagram.monopic new file mode 100644 index 00000000000..55f14d1d04a Binary files /dev/null and b/docs/architecture-diagram.monopic differ diff --git a/docs/architecture-diagram.png b/docs/architecture-diagram.png new file mode 100644 index 00000000000..30ffb837a9f Binary files /dev/null and b/docs/architecture-diagram.png differ diff --git a/fastlane/Fastfile b/fastlane/Fastfile index 40dd1c10c0f..1668fc46f6d 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -699,6 +699,7 @@ platform :android do end update_identifiers replace_assets + pinned_certificates link_sentry({:os_type => "android"}) build_android move_android_to_root @@ -816,6 +817,10 @@ platform :android do sh 'cp -R ../assets/sounds/* ../android/app/src/main/res/raw/' end + lane :pinned_certificates do + sh 'cp -R ../assets/certs/ ../android/app/src/main/assets/certs' + end + lane :deploy do |options| file_path = options[:file] diff --git a/fastlane/Gemfile.lock b/fastlane/Gemfile.lock index 1d0fef2fb91..b2aeac871b1 100644 --- a/fastlane/Gemfile.lock +++ b/fastlane/Gemfile.lock @@ -11,25 +11,25 @@ GEM base64 nkf rexml - addressable (2.8.6) - public_suffix (>= 2.0.2, < 6.0) + addressable (2.8.7) + public_suffix (>= 2.0.2, < 7.0) artifactory (3.0.17) atomos (0.1.3) aws-eventstream (1.3.0) - aws-partitions (1.940.0) - aws-sdk-core (3.197.0) + aws-partitions (1.966.0) + aws-sdk-core (3.201.5) aws-eventstream (~> 1, >= 1.3.0) aws-partitions (~> 1, >= 1.651.0) - aws-sigv4 (~> 1.8) + aws-sigv4 (~> 1.9) jmespath (~> 1, >= 1.6.1) - aws-sdk-kms (1.83.0) - aws-sdk-core (~> 3, >= 3.197.0) - aws-sigv4 (~> 1.1) - aws-sdk-s3 (1.152.0) - aws-sdk-core (~> 3, >= 3.197.0) + aws-sdk-kms (1.88.0) + aws-sdk-core (~> 3, >= 3.201.0) + aws-sigv4 (~> 1.5) + aws-sdk-s3 (1.159.0) + aws-sdk-core (~> 3, >= 3.201.0) aws-sdk-kms (~> 1) - aws-sigv4 (~> 1.8) - aws-sigv4 (1.8.0) + aws-sigv4 (~> 1.5) + aws-sigv4 (1.9.1) aws-eventstream (~> 1, >= 1.0.2) babosa (1.0.4) base64 (0.2.0) @@ -44,7 +44,7 @@ GEM domain_name (0.6.20240107) dotenv (2.8.1) emoji_regex (3.2.3) - excon (0.110.0) + excon (0.111.0) faraday (1.10.3) faraday-em_http (~> 1.0) faraday-em_synchrony (~> 1.0) @@ -66,7 +66,7 @@ GEM faraday-httpclient (1.0.1) faraday-multipart (1.0.4) multipart-post (~> 2) - faraday-net_http (1.0.1) + faraday-net_http (1.0.2) faraday-net_http_persistent (1.2.0) faraday-patron (1.0.0) faraday-rack (1.0.0) @@ -74,7 +74,7 @@ GEM faraday_middleware (1.2.0) faraday (~> 1.0) fastimage (2.3.1) - fastlane (2.220.0) + fastlane (2.222.0) CFPropertyList (>= 2.3, < 4.0.0) addressable (>= 2.8, < 3.0.0) artifactory (~> 3.0) @@ -136,7 +136,7 @@ GEM google-apis-core (>= 0.11.0, < 2.a) google-apis-storage_v1 (0.31.0) google-apis-core (>= 0.11.0, < 2.a) - google-cloud-core (1.7.0) + google-cloud-core (1.7.1) google-cloud-env (>= 1.0, < 3.a) google-cloud-errors (~> 1.0) google-cloud-env (1.6.0) @@ -157,37 +157,39 @@ GEM os (>= 0.9, < 2.0) signet (>= 0.16, < 2.a) highline (2.0.3) - http-cookie (1.0.6) + http-cookie (1.0.7) domain_name (~> 0.5) httpclient (2.8.3) jmespath (1.6.2) json (2.7.2) - jwt (2.8.1) + jwt (2.8.2) base64 - mini_magick (4.12.0) + mini_magick (4.13.2) mini_mime (1.1.5) multi_json (1.15.0) multipart-post (2.4.1) nanaimo (0.3.0) naturally (2.2.1) nkf (0.2.0) - nokogiri (1.16.5-arm64-darwin) + nokogiri (1.16.7-arm64-darwin) racc (~> 1.4) - nokogiri (1.16.5-x86_64-darwin) + nokogiri (1.16.7-x86_64-darwin) + racc (~> 1.4) + nokogiri (1.16.7-x86_64-linux) racc (~> 1.4) optparse (0.5.0) os (1.1.4) plist (3.7.1) - public_suffix (5.0.5) - racc (1.8.0) + public_suffix (6.0.1) + racc (1.8.1) rake (13.2.1) representable (3.2.0) declarative (< 0.1.0) trailblazer-option (>= 0.1.1, < 0.2.0) uber (< 0.2.0) retriable (3.1.2) - rexml (3.2.8) - strscan (>= 3.0.9) + rexml (3.3.5) + strscan rouge (2.0.7) ruby2_keywords (0.0.5) rubyzip (2.3.2) @@ -212,13 +214,13 @@ GEM uber (0.1.0) unicode-display_width (2.5.0) word_wrap (1.0.0) - xcodeproj (1.24.0) + xcodeproj (1.25.0) CFPropertyList (>= 2.3.3, < 4.0) atomos (~> 0.1.3) claide (>= 1.0.2, < 2.0) colored2 (~> 3.1) nanaimo (~> 0.3.0) - rexml (~> 3.2.4) + rexml (>= 3.3.2, < 4.0) xcpretty (0.3.0) rouge (~> 2.0.7) xcpretty-travis-formatter (1.0.1) @@ -229,6 +231,7 @@ PLATFORMS arm64-darwin-23 x86_64-darwin-20 x86_64-darwin-23 + x86_64-linux DEPENDENCIES dotenv diff --git a/ios/Gekidou/Sources/Gekidou/Bundle+Extensions.swift b/ios/Gekidou/Sources/Gekidou/Bundle+Extensions.swift new file mode 100644 index 00000000000..71feade2523 --- /dev/null +++ b/ios/Gekidou/Sources/Gekidou/Bundle+Extensions.swift @@ -0,0 +1,15 @@ +import Foundation + +extension Bundle { + static var app: Bundle { + var components = main.bundleURL.path.split(separator: "/") + var bundle: Bundle? + + if let index = components.lastIndex(where: { $0.hasSuffix(".app") }) { + components.removeLast((components.count - 1) - index) + bundle = Bundle(path: components.joined(separator: "/")) + } + + return bundle ?? main + } +} diff --git a/ios/Gekidou/Sources/Gekidou/Networking/Network+Delegate.swift b/ios/Gekidou/Sources/Gekidou/Networking/Network+Delegate.swift index 413da51e88a..e69ee25aa71 100644 --- a/ios/Gekidou/Sources/Gekidou/Networking/Network+Delegate.swift +++ b/ios/Gekidou/Sources/Gekidou/Networking/Network+Delegate.swift @@ -1,24 +1,120 @@ import Foundation +import os.log extension Network: URLSessionDelegate, URLSessionTaskDelegate { + typealias ChallengeEvaluation = (disposition: URLSession.AuthChallengeDisposition, credential: URLCredential?, error: NetworkError?) + public func urlSession(_ session: URLSession, task: URLSessionTask, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) { - var credential: URLCredential? = nil - var disposition: URLSession.AuthChallengeDisposition = .performDefaultHandling + var evaluation: ChallengeEvaluation + switch challenge.protectionSpace.authenticationMethod { + #if canImport(Security) + case NSURLAuthenticationMethodServerTrust: + evaluation = attemptServerTrustAuthentication(with: challenge) + case NSURLAuthenticationMethodClientCertificate: + evaluation = attemptClientAuthentication(with: challenge) + #endif + default: + evaluation = (.performDefaultHandling, nil, nil) + } + + if let error = evaluation.error { + os_log("Gekidou: %{public}@", + log: .default, + type: .error, + error.localizedDescription + ) + } + + completionHandler(evaluation.disposition, evaluation.credential) + } + + func attemptClientAuthentication(with challenge: URLAuthenticationChallenge) -> ChallengeEvaluation{ + let host = challenge.protectionSpace.host + + guard let (identity, certificate) = try? Keychain.default.getClientIdentityAndCertificate(for: host) else { + return (.performDefaultHandling, nil, nil) + } + + return (.useCredential, URLCredential(identity: identity, + certificates: [certificate], + persistence: URLCredential.Persistence.permanent + ), nil) + } + + func attemptServerTrustAuthentication(with challenge: URLAuthenticationChallenge) -> ChallengeEvaluation { + let host = challenge.protectionSpace.host + + guard challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust, + let trust = challenge.protectionSpace.serverTrust + else { + return (.performDefaultHandling, nil, nil) + } + + do { + guard let certs = certificates[host], !certs.isEmpty else { + return (.performDefaultHandling, nil, nil) + } - let authMethod = challenge.protectionSpace.authenticationMethod - if authMethod == NSURLAuthenticationMethodClientCertificate { - let host = task.currentRequest!.url!.host! - if let (identity, certificate) = try? Keychain.default.getClientIdentityAndCertificate(for: host) { - credential = URLCredential(identity: identity, - certificates: [certificate], - persistence: URLCredential.Persistence.permanent) + try performDefaultValidation(trust) + + try performValidation(trust, forHost: host) + + try evaluate(trust, forHost: host, withCerts: certs) + + return (.useCredential, URLCredential(trust: trust), nil) + } catch { + os_log("Gekidou: %{public}@", + log: .default, + type: .error, + error.localizedDescription + ) + return (.cancelAuthenticationChallenge, nil, error as? NetworkError) + } + } + + private func getServerTrustCertificates(_ trust: SecTrust) -> [SecCertificate] { + if #available(iOS 15, macOS 12, tvOS 15, watchOS 8, visionOS 1, *) { + return (SecTrustCopyCertificateChain(trust) as? [SecCertificate]) ?? [] + } else { + return (0.. Void @@ -13,12 +14,15 @@ public class Network: NSObject { internal var session: URLSession? internal let queue = OperationQueue() internal let urlVersion = "/api/v4" + internal var certificates: [String: [SecCertificate]] = [:] @objc public static let `default` = Network() override private init() { super.init() + loadPinnedCertificates() + queue.maxConcurrentOperationCount = 1 let config = URLSessionConfiguration.default @@ -33,6 +37,41 @@ public class Network: NSObject { ) } + internal func loadPinnedCertificates() { + guard let certsPath = Bundle.app.resourceURL?.appendingPathComponent("certs") else { + return + } + + let fileManager = FileManager.default + do { + let certsArray = try fileManager.contentsOfDirectory(at: certsPath, includingPropertiesForKeys: nil, options: .skipsHiddenFiles) + let certs = certsArray.filter{ $0.pathExtension == "crt" || $0.pathExtension == "cer"} + for cert in certs { + if let domain = URL(string: cert.absoluteString)?.deletingPathExtension().lastPathComponent, + let certData = try? Data(contentsOf: cert), + let certificate = SecCertificateCreateWithData(nil, certData as CFData){ + if certificates[domain] != nil { + certificates[domain]?.append(certificate) + } else { + certificates[domain] = [certificate] + } + os_log("Gekidou: loaded certificate %{public}@ for domain %{public}@", + log: .default, + type: .info, + cert.lastPathComponent, domain + ) + } + } + } catch { + os_log( + "Gekidou: Error loading pinned certificates -- %{public}@", + log: .default, + type: .error, + String(describing: error) + ) + } + } + internal func buildApiUrl(_ serverUrl: String, _ endpoint: String) -> URL { return URL(string: "\(serverUrl)\(urlVersion)\(endpoint)")! } diff --git a/ios/Gekidou/Sources/Gekidou/Networking/ShareExtension+Delegate.swift b/ios/Gekidou/Sources/Gekidou/Networking/ShareExtension+Delegate.swift index f622c26b01d..ae71b1f3824 100644 --- a/ios/Gekidou/Sources/Gekidou/Networking/ShareExtension+Delegate.swift +++ b/ios/Gekidou/Sources/Gekidou/Networking/ShareExtension+Delegate.swift @@ -61,8 +61,9 @@ extension ShareExtension: URLSessionDataDelegate { let uploadData = getUploadSessionData(id: id) else { os_log( - OSLogType.default, "Mattermost BackgroundSession: didReceived failed to getUploadSessionData identifier=%{public}@", + log: .default, + type: .error, session.configuration.identifier ?? "no identifier" ) return @@ -95,6 +96,8 @@ extension ShareExtension: URLSessionDataDelegate { filename, fileId ) + + postMessageIfUploadFinished(forSession: session) } else { os_log( OSLogType.default, @@ -105,8 +108,9 @@ extension ShareExtension: URLSessionDataDelegate { } } catch { os_log( - OSLogType.default, "Mattermost BackgroundSession: Failed to receive data identifier=%{public}@ error=%{public}", + log: .default, + type: .error, id, error.localizedDescription ) @@ -116,48 +120,22 @@ extension ShareExtension: URLSessionDataDelegate { } public func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) { - if error == nil, - let id = session.configuration.identifier { - guard let data = getUploadSessionData(id: id) - else { - os_log( - OSLogType.default, - "Mattermost BackgroundSession: didCompleteWithError delegate failed to getUploadSessionData identifier=%{public}@", - session.configuration.identifier ?? "no identifier" - ) - return - - } - - let total = data.totalFiles - let count = data.fileIds.count + if error != nil { os_log( - OSLogType.default, - "Mattermost BackgroundSession: didCompleteWithError delegate for identifier=%{public}@ total files %{public}@ of %{public}@", - id, - "\(count)", - "\(total)" - ) - if data.fileIds.count == data.totalFiles { - ProcessInfo().performExpiringActivity( - withReason: "Need to post the message") {expires in - os_log( - OSLogType.default, - "Mattermost BackgroundSession: posting message for identifier=%{public}@", - id - ) - self.postMessageForSession(withId: id) - self.urlSessionDidFinishEvents(forBackgroundURLSession: session) - } - } - } else if error != nil { - os_log( - OSLogType.default, "Mattermost BackgroundSession: didCompleteWithError delegate failed identifier=%{public}@ with error %{public}@", + log: .default, + type: .error, session.configuration.identifier ?? "no identifier", error?.localizedDescription ?? "no error description available" ) + return } + + os_log( + OSLogType.default, + "Mattermost BackgroundSession: didCompleteWithError delegate for identifier=%{public}@ after calling postMessageIfUploadFinished", + session.configuration.identifier ?? "no identifier" + ) } @@ -180,4 +158,45 @@ extension ShareExtension: URLSessionDataDelegate { } }) } + + private func postMessageIfUploadFinished(forSession session: URLSession) { + lock.lock() + defer { lock.unlock() } + + guard let id = session.configuration.identifier, let data = getUploadSessionData(id: id) + else { + os_log( + "Mattermost BackgroundSession: postMessageIfUploadFinished failed to getUploadSessionData identifier=%{public}@", + log: .default, + type: .error, + session.configuration.identifier ?? "no identifier" + ) + return + } + + let total = data.totalFiles + let count = data.fileIds.count + + if count == total { + os_log( + OSLogType.default, + "Mattermost BackgroundSession: posting message for identifier=%{public}@", + id + ) + + ProcessInfo().performExpiringActivity( + withReason: "Need to post the message") {expires in + self.postMessageForSession(withId: id) + self.urlSessionDidFinishEvents(forBackgroundURLSession: session) + } + } else { + os_log( + OSLogType.default, + "Mattermost BackgroundSession: finish uploading files %{public}@ of %{public}@ for identifier=%{public}@", + "\(count)", + "\(total)", + id + ) + } + } } diff --git a/ios/Gekidou/Sources/Gekidou/Networking/ShareExtension+Post.swift b/ios/Gekidou/Sources/Gekidou/Networking/ShareExtension+Post.swift index 968feda623c..8e548b41069 100644 --- a/ios/Gekidou/Sources/Gekidou/Networking/ShareExtension+Post.swift +++ b/ios/Gekidou/Sources/Gekidou/Networking/ShareExtension+Post.swift @@ -98,9 +98,11 @@ extension ShareExtension { id ) return - } + self.removeUploadSessionData(id: id) + self.deleteUploadedFiles(files: data.files) + if let serverUrl = data.serverUrl, let channelId = data.channelId { Network.default.createPost( @@ -109,8 +111,15 @@ extension ShareExtension { message: data.message, fileIds: data.fileIds, completionHandler: {info, reponse, error in - self.removeUploadSessionData(id: id) - self.deleteUploadedFiles(files: data.files) + if let err = error { + os_log( + "Mattermost BackgroundSession: error to create post for session identifier=%{public}@ -- %{public}@", + log: .default, + type: .error, + id, + err.localizedDescription + ) + } if let handler = completionHandler { os_log( diff --git a/ios/Gekidou/Sources/Gekidou/Networking/ShareExtension.swift b/ios/Gekidou/Sources/Gekidou/Networking/ShareExtension.swift index 64504061f9f..24b13733ee5 100644 --- a/ios/Gekidou/Sources/Gekidou/Networking/ShareExtension.swift +++ b/ios/Gekidou/Sources/Gekidou/Networking/ShareExtension.swift @@ -54,6 +54,7 @@ public class ShareExtension: NSObject { private let fileMgr = FileManager.default private let preferences = Preferences.default public var completionHandler: (() -> Void)? + internal let lock = NSLock() public override init() { super.init() diff --git a/ios/Gekidou/Sources/Gekidou/PushNotification/PushNotification.swift b/ios/Gekidou/Sources/Gekidou/PushNotification/PushNotification.swift index 4e67f2c461b..e91c65e70fb 100644 --- a/ios/Gekidou/Sources/Gekidou/PushNotification/PushNotification.swift +++ b/ios/Gekidou/Sources/Gekidou/PushNotification/PushNotification.swift @@ -34,7 +34,7 @@ public class PushNotification: NSObject { "Mattermost Notifications: process receipt response for serverUrl %{public}@ does not contain data", ackNotification.serverUrl ) - completionHandler(notification) + completionHandler(nil) return } notification.userInfo["server_url"] = ackNotification.serverUrl @@ -75,9 +75,14 @@ public class PushNotification: NSObject { url, withMethod: "POST", withBody: jsonData, andHeaders: headers, forServerUrl: ackNotification.serverUrl) {[weak self] data, response, error in if error != nil && ackNotification.isIdLoaded, + let nsError = error as? NSError, let fibonacciBackoffsInSeconds = self?.fibonacciBackoffsInSeconds, let retryIndex = self?.retryIndex, fibonacciBackoffsInSeconds.count > retryIndex { + if nsError.code == NSURLErrorCancelled { + completionHandler(nil) + return + } let backoffInSeconds = fibonacciBackoffsInSeconds[retryIndex] self?.retryIndex += 1 diff --git a/ios/Mattermost.xcodeproj/project.pbxproj b/ios/Mattermost.xcodeproj/project.pbxproj index 4d64845c56a..795da23e34e 100644 --- a/ios/Mattermost.xcodeproj/project.pbxproj +++ b/ios/Mattermost.xcodeproj/project.pbxproj @@ -168,6 +168,7 @@ 7FD482672864DC5900A5B18B /* OpenSans-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = BC977883E2624E05975CA65B /* OpenSans-Regular.ttf */; }; 7FD482682864DC5900A5B18B /* OpenSans-SemiBold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = E5C16B14E1CE4868886A1A00 /* OpenSans-SemiBold.ttf */; }; 7FD482692864DC5900A5B18B /* OpenSans-SemiBoldItalic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 0DB14DFDF6E04FA69FE769DC /* OpenSans-SemiBoldItalic.ttf */; }; + 7FDEF5362C2D01CC0093AADB /* certs in Resources */ = {isa = PBXBuildFile; fileRef = 7FDEF5352C2D01CC0093AADB /* certs */; }; 7FF9C03D2983E7C6005CDCF5 /* ErrorSharingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7FF9C03C2983E7C6005CDCF5 /* ErrorSharingView.swift */; }; 83ABFD122C1C90D90029685B /* calls_dynamic.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = 83ABFD0E2C1C90D90029685B /* calls_dynamic.mp3 */; }; 83ABFD132C1C90D90029685B /* calls_cheerful.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = 83ABFD0F2C1C90D90029685B /* calls_cheerful.mp3 */; }; @@ -360,6 +361,7 @@ 7FCEFB9126B7934F006DC1DE /* SDWebImageDownloaderOperation+Swizzle.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SDWebImageDownloaderOperation+Swizzle.h"; sourceTree = ""; }; 7FCEFB9226B7934F006DC1DE /* SDWebImageDownloaderOperation+Swizzle.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "SDWebImageDownloaderOperation+Swizzle.m"; sourceTree = ""; }; 7FD482282864D69700A5B18B /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 7FDEF5352C2D01CC0093AADB /* certs */ = {isa = PBXFileReference; lastKnownFileType = folder; name = certs; path = ../assets/certs; sourceTree = ""; }; 7FF9C03C2983E7C6005CDCF5 /* ErrorSharingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorSharingView.swift; sourceTree = ""; }; 7FFE32B51FD9CCAA0038C7A0 /* FLAnimatedImage.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = FLAnimatedImage.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 7FFE32B61FD9CCAA0038C7A0 /* KSCrash.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = KSCrash.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -1004,6 +1006,7 @@ 83CBB9F61A601CBA00E9B192 = { isa = PBXGroup; children = ( + 7FDEF5352C2D01CC0093AADB /* certs */, 27C667AB2952425700E590D5 /* ErrorReporting */, 13B07FAE1A68108700A75B9A /* Mattermost */, 7F581D33221ED5C60099E66B /* NotificationService */, @@ -1214,6 +1217,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 7FDEF5362C2D01CC0093AADB /* certs in Resources */, 83ABFD132C1C90D90029685B /* calls_cheerful.mp3 in Resources */, 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */, 7F0F4B0A24BA173900E14C60 /* LaunchScreen.storyboard in Resources */, @@ -1980,7 +1984,7 @@ CODE_SIGN_ENTITLEMENTS = Mattermost/Mattermost.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - CURRENT_PROJECT_VERSION = 532; + CURRENT_PROJECT_VERSION = 555; DEVELOPMENT_TEAM = UQ8HT4Q2XM; ENABLE_BITCODE = NO; HEADER_SEARCH_PATHS = "$(inherited)"; @@ -2022,7 +2026,7 @@ CODE_SIGN_ENTITLEMENTS = Mattermost/Mattermost.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - CURRENT_PROJECT_VERSION = 532; + CURRENT_PROJECT_VERSION = 555; DEVELOPMENT_TEAM = UQ8HT4Q2XM; ENABLE_BITCODE = NO; HEADER_SEARCH_PATHS = "$(inherited)"; @@ -2165,7 +2169,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 532; + CURRENT_PROJECT_VERSION = 555; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = UQ8HT4Q2XM; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -2215,7 +2219,7 @@ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 532; + CURRENT_PROJECT_VERSION = 555; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = UQ8HT4Q2XM; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -2417,7 +2421,7 @@ repositoryURL = "https://github.com/getsentry/sentry-cocoa.git"; requirement = { kind = exactVersion; - version = 8.29.1; + version = 8.32.0; }; }; 7FD4822A2864D73300A5B18B /* XCRemoteSwiftPackageReference "OpenGraph" */ = { diff --git a/ios/Mattermost.xcworkspace/xcshareddata/swiftpm/Package.resolved b/ios/Mattermost.xcworkspace/xcshareddata/swiftpm/Package.resolved index 02524282363..3a5f05aea7f 100644 --- a/ios/Mattermost.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/ios/Mattermost.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -60,8 +60,8 @@ "repositoryURL": "https://github.com/getsentry/sentry-cocoa.git", "state": { "branch": null, - "revision": "08862789e1cbba7a9561bed69832a9306f339cd3", - "version": "8.29.1" + "revision": "5421f94cc859eb65f5ae3866165a053aa634431e", + "version": "8.32.0" } }, { diff --git a/ios/Mattermost/Info.plist b/ios/Mattermost/Info.plist index f5ceb9f032d..8c442e53930 100644 --- a/ios/Mattermost/Info.plist +++ b/ios/Mattermost/Info.plist @@ -21,7 +21,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 2.18.0 + 2.20.0 CFBundleSignature ???? CFBundleURLTypes @@ -37,7 +37,7 @@ CFBundleVersion - 532 + 555 ITSAppUsesNonExemptEncryption LSRequiresIPhoneOS @@ -65,10 +65,6 @@ Allowing access to your camera enables you to take photos or videos and attach them to messages. NSFaceIDUsageDescription Enabling access to your Face ID means we can restrict unauthorized users from accessing $(PRODUCT_NAME) on your device. - NSLocationAlwaysUsageDescription - Enabling access to your location data means we can add location metadata to pictures and videos you share in $(PRODUCT_NAME). - NSLocationWhenInUseUsageDescription - Enabling access to your location data means we can add location metadata to pictures and videos you share in $(PRODUCT_NAME). NSMicrophoneUsageDescription Enabling access to your device's microphones means you can capture audio for calls or videos to share in $(PRODUCT_NAME). NSPhotoLibraryAddUsageDescription diff --git a/ios/MattermostShare/Info.plist b/ios/MattermostShare/Info.plist index 288f25fdf93..8a446b5e70b 100644 --- a/ios/MattermostShare/Info.plist +++ b/ios/MattermostShare/Info.plist @@ -19,9 +19,9 @@ CFBundlePackageType XPC! CFBundleShortVersionString - 2.18.0 + 2.20.0 CFBundleVersion - 532 + 555 UIAppFonts OpenSans-Bold.ttf diff --git a/ios/NotificationService/Info.plist b/ios/NotificationService/Info.plist index 5bb26020114..6f267a34917 100644 --- a/ios/NotificationService/Info.plist +++ b/ios/NotificationService/Info.plist @@ -19,9 +19,9 @@ CFBundlePackageType XPC! CFBundleShortVersionString - 2.18.0 + 2.20.0 CFBundleVersion - 532 + 555 NSExtension NSExtensionPointIdentifier diff --git a/ios/Podfile.lock b/ios/Podfile.lock index e564e24623f..2cdf147eb9e 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -9,7 +9,7 @@ PODS: - ExpoModulesCore - EXConstants (16.0.2): - ExpoModulesCore - - Expo (51.0.14): + - Expo (51.0.24): - ExpoModulesCore - ExpoCrypto (13.0.2): - ExpoModulesCore @@ -17,16 +17,14 @@ PODS: - ExpoModulesCore - ExpoFileSystem (17.0.1): - ExpoModulesCore - - ExpoImage (1.12.12): + - ExpoImage (1.12.13): - ExpoModulesCore - - libavif/libdav1d - SDWebImage (~> 5.19.1) - - SDWebImageAVIFCoder (~> 0.11.0) - SDWebImageSVGCoder (~> 1.7.0) - SDWebImageWebPCoder (~> 0.14.6) - ExpoLinearGradient (13.0.2): - ExpoModulesCore - - ExpoModulesCore (1.12.15): + - ExpoModulesCore (1.12.20): - DoubleConversion - glog - hermes-engine @@ -55,19 +53,14 @@ PODS: - ExpoModulesCore - ExpoWebBrowser (13.0.3): - ExpoModulesCore - - FBLazyVector (0.74.2) + - FBLazyVector (0.74.5) - fmt (9.1.0) - glog (0.3.5) - - hermes-engine (0.74.2): - - hermes-engine/Pre-built (= 0.74.2) - - hermes-engine/Pre-built (0.74.2) + - hermes-engine (0.74.5): + - hermes-engine/Pre-built (= 0.74.5) + - hermes-engine/Pre-built (0.74.5) - HMSegmentedControl (1.5.6) - JitsiWebRTC (124.0.0) - - libavif/core (0.11.1) - - libavif/libdav1d (0.11.1): - - libavif/core - - libdav1d (>= 0.6.0) - - libdav1d (1.2.0) - libwebp (1.3.2): - libwebp/demux (= 1.3.2) - libwebp/mux (= 1.3.2) @@ -181,27 +174,27 @@ PODS: - DoubleConversion - fmt (= 9.1.0) - glog - - RCTDeprecation (0.74.2) - - RCTRequired (0.74.2) - - RCTTypeSafety (0.74.2): - - FBLazyVector (= 0.74.2) - - RCTRequired (= 0.74.2) - - React-Core (= 0.74.2) - - React (0.74.2): - - React-Core (= 0.74.2) - - React-Core/DevSupport (= 0.74.2) - - React-Core/RCTWebSocket (= 0.74.2) - - React-RCTActionSheet (= 0.74.2) - - React-RCTAnimation (= 0.74.2) - - React-RCTBlob (= 0.74.2) - - React-RCTImage (= 0.74.2) - - React-RCTLinking (= 0.74.2) - - React-RCTNetwork (= 0.74.2) - - React-RCTSettings (= 0.74.2) - - React-RCTText (= 0.74.2) - - React-RCTVibration (= 0.74.2) - - React-callinvoker (0.74.2) - - React-Codegen (0.74.2): + - RCTDeprecation (0.74.5) + - RCTRequired (0.74.5) + - RCTTypeSafety (0.74.5): + - FBLazyVector (= 0.74.5) + - RCTRequired (= 0.74.5) + - React-Core (= 0.74.5) + - React (0.74.5): + - React-Core (= 0.74.5) + - React-Core/DevSupport (= 0.74.5) + - React-Core/RCTWebSocket (= 0.74.5) + - React-RCTActionSheet (= 0.74.5) + - React-RCTAnimation (= 0.74.5) + - React-RCTBlob (= 0.74.5) + - React-RCTImage (= 0.74.5) + - React-RCTLinking (= 0.74.5) + - React-RCTNetwork (= 0.74.5) + - React-RCTSettings (= 0.74.5) + - React-RCTText (= 0.74.5) + - React-RCTVibration (= 0.74.5) + - React-callinvoker (0.74.5) + - React-Codegen (0.74.5): - DoubleConversion - glog - hermes-engine @@ -221,12 +214,12 @@ PODS: - React-utils - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - - React-Core (0.74.2): + - React-Core (0.74.5): - glog - hermes-engine - RCT-Folly (= 2024.01.01.00) - RCTDeprecation - - React-Core/Default (= 0.74.2) + - React-Core/Default (= 0.74.5) - React-cxxreact - React-featureflags - React-hermes @@ -238,7 +231,7 @@ PODS: - React-utils - SocketRocket (= 0.7.0) - Yoga - - React-Core/CoreModulesHeaders (0.74.2): + - React-Core/CoreModulesHeaders (0.74.5): - glog - hermes-engine - RCT-Folly (= 2024.01.01.00) @@ -255,7 +248,7 @@ PODS: - React-utils - SocketRocket (= 0.7.0) - Yoga - - React-Core/Default (0.74.2): + - React-Core/Default (0.74.5): - glog - hermes-engine - RCT-Folly (= 2024.01.01.00) @@ -271,13 +264,13 @@ PODS: - React-utils - SocketRocket (= 0.7.0) - Yoga - - React-Core/DevSupport (0.74.2): + - React-Core/DevSupport (0.74.5): - glog - hermes-engine - RCT-Folly (= 2024.01.01.00) - RCTDeprecation - - React-Core/Default (= 0.74.2) - - React-Core/RCTWebSocket (= 0.74.2) + - React-Core/Default (= 0.74.5) + - React-Core/RCTWebSocket (= 0.74.5) - React-cxxreact - React-featureflags - React-hermes @@ -289,7 +282,7 @@ PODS: - React-utils - SocketRocket (= 0.7.0) - Yoga - - React-Core/RCTActionSheetHeaders (0.74.2): + - React-Core/RCTActionSheetHeaders (0.74.5): - glog - hermes-engine - RCT-Folly (= 2024.01.01.00) @@ -306,7 +299,7 @@ PODS: - React-utils - SocketRocket (= 0.7.0) - Yoga - - React-Core/RCTAnimationHeaders (0.74.2): + - React-Core/RCTAnimationHeaders (0.74.5): - glog - hermes-engine - RCT-Folly (= 2024.01.01.00) @@ -323,7 +316,7 @@ PODS: - React-utils - SocketRocket (= 0.7.0) - Yoga - - React-Core/RCTBlobHeaders (0.74.2): + - React-Core/RCTBlobHeaders (0.74.5): - glog - hermes-engine - RCT-Folly (= 2024.01.01.00) @@ -340,7 +333,7 @@ PODS: - React-utils - SocketRocket (= 0.7.0) - Yoga - - React-Core/RCTImageHeaders (0.74.2): + - React-Core/RCTImageHeaders (0.74.5): - glog - hermes-engine - RCT-Folly (= 2024.01.01.00) @@ -357,7 +350,7 @@ PODS: - React-utils - SocketRocket (= 0.7.0) - Yoga - - React-Core/RCTLinkingHeaders (0.74.2): + - React-Core/RCTLinkingHeaders (0.74.5): - glog - hermes-engine - RCT-Folly (= 2024.01.01.00) @@ -374,7 +367,7 @@ PODS: - React-utils - SocketRocket (= 0.7.0) - Yoga - - React-Core/RCTNetworkHeaders (0.74.2): + - React-Core/RCTNetworkHeaders (0.74.5): - glog - hermes-engine - RCT-Folly (= 2024.01.01.00) @@ -391,7 +384,7 @@ PODS: - React-utils - SocketRocket (= 0.7.0) - Yoga - - React-Core/RCTSettingsHeaders (0.74.2): + - React-Core/RCTSettingsHeaders (0.74.5): - glog - hermes-engine - RCT-Folly (= 2024.01.01.00) @@ -408,7 +401,7 @@ PODS: - React-utils - SocketRocket (= 0.7.0) - Yoga - - React-Core/RCTTextHeaders (0.74.2): + - React-Core/RCTTextHeaders (0.74.5): - glog - hermes-engine - RCT-Folly (= 2024.01.01.00) @@ -425,7 +418,7 @@ PODS: - React-utils - SocketRocket (= 0.7.0) - Yoga - - React-Core/RCTVibrationHeaders (0.74.2): + - React-Core/RCTVibrationHeaders (0.74.5): - glog - hermes-engine - RCT-Folly (= 2024.01.01.00) @@ -442,12 +435,12 @@ PODS: - React-utils - SocketRocket (= 0.7.0) - Yoga - - React-Core/RCTWebSocket (0.74.2): + - React-Core/RCTWebSocket (0.74.5): - glog - hermes-engine - RCT-Folly (= 2024.01.01.00) - RCTDeprecation - - React-Core/Default (= 0.74.2) + - React-Core/Default (= 0.74.5) - React-cxxreact - React-featureflags - React-hermes @@ -459,36 +452,36 @@ PODS: - React-utils - SocketRocket (= 0.7.0) - Yoga - - React-CoreModules (0.74.2): + - React-CoreModules (0.74.5): - DoubleConversion - fmt (= 9.1.0) - RCT-Folly (= 2024.01.01.00) - - RCTTypeSafety (= 0.74.2) + - RCTTypeSafety (= 0.74.5) - React-Codegen - - React-Core/CoreModulesHeaders (= 0.74.2) - - React-jsi (= 0.74.2) + - React-Core/CoreModulesHeaders (= 0.74.5) + - React-jsi (= 0.74.5) - React-jsinspector - React-NativeModulesApple - React-RCTBlob - - React-RCTImage (= 0.74.2) + - React-RCTImage (= 0.74.5) - ReactCommon - SocketRocket (= 0.7.0) - - React-cxxreact (0.74.2): + - React-cxxreact (0.74.5): - boost (= 1.83.0) - DoubleConversion - fmt (= 9.1.0) - glog - hermes-engine - RCT-Folly (= 2024.01.01.00) - - React-callinvoker (= 0.74.2) - - React-debug (= 0.74.2) - - React-jsi (= 0.74.2) + - React-callinvoker (= 0.74.5) + - React-debug (= 0.74.5) + - React-jsi (= 0.74.5) - React-jsinspector - - React-logger (= 0.74.2) - - React-perflogger (= 0.74.2) - - React-runtimeexecutor (= 0.74.2) - - React-debug (0.74.2) - - React-Fabric (0.74.2): + - React-logger (= 0.74.5) + - React-perflogger (= 0.74.5) + - React-runtimeexecutor (= 0.74.5) + - React-debug (0.74.5) + - React-Fabric (0.74.5): - DoubleConversion - fmt (= 9.1.0) - glog @@ -499,20 +492,20 @@ PODS: - React-Core - React-cxxreact - React-debug - - React-Fabric/animations (= 0.74.2) - - React-Fabric/attributedstring (= 0.74.2) - - React-Fabric/componentregistry (= 0.74.2) - - React-Fabric/componentregistrynative (= 0.74.2) - - React-Fabric/components (= 0.74.2) - - React-Fabric/core (= 0.74.2) - - React-Fabric/imagemanager (= 0.74.2) - - React-Fabric/leakchecker (= 0.74.2) - - React-Fabric/mounting (= 0.74.2) - - React-Fabric/scheduler (= 0.74.2) - - React-Fabric/telemetry (= 0.74.2) - - React-Fabric/templateprocessor (= 0.74.2) - - React-Fabric/textlayoutmanager (= 0.74.2) - - React-Fabric/uimanager (= 0.74.2) + - React-Fabric/animations (= 0.74.5) + - React-Fabric/attributedstring (= 0.74.5) + - React-Fabric/componentregistry (= 0.74.5) + - React-Fabric/componentregistrynative (= 0.74.5) + - React-Fabric/components (= 0.74.5) + - React-Fabric/core (= 0.74.5) + - React-Fabric/imagemanager (= 0.74.5) + - React-Fabric/leakchecker (= 0.74.5) + - React-Fabric/mounting (= 0.74.5) + - React-Fabric/scheduler (= 0.74.5) + - React-Fabric/telemetry (= 0.74.5) + - React-Fabric/templateprocessor (= 0.74.5) + - React-Fabric/textlayoutmanager (= 0.74.5) + - React-Fabric/uimanager (= 0.74.5) - React-graphics - React-jsi - React-jsiexecutor @@ -521,7 +514,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/animations (0.74.2): + - React-Fabric/animations (0.74.5): - DoubleConversion - fmt (= 9.1.0) - glog @@ -540,7 +533,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/attributedstring (0.74.2): + - React-Fabric/attributedstring (0.74.5): - DoubleConversion - fmt (= 9.1.0) - glog @@ -559,7 +552,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/componentregistry (0.74.2): + - React-Fabric/componentregistry (0.74.5): - DoubleConversion - fmt (= 9.1.0) - glog @@ -578,7 +571,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/componentregistrynative (0.74.2): + - React-Fabric/componentregistrynative (0.74.5): - DoubleConversion - fmt (= 9.1.0) - glog @@ -597,7 +590,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/components (0.74.2): + - React-Fabric/components (0.74.5): - DoubleConversion - fmt (= 9.1.0) - glog @@ -608,17 +601,17 @@ PODS: - React-Core - React-cxxreact - React-debug - - React-Fabric/components/inputaccessory (= 0.74.2) - - React-Fabric/components/legacyviewmanagerinterop (= 0.74.2) - - React-Fabric/components/modal (= 0.74.2) - - React-Fabric/components/rncore (= 0.74.2) - - React-Fabric/components/root (= 0.74.2) - - React-Fabric/components/safeareaview (= 0.74.2) - - React-Fabric/components/scrollview (= 0.74.2) - - React-Fabric/components/text (= 0.74.2) - - React-Fabric/components/textinput (= 0.74.2) - - React-Fabric/components/unimplementedview (= 0.74.2) - - React-Fabric/components/view (= 0.74.2) + - React-Fabric/components/inputaccessory (= 0.74.5) + - React-Fabric/components/legacyviewmanagerinterop (= 0.74.5) + - React-Fabric/components/modal (= 0.74.5) + - React-Fabric/components/rncore (= 0.74.5) + - React-Fabric/components/root (= 0.74.5) + - React-Fabric/components/safeareaview (= 0.74.5) + - React-Fabric/components/scrollview (= 0.74.5) + - React-Fabric/components/text (= 0.74.5) + - React-Fabric/components/textinput (= 0.74.5) + - React-Fabric/components/unimplementedview (= 0.74.5) + - React-Fabric/components/view (= 0.74.5) - React-graphics - React-jsi - React-jsiexecutor @@ -627,7 +620,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/components/inputaccessory (0.74.2): + - React-Fabric/components/inputaccessory (0.74.5): - DoubleConversion - fmt (= 9.1.0) - glog @@ -646,7 +639,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/components/legacyviewmanagerinterop (0.74.2): + - React-Fabric/components/legacyviewmanagerinterop (0.74.5): - DoubleConversion - fmt (= 9.1.0) - glog @@ -665,7 +658,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/components/modal (0.74.2): + - React-Fabric/components/modal (0.74.5): - DoubleConversion - fmt (= 9.1.0) - glog @@ -684,7 +677,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/components/rncore (0.74.2): + - React-Fabric/components/rncore (0.74.5): - DoubleConversion - fmt (= 9.1.0) - glog @@ -703,7 +696,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/components/root (0.74.2): + - React-Fabric/components/root (0.74.5): - DoubleConversion - fmt (= 9.1.0) - glog @@ -722,7 +715,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/components/safeareaview (0.74.2): + - React-Fabric/components/safeareaview (0.74.5): - DoubleConversion - fmt (= 9.1.0) - glog @@ -741,7 +734,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/components/scrollview (0.74.2): + - React-Fabric/components/scrollview (0.74.5): - DoubleConversion - fmt (= 9.1.0) - glog @@ -760,7 +753,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/components/text (0.74.2): + - React-Fabric/components/text (0.74.5): - DoubleConversion - fmt (= 9.1.0) - glog @@ -779,7 +772,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/components/textinput (0.74.2): + - React-Fabric/components/textinput (0.74.5): - DoubleConversion - fmt (= 9.1.0) - glog @@ -798,7 +791,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/components/unimplementedview (0.74.2): + - React-Fabric/components/unimplementedview (0.74.5): - DoubleConversion - fmt (= 9.1.0) - glog @@ -817,7 +810,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/components/view (0.74.2): + - React-Fabric/components/view (0.74.5): - DoubleConversion - fmt (= 9.1.0) - glog @@ -837,7 +830,7 @@ PODS: - React-utils - ReactCommon/turbomodule/core - Yoga - - React-Fabric/core (0.74.2): + - React-Fabric/core (0.74.5): - DoubleConversion - fmt (= 9.1.0) - glog @@ -856,7 +849,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/imagemanager (0.74.2): + - React-Fabric/imagemanager (0.74.5): - DoubleConversion - fmt (= 9.1.0) - glog @@ -875,7 +868,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/leakchecker (0.74.2): + - React-Fabric/leakchecker (0.74.5): - DoubleConversion - fmt (= 9.1.0) - glog @@ -894,7 +887,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/mounting (0.74.2): + - React-Fabric/mounting (0.74.5): - DoubleConversion - fmt (= 9.1.0) - glog @@ -913,7 +906,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/scheduler (0.74.2): + - React-Fabric/scheduler (0.74.5): - DoubleConversion - fmt (= 9.1.0) - glog @@ -932,7 +925,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/telemetry (0.74.2): + - React-Fabric/telemetry (0.74.5): - DoubleConversion - fmt (= 9.1.0) - glog @@ -951,7 +944,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/templateprocessor (0.74.2): + - React-Fabric/templateprocessor (0.74.5): - DoubleConversion - fmt (= 9.1.0) - glog @@ -970,7 +963,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/textlayoutmanager (0.74.2): + - React-Fabric/textlayoutmanager (0.74.5): - DoubleConversion - fmt (= 9.1.0) - glog @@ -990,7 +983,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/uimanager (0.74.2): + - React-Fabric/uimanager (0.74.5): - DoubleConversion - fmt (= 9.1.0) - glog @@ -1009,45 +1002,45 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-FabricImage (0.74.2): + - React-FabricImage (0.74.5): - DoubleConversion - fmt (= 9.1.0) - glog - hermes-engine - RCT-Folly/Fabric (= 2024.01.01.00) - - RCTRequired (= 0.74.2) - - RCTTypeSafety (= 0.74.2) + - RCTRequired (= 0.74.5) + - RCTTypeSafety (= 0.74.5) - React-Fabric - React-graphics - React-ImageManager - React-jsi - - React-jsiexecutor (= 0.74.2) + - React-jsiexecutor (= 0.74.5) - React-logger - React-rendererdebug - React-utils - ReactCommon - Yoga - - React-featureflags (0.74.2) - - React-graphics (0.74.2): + - React-featureflags (0.74.5) + - React-graphics (0.74.5): - DoubleConversion - fmt (= 9.1.0) - glog - RCT-Folly/Fabric (= 2024.01.01.00) - - React-Core/Default (= 0.74.2) + - React-Core/Default (= 0.74.5) - React-utils - - React-hermes (0.74.2): + - React-hermes (0.74.5): - DoubleConversion - fmt (= 9.1.0) - glog - hermes-engine - RCT-Folly (= 2024.01.01.00) - - React-cxxreact (= 0.74.2) + - React-cxxreact (= 0.74.5) - React-jsi - - React-jsiexecutor (= 0.74.2) + - React-jsiexecutor (= 0.74.5) - React-jsinspector - - React-perflogger (= 0.74.2) + - React-perflogger (= 0.74.5) - React-runtimeexecutor - - React-ImageManager (0.74.2): + - React-ImageManager (0.74.5): - glog - RCT-Folly/Fabric - React-Core/Default @@ -1056,51 +1049,51 @@ PODS: - React-graphics - React-rendererdebug - React-utils - - React-jsc (0.74.2): - - React-jsc/Fabric (= 0.74.2) - - React-jsi (= 0.74.2) - - React-jsc/Fabric (0.74.2): - - React-jsi (= 0.74.2) - - React-jserrorhandler (0.74.2): + - React-jsc (0.74.5): + - React-jsc/Fabric (= 0.74.5) + - React-jsi (= 0.74.5) + - React-jsc/Fabric (0.74.5): + - React-jsi (= 0.74.5) + - React-jserrorhandler (0.74.5): - RCT-Folly/Fabric (= 2024.01.01.00) - React-debug - React-jsi - React-Mapbuffer - - React-jsi (0.74.2): + - React-jsi (0.74.5): - boost (= 1.83.0) - DoubleConversion - fmt (= 9.1.0) - glog - hermes-engine - RCT-Folly (= 2024.01.01.00) - - React-jsiexecutor (0.74.2): + - React-jsiexecutor (0.74.5): - DoubleConversion - fmt (= 9.1.0) - glog - hermes-engine - RCT-Folly (= 2024.01.01.00) - - React-cxxreact (= 0.74.2) - - React-jsi (= 0.74.2) + - React-cxxreact (= 0.74.5) + - React-jsi (= 0.74.5) - React-jsinspector - - React-perflogger (= 0.74.2) - - React-jsinspector (0.74.2): + - React-perflogger (= 0.74.5) + - React-jsinspector (0.74.5): - DoubleConversion - glog - hermes-engine - RCT-Folly (= 2024.01.01.00) - React-featureflags - React-jsi - - React-runtimeexecutor (= 0.74.2) - - React-jsitracing (0.74.2): + - React-runtimeexecutor (= 0.74.5) + - React-jsitracing (0.74.5): - React-jsi - - React-logger (0.74.2): + - React-logger (0.74.5): - glog - - React-Mapbuffer (0.74.2): + - React-Mapbuffer (0.74.5): - glog - React-debug - react-native-background-timer (2.4.1): - React-Core - - react-native-cameraroll (7.8.1): + - react-native-cameraroll (7.8.3): - DoubleConversion - glog - hermes-engine @@ -1169,7 +1162,7 @@ PODS: - Yoga - react-native-netinfo (11.3.2): - React-Core - - react-native-network-client (1.6.0): + - react-native-network-client (1.7.2): - Alamofire (~> 5.9.1) - DoubleConversion - glog @@ -1218,9 +1211,9 @@ PODS: - Yoga - react-native-performance (5.1.2): - React-Core - - react-native-safe-area-context (4.10.5): + - react-native-safe-area-context (4.10.8): - React-Core - - react-native-video (6.2.0): + - react-native-video (6.4.3): - DoubleConversion - glog - hermes-engine @@ -1234,7 +1227,7 @@ PODS: - React-featureflags - React-graphics - React-ImageManager - - react-native-video/Video (= 6.2.0) + - react-native-video/Video (= 6.4.3) - React-NativeModulesApple - React-RCTFabric - React-rendererdebug @@ -1242,7 +1235,7 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga - - react-native-video/Video (6.2.0): + - react-native-video/Video (6.4.3): - DoubleConversion - glog - hermes-engine @@ -1263,11 +1256,11 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga - - react-native-webrtc (124.0.1): + - react-native-webrtc (124.0.3): - JitsiWebRTC (~> 124.0.0) - React-Core - - React-nativeconfig (0.74.2) - - React-NativeModulesApple (0.74.2): + - React-nativeconfig (0.74.5) + - React-NativeModulesApple (0.74.5): - glog - hermes-engine - React-callinvoker @@ -1278,10 +1271,10 @@ PODS: - React-runtimeexecutor - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - - React-perflogger (0.74.2) - - React-RCTActionSheet (0.74.2): - - React-Core/RCTActionSheetHeaders (= 0.74.2) - - React-RCTAnimation (0.74.2): + - React-perflogger (0.74.5) + - React-RCTActionSheet (0.74.5): + - React-Core/RCTActionSheetHeaders (= 0.74.5) + - React-RCTAnimation (0.74.5): - RCT-Folly (= 2024.01.01.00) - RCTTypeSafety - React-Codegen @@ -1289,7 +1282,7 @@ PODS: - React-jsi - React-NativeModulesApple - ReactCommon - - React-RCTAppDelegate (0.74.2): + - React-RCTAppDelegate (0.74.5): - RCT-Folly (= 2024.01.01.00) - RCTRequired - RCTTypeSafety @@ -1313,7 +1306,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon - - React-RCTBlob (0.74.2): + - React-RCTBlob (0.74.5): - DoubleConversion - fmt (= 9.1.0) - hermes-engine @@ -1326,7 +1319,7 @@ PODS: - React-NativeModulesApple - React-RCTNetwork - ReactCommon - - React-RCTFabric (0.74.2): + - React-RCTFabric (0.74.5): - glog - hermes-engine - RCT-Folly/Fabric (= 2024.01.01.00) @@ -1346,7 +1339,7 @@ PODS: - React-runtimescheduler - React-utils - Yoga - - React-RCTImage (0.74.2): + - React-RCTImage (0.74.5): - RCT-Folly (= 2024.01.01.00) - RCTTypeSafety - React-Codegen @@ -1355,14 +1348,14 @@ PODS: - React-NativeModulesApple - React-RCTNetwork - ReactCommon - - React-RCTLinking (0.74.2): + - React-RCTLinking (0.74.5): - React-Codegen - - React-Core/RCTLinkingHeaders (= 0.74.2) - - React-jsi (= 0.74.2) + - React-Core/RCTLinkingHeaders (= 0.74.5) + - React-jsi (= 0.74.5) - React-NativeModulesApple - ReactCommon - - ReactCommon/turbomodule/core (= 0.74.2) - - React-RCTNetwork (0.74.2): + - ReactCommon/turbomodule/core (= 0.74.5) + - React-RCTNetwork (0.74.5): - RCT-Folly (= 2024.01.01.00) - RCTTypeSafety - React-Codegen @@ -1370,7 +1363,7 @@ PODS: - React-jsi - React-NativeModulesApple - ReactCommon - - React-RCTSettings (0.74.2): + - React-RCTSettings (0.74.5): - RCT-Folly (= 2024.01.01.00) - RCTTypeSafety - React-Codegen @@ -1378,23 +1371,23 @@ PODS: - React-jsi - React-NativeModulesApple - ReactCommon - - React-RCTText (0.74.2): - - React-Core/RCTTextHeaders (= 0.74.2) + - React-RCTText (0.74.5): + - React-Core/RCTTextHeaders (= 0.74.5) - Yoga - - React-RCTVibration (0.74.2): + - React-RCTVibration (0.74.5): - RCT-Folly (= 2024.01.01.00) - React-Codegen - React-Core/RCTVibrationHeaders - React-jsi - React-NativeModulesApple - ReactCommon - - React-rendererdebug (0.74.2): + - React-rendererdebug (0.74.5): - DoubleConversion - fmt (= 9.1.0) - RCT-Folly (= 2024.01.01.00) - React-debug - - React-rncore (0.74.2) - - React-RuntimeApple (0.74.2): + - React-rncore (0.74.5) + - React-RuntimeApple (0.74.5): - hermes-engine - RCT-Folly/Fabric (= 2024.01.01.00) - React-callinvoker @@ -1412,7 +1405,7 @@ PODS: - React-runtimeexecutor - React-RuntimeHermes - React-utils - - React-RuntimeCore (0.74.2): + - React-RuntimeCore (0.74.5): - glog - hermes-engine - RCT-Folly/Fabric (= 2024.01.01.00) @@ -1425,9 +1418,9 @@ PODS: - React-runtimeexecutor - React-runtimescheduler - React-utils - - React-runtimeexecutor (0.74.2): - - React-jsi (= 0.74.2) - - React-RuntimeHermes (0.74.2): + - React-runtimeexecutor (0.74.5): + - React-jsi (= 0.74.5) + - React-RuntimeHermes (0.74.5): - hermes-engine - RCT-Folly/Fabric (= 2024.01.01.00) - React-featureflags @@ -1438,7 +1431,7 @@ PODS: - React-nativeconfig - React-RuntimeCore - React-utils - - React-runtimescheduler (0.74.2): + - React-runtimescheduler (0.74.5): - glog - hermes-engine - RCT-Folly (= 2024.01.01.00) @@ -1450,63 +1443,63 @@ PODS: - React-rendererdebug - React-runtimeexecutor - React-utils - - React-utils (0.74.2): + - React-utils (0.74.5): - glog - hermes-engine - RCT-Folly (= 2024.01.01.00) - React-debug - - React-jsi (= 0.74.2) - - ReactCommon (0.74.2): - - ReactCommon/turbomodule (= 0.74.2) - - ReactCommon/turbomodule (0.74.2): + - React-jsi (= 0.74.5) + - ReactCommon (0.74.5): + - ReactCommon/turbomodule (= 0.74.5) + - ReactCommon/turbomodule (0.74.5): - DoubleConversion - fmt (= 9.1.0) - glog - hermes-engine - RCT-Folly (= 2024.01.01.00) - - React-callinvoker (= 0.74.2) - - React-cxxreact (= 0.74.2) - - React-jsi (= 0.74.2) - - React-logger (= 0.74.2) - - React-perflogger (= 0.74.2) - - ReactCommon/turbomodule/bridging (= 0.74.2) - - ReactCommon/turbomodule/core (= 0.74.2) - - ReactCommon/turbomodule/bridging (0.74.2): + - React-callinvoker (= 0.74.5) + - React-cxxreact (= 0.74.5) + - React-jsi (= 0.74.5) + - React-logger (= 0.74.5) + - React-perflogger (= 0.74.5) + - ReactCommon/turbomodule/bridging (= 0.74.5) + - ReactCommon/turbomodule/core (= 0.74.5) + - ReactCommon/turbomodule/bridging (0.74.5): - DoubleConversion - fmt (= 9.1.0) - glog - hermes-engine - RCT-Folly (= 2024.01.01.00) - - React-callinvoker (= 0.74.2) - - React-cxxreact (= 0.74.2) - - React-jsi (= 0.74.2) - - React-logger (= 0.74.2) - - React-perflogger (= 0.74.2) - - ReactCommon/turbomodule/core (0.74.2): + - React-callinvoker (= 0.74.5) + - React-cxxreact (= 0.74.5) + - React-jsi (= 0.74.5) + - React-logger (= 0.74.5) + - React-perflogger (= 0.74.5) + - ReactCommon/turbomodule/core (0.74.5): - DoubleConversion - fmt (= 9.1.0) - glog - hermes-engine - RCT-Folly (= 2024.01.01.00) - - React-callinvoker (= 0.74.2) - - React-cxxreact (= 0.74.2) - - React-debug (= 0.74.2) - - React-jsi (= 0.74.2) - - React-logger (= 0.74.2) - - React-perflogger (= 0.74.2) - - React-utils (= 0.74.2) + - React-callinvoker (= 0.74.5) + - React-cxxreact (= 0.74.5) + - React-debug (= 0.74.5) + - React-jsi (= 0.74.5) + - React-logger (= 0.74.5) + - React-perflogger (= 0.74.5) + - React-utils (= 0.74.5) - ReactNativeExceptionHandler (2.10.10): - React-Core - ReactNativeIncallManager (4.2.0): - React-Core - - ReactNativeNavigation (7.40.0): + - ReactNativeNavigation (7.40.1): - HMSegmentedControl - React-Core - React-CoreModules - React-RCTImage - React-RCTText - - ReactNativeNavigation/Core (= 7.40.0) - - ReactNativeNavigation/Core (7.40.0): + - ReactNativeNavigation/Core (= 7.40.1) + - ReactNativeNavigation/Core (7.40.1): - HMSegmentedControl - React-Core - React-CoreModules @@ -1514,11 +1507,11 @@ PODS: - React-RCTText - RNCClipboard (1.14.1): - React-Core - - RNDateTimePicker (8.1.1): + - RNDateTimePicker (8.2.0): - React-Core - RNFileViewer (2.1.5): - React-Core - - RNGestureHandler (2.16.2): + - RNGestureHandler (2.18.1): - DoubleConversion - glog - hermes-engine @@ -1541,13 +1534,13 @@ PODS: - Yoga - RNKeychain (8.2.0): - React-Core - - RNLocalize (3.2.0): + - RNLocalize (3.2.1): - React-Core - RNPermissions (4.1.5): - React-Core - RNReactNativeHapticFeedback (2.2.0): - React-Core - - RNReanimated (3.12.0): + - RNReanimated (3.14.0): - DoubleConversion - glog - hermes-engine @@ -1568,7 +1561,7 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga - - RNScreens (3.32.0): + - RNScreens (3.34.0): - DoubleConversion - glog - hermes-engine @@ -1590,14 +1583,14 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga - - RNSentry (5.24.1): + - RNSentry (5.27.0): - hermes-engine - React-Core - React-hermes - - Sentry/HybridSDK (= 8.29.1) + - Sentry/HybridSDK (= 8.32.0) - RNShare (10.2.1): - React-Core - - RNSVG (15.3.0): + - RNSVG (15.4.0): - React-Core - RNVectorIcons (10.1.0): - DoubleConversion @@ -1620,18 +1613,15 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga - - SDWebImage (5.19.2): - - SDWebImage/Core (= 5.19.2) - - SDWebImage/Core (5.19.2) - - SDWebImageAVIFCoder (0.11.0): - - libavif/core (>= 0.11.0) - - SDWebImage (~> 5.10) + - SDWebImage (5.19.4): + - SDWebImage/Core (= 5.19.4) + - SDWebImage/Core (5.19.4) - SDWebImageSVGCoder (1.7.0): - SDWebImage/Core (~> 5.6) - SDWebImageWebPCoder (0.14.6): - libwebp (~> 1.0) - SDWebImage/Core (~> 5.17) - - Sentry/HybridSDK (8.29.1) + - Sentry/HybridSDK (8.32.0) - simdjson (3.1.0-wmelon1) - SocketRocket (0.7.0) - Starscream (4.0.8) @@ -1755,11 +1745,8 @@ SPEC REPOS: - CocoaLumberjack - HMSegmentedControl - JitsiWebRTC - - libavif - - libdav1d - libwebp - SDWebImage - - SDWebImageAVIFCoder - SDWebImageSVGCoder - SDWebImageWebPCoder - Sentry @@ -1804,7 +1791,7 @@ EXTERNAL SOURCES: :podspec: "../node_modules/react-native/third-party-podspecs/glog.podspec" hermes-engine: :podspec: "../node_modules/react-native/sdks/hermes-engine/hermes-engine.podspec" - :tag: hermes-2024-06-03-RNv0.74.2-bb1e74fe1e95c2b5a2f4f9311152da052badc2bc + :tag: hermes-2024-06-28-RNv0.74.3-7bda0c267e76d11b68a585f84cfdd65000babf85 mattermost-hardware-keyboard: :path: "../node_modules/@mattermost/hardware-keyboard" mattermost-keyboard-tracker: @@ -1985,119 +1972,116 @@ SPEC CHECKSUMS: DoubleConversion: 76ab83afb40bddeeee456813d9c04f67f78771b5 EXApplication: c08200c34daca7af7fd76ac4b9d606077410e8ad EXConstants: 409690fbfd5afea964e5e9d6c4eb2c2b59222c59 - Expo: 9c87c876b45a6934894ba5e9353ee94fdba48125 + Expo: 798848eae1daf13363d69790986146b08d0cf92f ExpoCrypto: 156078f266bf28f80ecf5e2a9c3a0d6ffce07a1c ExpoDevice: fc94f0e42ecdfd897e7590f2874fc64dfa7e9b1c ExpoFileSystem: 80bfe850b1f9922c16905822ecbf97acd711dc51 - ExpoImage: 2ccccff1219ebc765e344f3338f2430af2df4824 + ExpoImage: 5d64eab48726241df25489503999e0312d602c8e ExpoLinearGradient: 8cec4a09426d8934c433e83cb36262d72c667fce - ExpoModulesCore: 54c85b57000a2a82e5212d5e42cd7a36524be4ac + ExpoModulesCore: 5440e96a8ee014f4fd88e77264985fd0a65f5f8c ExpoStoreReview: 15f9a636b62ff00bb21cbe9a9fe22f0239da4481 ExpoVideoThumbnails: 40c737b2507a7470b14e9452e286513beb9a7d77 ExpoWebBrowser: 7595ccac6938eb65b076385fd23d035db9ecdc8e - FBLazyVector: 4bc164e5b5e6cfc288d2b5ff28643ea15fa1a589 + FBLazyVector: ac12dc084d1c8ec4cc4d7b3cf1b0ebda6dab85af fmt: 4c2741a687cc09f0634a2e2c72a838b99f1ff120 glog: fdfdfe5479092de0c4bdbebedd9056951f092c4f - hermes-engine: 01d3e052018c2a13937aca1860fbedbccd4a41b7 + hermes-engine: 8c1577f3fdb849cbe7729c2e7b5abc4b845e88f8 HMSegmentedControl: 34c1f54d822d8308e7b24f5d901ec674dfa31352 JitsiWebRTC: 37fb2fb70d42cac58c06948527a5f9e1b3f50812 - libavif: 84bbb62fb232c3018d6f1bab79beea87e35de7b7 - libdav1d: 23581a4d8ec811ff171ed5e2e05cd27bad64c39f libwebp: 1786c9f4ff8a279e4dac1e8f385004d5fc253009 mattermost-hardware-keyboard: b916a168bb3add779bb226b3a62d6ac5ec225a79 mattermost-keyboard-tracker: b48752a97e731fdae5e8259a4ea0e731e1a05094 mattermost-react-native-turbo-log: c446c7e8a39c131d6bafa6ae6f830c735030ed5e mattermost-rnutils: 01504fb26beb75594f4c6e2150668b27783112f5 RCT-Folly: 02617c592a293bd6d418e0a88ff4ee1f88329b47 - RCTDeprecation: b03c35057846b685b3ccadc9bfe43e349989cdb2 - RCTRequired: 194626909cfa8d39ca6663138c417bc6c431648c - RCTTypeSafety: 552aff5b8e8341660594db00e53ac889682bc120 - React: a57fe42044fe6ed3e828f8867ce070a6c5872754 - React-callinvoker: 6bedefb354a8848b534752417954caa3a5cf34f9 - React-Codegen: 0952549a095f8f8cb2fb5def1733b6b232769b1c - React-Core: 289ee3dfc1639bb9058c1e77427bb48169c26d7a - React-CoreModules: eda5ce541a1f552158317abd90a5a0f6a4f8d6f7 - React-cxxreact: 56bd17ccc6d4248116f7f95884ddb8c528379fb6 - React-debug: 164b8e302404d92d4bec39778a5e03bcb1b6eb08 - React-Fabric: 05620c36074e3ab397dd8f9db0deb6d3c38b4efa - React-FabricImage: 2a8a7f5729f5c44e32e6f58f7225ee1017ed0704 - React-featureflags: d97a6393993052e951e16a3b81206e22110be8d2 - React-graphics: ef07d701f4eb72ae6fca6ed0a7260a04f2a58dec - React-hermes: 6ccc301ababfa17a9aad25a7e33faf325fd024b4 - React-ImageManager: 00404bfe122626bc6493621f2a31ce802115a9b3 - React-jsc: e70a1bb9f4336280fc7b225a01f3d43435713d4f - React-jserrorhandler: 5e2632590a84363855b2083e6b3d501e93bc3f04 - React-jsi: 828703c235f4eea1647897ee8030efdc6e8e9f14 - React-jsiexecutor: 713d7bbef0a410cee5b3b78f73ed1fc16e177ba7 - React-jsinspector: e1fa5325a47f34645195c63e3312ddb6a2efef5d - React-jsitracing: 0fa7f78d8fdda794667cb2e6f19c874c1cf31d7e - React-logger: 29fa3e048f5f67fe396bc08af7606426d9bd7b5d - React-Mapbuffer: bf56147c9775491e53122a94c423ac201417e326 + RCTDeprecation: 3afceddffa65aee666dafd6f0116f1d975db1584 + RCTRequired: ec1239bc9d8bf63e10fb92bd8b26171a9258e0c1 + RCTTypeSafety: f5ecbc86c5c5fa163c05acb7a1c5012e15b5f994 + React: fc9fa7258eff606f44d58c5b233a82dc9cf09018 + React-callinvoker: e3fab14d69607fb7e8e3a57e5a415aed863d3599 + React-Codegen: 6fa87b7c6b8efcd0cef4bfeaec8c8bc8a6abe75a + React-Core: 3a5fd9e781cecf87803e5b091496a606a3df774a + React-CoreModules: cbf4707dafab8f9f826ac0c63a07d0bf5d01e256 + React-cxxreact: 7b188556271e3c7fdf22a04819f6a6225045b9dd + React-debug: d30893c49ae1bce4037ea5cd8bb2511d2a38d057 + React-Fabric: 826729dd2304fda9b89ff0a579f60ba2a470bc26 + React-FabricImage: 2ad1fb8ffa5778eda9ed204a7b3cdd70bc333ce7 + React-featureflags: 4ae83e72d9a92452793601ac9ac7d2280e486089 + React-graphics: 61a026e1c1e7e20d20ac9fec6f6de631732b233d + React-hermes: a7054fbcbda3957e3c5eaad06ef9bf79998d535a + React-ImageManager: 2bbd6eb2e696bc680f76f84563e4b87d241614e1 + React-jsc: 3ec68d4dd44b21345144a18c84204495e6232db5 + React-jserrorhandler: 56fa04d49bfbe54ddfece7916673a73ebfea286b + React-jsi: f3ce1dd2e950b6ad12b65ea3ef89168f1b94c584 + React-jsiexecutor: b4df3a27973d82f9abf3c4bd0f88e042cda25f16 + React-jsinspector: 97ea746c023687de7313ee289817d6991d596c7d + React-jsitracing: 3b6060bbf5317663667e1dd93560c7943ab86ccc + React-logger: 257858bd55f3a4e1bc0cf07ddc8fb9faba6f8c7c + React-Mapbuffer: 6c1cacdbf40b531f549eba249e531a7d0bfd8e7f react-native-background-timer: 17ea5e06803401a379ebf1f20505b793ac44d0fe - react-native-cameraroll: a9138c165c9975da773d26945591d313992c799b + react-native-cameraroll: 4f313ab09aeaf42ac5f72dec840641da7656f104 react-native-cookies: f54fcded06bb0cda05c11d86788020b43528a26c react-native-document-picker: 5b97e24a7f1a1e4a50a72c540a043f32d29a70a2 react-native-emm: ecab44d78fb1cc7d7b7901914f48fb6309c46ff2 react-native-image-picker: c3afe5472ef870d98a4b28415fc0b928161ee5f7 react-native-netinfo: 076df4f9b07f6670acf4ce9a75aac8d34c2e2ccc - react-native-network-client: a862d041763721fddb9802a24fb6bb28208781cb + react-native-network-client: 5173c47230b5f497cdef469cba8e6e1b3df687eb react-native-notifications: 4601a5a8db4ced6ae7cfc43b44d35fe437ac50c4 react-native-paste-input: 011a9916ef3acf809a7da122847c61ca0f93a60e react-native-performance: ff93f8af3b2ee9519fd7879896aa9b8b8272691d - react-native-safe-area-context: a240ad4b683349e48b1d51fed1611138d1bdad97 - react-native-video: 8cd68fa00f56087184154c0e976d3756adaad144 - react-native-webrtc: 8b9b90ab7ad5b77811a22a3c3ac1fe2a64737e29 - React-nativeconfig: 9f223cd321823afdecf59ed00861ab2d69ee0fc1 - React-NativeModulesApple: ff7efaff7098639db5631236cfd91d60abff04c0 - React-perflogger: 32ed45d9cee02cf6639acae34251590dccd30994 - React-RCTActionSheet: 19f967ddaea258182b56ef11437133b056ba2adf - React-RCTAnimation: d7f4137fc44a08bba465267ea7cb1dbdb7c4ec87 - React-RCTAppDelegate: 2b3f4d8009796af209a0d496e73276b743acee08 - React-RCTBlob: c6c3e1e0251700b7bea036b893913f22e2b9cb47 - React-RCTFabric: 93a3ea55169d19294f07092013c1c9ea7a015c9b - React-RCTImage: 40528ab74a4fef0f0e2ee797a074b26d120b6cc6 - React-RCTLinking: 385b5beb96749aae9ae1606746e883e1c9f8a6a7 - React-RCTNetwork: ffc9f05bd8fa5b3bce562199ba41235ad0af645c - React-RCTSettings: 21914178bb65cb2c20c655ae1fb401617ae74618 - React-RCTText: 7f8dba1a311e99f4de15bbace2350e805f33f024 - React-RCTVibration: e4ccf673579d0d94a96b3a0b64492db08f8324d5 - React-rendererdebug: ac70f40de137ce7bdbc55eaee60c467a215d9923 - React-rncore: edfff7a3f7f82ca1e0ba26978c6d84c7a8970dac - React-RuntimeApple: a0c98b75571aa5f44ddc7c6e9fd55803fa4db00f - React-RuntimeCore: 4b8db1fe2f3f4a3a5ecb22e1a419824e3e2cd7ef - React-runtimeexecutor: 5961acc7a77b69f964e1645a5d6069e124ce6b37 - React-RuntimeHermes: c5825bfae4815fdf4e9e639340c3a986a491884c - React-runtimescheduler: 56b642bf605ba5afa500d35790928fc1d51565ad - React-utils: 4476b7fcbbd95cfd002f3e778616155241d86e31 - ReactCommon: ecad995f26e0d1e24061f60f4e5d74782f003f12 + react-native-safe-area-context: b7daa1a8df36095a032dff095a1ea8963cb48371 + react-native-video: b3ba8f424c8c3f54dd9289d47bbe60fbc09bc986 + react-native-webrtc: 41f6602490c618e3ec0e2108e46b84e2c731c127 + React-nativeconfig: ba9a2e54e2f0882cf7882698825052793ed4c851 + React-NativeModulesApple: 8d11ff8955181540585c944cf48e9e7236952697 + React-perflogger: ed4e0c65781521e0424f2e5e40b40cc7879d737e + React-RCTActionSheet: 49d53ff03bb5688ca4606c55859053a0cd129ea5 + React-RCTAnimation: 07b4923885c52c397c4ec103924bf6e53b42c73e + React-RCTAppDelegate: 316e295076734baf9bdf1bfac7d92ab647aed930 + React-RCTBlob: 85c57b0d5e667ff8a472163ba3af0628171a64bb + React-RCTFabric: 97c1465ded4dc92841f5376a39e43e1b2c455f40 + React-RCTImage: b965c85bec820e2a9c154b1fb00a2ecdd59a9c92 + React-RCTLinking: 75f04a5f27c26c4e73a39c50df470820d219df79 + React-RCTNetwork: c1a9143f4d5778efc92da40d83969d03912ccc24 + React-RCTSettings: c6800f91c0ecd48868cd5db754b0b0a7f5ffe039 + React-RCTText: b923e24f9b7250bc4f7ab154c4168ad9f8d8fc9d + React-RCTVibration: 08c4f0c917c435b3619386c25a94ee5d64c250f0 + React-rendererdebug: 3cda04217d9df67b94397ee0ead8ef3d8b7e427b + React-rncore: 4013508a2f3fcf46c961919bbbd4bfdda198977e + React-RuntimeApple: 447844a2bdb0a03ffd24e5b4a4b96cfc50325b88 + React-RuntimeCore: 9b5bffdaccee9b707b1c2694c9044e13ff0bb087 + React-runtimeexecutor: 0e688aefc14c6bc8601f4968d8d01c3fb6446844 + React-RuntimeHermes: 4d6ef6bb0f2b0b40d59143317f6b99c82764c959 + React-runtimescheduler: cfbe85c3510c541ec6dc815c7729b41304b67961 + React-utils: f242eb7e7889419d979ca0e1c02ccc0ea6e43b29 + ReactCommon: f7da14a8827b72704169a48c929bcde802698361 ReactNativeExceptionHandler: b11ff67c78802b2f62eed0e10e75cb1ef7947c60 ReactNativeIncallManager: bfc9c67358cd524882a7c4116dcb311ac2293d4b - ReactNativeNavigation: e62ba14d9cd44774dca6068b6559ec872e0e61a6 + ReactNativeNavigation: 84cfcceb62947491beda20b96c5999c15ff5b959 RNCClipboard: 0a720adef5ec193aa0e3de24c3977222c7e52a37 - RNDateTimePicker: 430174392d275f0ffefb627d04f0f8677f667fed + RNDateTimePicker: 40ffda97d071a98a10fdca4fa97e3977102ccd14 RNFileViewer: ce7ca3ac370e18554d35d6355cffd7c30437c592 - RNGestureHandler: 2282cfbcf86c360d29f44ace393203afd5c6cff7 + RNGestureHandler: efed690b8493a00b99654043daeb1335276ac4a2 RNKeychain: bfe3d12bf4620fe488771c414530bf16e88f3678 - RNLocalize: b77875884750cb6a58cd6865863fe2ba2729b72b + RNLocalize: 4f22418187ecd5ca693231093ff1d912d1b3c9bc RNPermissions: 80011019862146c08f5bad2a86c27e982f483a99 RNReactNativeHapticFeedback: ec56a5f81c3941206fd85625fa669ffc7b4545f9 - RNReanimated: 8978449b2a511388ea71faf1e66f669134e87658 - RNScreens: 5aeecbb09aa7285379b6e9f3c8a3c859bb16401c - RNSentry: e9aa15bb2f3e18c822c002eea13bbd3b222ab493 + RNReanimated: f4ff116e33e0afc3d127f70efe928847c7c66355 + RNScreens: aa943ad421c3ced3ef5a47ede02b0cbfc43a012e + RNSentry: 23e21e49e5375ea9947461073ad0fd427e6e2875 RNShare: 0fad69ae2d71de9d1f7b9a43acf876886a6cb99c - RNSVG: a48668fd382115bc89761ce291a81c4ca5f2fd2e + RNSVG: cb24fb322de8c1ebf59904e7aca0447bb8dbed5a RNVectorIcons: 2a2f79274248390b80684ea3c4400bd374a15c90 - SDWebImage: dfe95b2466a9823cf9f0c6d01217c06550d7b29a - SDWebImageAVIFCoder: 00310d246aab3232ce77f1d8f0076f8c4b021d90 + SDWebImage: 066c47b573f408f18caa467d71deace7c0f8280d SDWebImageSVGCoder: 15a300a97ec1c8ac958f009c02220ac0402e936c SDWebImageWebPCoder: e38c0a70396191361d60c092933e22c20d5b1380 - Sentry: c446963245407030e17f1b926a83c6337dc99f4e + Sentry: 96ae1dcdf01a644bc3a3b1dc279cecaf48a833fb simdjson: e6bfae9ce4bcdc80452d388d593816f1ca2106f3 SocketRocket: abac6f5de4d4d62d24e11868d7a2f427e0ef940d Starscream: 19b5533ddb925208db698f0ac508a100b884a1b9 SwiftyJSON: f5b1bf1cd8dd53cd25887ac0eabcfd92301c6a5a WatermelonDB: 842d22ba555425aa9f3ce551239a001200c539bc - Yoga: ae3c32c514802d30f687a04a6a35b348506d411f + Yoga: 950bbfd7e6f04790fdb51149ed51df41f329fcc8 PODFILE CHECKSUM: 95e3d9580e262f9a50697ef0d88f61ef54954529 diff --git a/libraries/@mattermost/hardware-keyboard/src/index.tsx b/libraries/@mattermost/hardware-keyboard/src/index.tsx index 76986af975c..b8747c4c687 100644 --- a/libraries/@mattermost/hardware-keyboard/src/index.tsx +++ b/libraries/@mattermost/hardware-keyboard/src/index.tsx @@ -1,7 +1,7 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {useEffect} from 'react'; +import {useCallback, useEffect} from 'react'; import {NativeEventEmitter, NativeModules, Platform} from 'react-native'; const LINKING_ERROR = @@ -37,7 +37,7 @@ type Events = { } export function useHardwareKeyboardEvents(events: Events) { - const handleEvent = (e: Event) => { + const handleEvent = useCallback((e: Event) => { switch (e.action) { case 'enter': events.onEnterPressed?.(); @@ -49,11 +49,11 @@ export function useHardwareKeyboardEvents(events: Events) { events.onFindChannels?.(); break; } - }; + }, [events]); useEffect(() => { const listener = emitter.addListener('mmHardwareKeyboardEvent', handleEvent); return () => listener.remove(); - }, []); + }, [handleEvent]); } diff --git a/libraries/@mattermost/rnshare/android/src/main/java/com/mattermost/rnshare/ShareWorker.kt b/libraries/@mattermost/rnshare/android/src/main/java/com/mattermost/rnshare/ShareWorker.kt index 79063c2a08f..6058e1c7d21 100644 --- a/libraries/@mattermost/rnshare/android/src/main/java/com/mattermost/rnshare/ShareWorker.kt +++ b/libraries/@mattermost/rnshare/android/src/main/java/com/mattermost/rnshare/ShareWorker.kt @@ -1,12 +1,15 @@ package com.mattermost.rnshare +import android.content.pm.ServiceInfo import android.content.Context +import android.util.Base64 import android.util.Log import androidx.core.app.NotificationCompat import androidx.work.ForegroundInfo import androidx.work.Worker import androidx.work.WorkerParameters import com.mattermost.rnshare.helpers.RealPathUtil +import okhttp3.CertificatePinner import okhttp3.MediaType import okhttp3.MediaType.Companion.toMediaType import okhttp3.MediaType.Companion.toMediaTypeOrNull @@ -22,11 +25,60 @@ import org.json.JSONException import org.json.JSONObject import java.io.File import java.io.IOException +import java.io.InputStream +import java.security.MessageDigest +import java.security.cert.CertificateFactory +import java.security.cert.X509Certificate import java.util.Objects -class ShareWorker(context: Context, workerParameters: WorkerParameters) : Worker(context, workerParameters) { - private val okHttpClient = OkHttpClient() +class ShareWorker(private val context: Context, workerParameters: WorkerParameters) : Worker(context, workerParameters) { private val jsonType: MediaType? = "application/json; charset=utf-8".toMediaTypeOrNull() + private val okHttpClient: OkHttpClient + get() { + val builder = OkHttpClient.Builder() + val fingerprintsMap = getCertificatesFingerPrints() + if (fingerprintsMap.isNotEmpty()) { + val pinner = CertificatePinner.Builder() + for ((domain, fingerprints) in fingerprintsMap) { + for (fingerprint in fingerprints) { + pinner.add(domain, "sha256/$fingerprint") + } + } + val certificatePinner = pinner.build() + builder.certificatePinner(certificatePinner) + } + return builder.build() + } + + private fun getCertificateFingerPrint(certInputStream: InputStream): String { + val certFactory = CertificateFactory.getInstance("X.509") + val certificate = certFactory.generateCertificate(certInputStream) as X509Certificate + val sha256 = MessageDigest.getInstance("SHA-256") + Base64.encodeToString(sha256.digest(certificate.publicKey.encoded), Base64.NO_WRAP) + val fingerprintBytes = sha256.digest(certificate.publicKey.encoded) + return Base64.encodeToString(fingerprintBytes, Base64.NO_WRAP) + } + + private fun getCertificatesFingerPrints(): Map> { + val fingerprintsMap = mutableMapOf>() + val assetsManager = context.assets + val certFiles = assetsManager.list("certs")?.filter { it.endsWith(".cer") || it.endsWith(".crt") } ?: return emptyMap() + + for (fileName in certFiles) { + val domain = fileName.substringBeforeLast(".") + val certInputStream = assetsManager.open("certs/$fileName") + certInputStream.use { + val fingerprint = getCertificateFingerPrint(it) + if (fingerprintsMap.containsKey(domain)) { + fingerprintsMap[domain]?.add(fingerprint) + } else { + fingerprintsMap[domain] = mutableListOf(fingerprint) + } + } + } + + return fingerprintsMap + } override fun doWork(): Result { val jsonString = inputData.getString("json_data") ?: return Result.failure() @@ -147,7 +199,7 @@ class ShareWorker(context: Context, workerParameters: WorkerParameters) : Worker .setSmallIcon(applicationContext.resources.getIdentifier("ic_notification", "mipmap", applicationContext.packageName)) .setOngoing(true) .build() - return ForegroundInfo(1, notification) + return ForegroundInfo(1, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC) } } diff --git a/libraries/@mattermost/rnshare/android/src/main/java/com/mattermost/rnshare/helpers/RealPathUtil.kt b/libraries/@mattermost/rnshare/android/src/main/java/com/mattermost/rnshare/helpers/RealPathUtil.kt index 1cad57c847b..4d9b6eae7f1 100644 --- a/libraries/@mattermost/rnshare/android/src/main/java/com/mattermost/rnshare/helpers/RealPathUtil.kt +++ b/libraries/@mattermost/rnshare/android/src/main/java/com/mattermost/rnshare/helpers/RealPathUtil.kt @@ -1,10 +1,8 @@ package com.mattermost.rnshare.helpers import android.content.Context -import android.database.Cursor import android.net.Uri import android.provider.DocumentsContract -import android.provider.MediaStore import android.provider.OpenableColumns import android.text.TextUtils import android.util.Log @@ -49,25 +47,10 @@ object RealPathUtil { return null } } - } else if (isMediaDocument(uri)) { + } + else if (isMediaDocument(uri)) { // MediaProvider - val docId = DocumentsContract.getDocumentId(uri) - val split = docId.split((":").toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray() - val type = split[0] - var contentUri: Uri? = null - when (type) { - "image" -> { - contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI - } - "video" -> { - contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI - } - "audio" -> { - contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI - } - } - val selectionArgs = arrayOf(split[1]) - return contentUri?.let { getDataColumn(context, it, selectionArgs) } + return getPathFromSavingTempFile(context, uri) } } if ("content".equals(uri.scheme, ignoreCase = true)) { @@ -133,27 +116,6 @@ object RealPathUtil { return f.name } - private fun getDataColumn(context:Context, uri:Uri, selectionArgs:Array): String? { - var cursor: Cursor? = null - val column = "_data" - val selection = "_id=?" - val projection = arrayOf(column) - try - { - cursor = context.contentResolver.query(uri, projection, selection, selectionArgs, null) - if (cursor != null && cursor.moveToFirst()) { - val index = cursor.getColumnIndexOrThrow(column) - return cursor.getString(index) - } - } - finally - { - cursor?.close() - } - - return null - } - private fun isExternalStorageDocument(uri:Uri):Boolean { return "com.android.externalstorage.documents" == uri.authority } diff --git a/package-lock.json b/package-lock.json index 23aef6ae4d5..cb9d55b810c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "mattermost-mobile", - "version": "2.18.0", + "version": "2.20.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "mattermost-mobile", - "version": "2.18.0", + "version": "2.20.0", "hasInstallScript": true, "license": "Apache 2.0", "dependencies": { @@ -16,113 +16,114 @@ "@formatjs/intl-locale": "4.0.0", "@formatjs/intl-numberformat": "8.10.3", "@formatjs/intl-pluralrules": "5.2.14", - "@gorhom/bottom-sheet": "4.6.3", - "@mattermost/calls": "github:mattermost/calls-common#cc5e65f6c103138fd013d6fa45eb33bfe7544f2a", + "@gorhom/bottom-sheet": "4.6.4", + "@mattermost/calls": "github:mattermost/calls-common#1ce6defb1ee0c1e0f106ddff8f46c37d10d60b76", "@mattermost/compass-icons": "0.1.45", "@mattermost/hardware-keyboard": "file:./libraries/@mattermost/hardware-keyboard", "@mattermost/keyboard-tracker": "file:./libraries/@mattermost/keyboard-tracker", "@mattermost/react-native-emm": "1.5.0", - "@mattermost/react-native-network-client": "1.6.0", + "@mattermost/react-native-network-client": "1.7.2", "@mattermost/react-native-paste-input": "0.8.0", "@mattermost/react-native-turbo-log": "0.4.0", "@mattermost/rnshare": "file:./libraries/@mattermost/rnshare", "@mattermost/rnutils": "file:./libraries/@mattermost/rnutils", "@msgpack/msgpack": "2.8.0", "@nozbe/watermelondb": "0.27.1", - "@react-native-camera-roll/camera-roll": "7.8.1", + "@react-native-camera-roll/camera-roll": "7.8.3", "@react-native-clipboard/clipboard": "1.14.1", - "@react-native-community/datetimepicker": "8.1.1", + "@react-native-community/datetimepicker": "8.2.0", "@react-native-community/netinfo": "11.3.2", "@react-native-cookies/cookies": "6.2.1", - "@react-navigation/bottom-tabs": "6.5.20", - "@react-navigation/native": "6.1.17", - "@react-navigation/stack": "6.3.29", + "@react-navigation/bottom-tabs": "6.6.1", + "@react-navigation/native": "6.1.18", + "@react-navigation/stack": "6.4.1", "@rneui/base": "4.0.0-rc.8", - "@sentry/react-native": "5.24.1", + "@sentry/react-native": "5.27.0", "@stream-io/flat-list-mvcp": "0.10.3", "@voximplant/react-native-foreground-service": "3.0.2", "base-64": "1.0.0", - "commonmark": "npm:@mattermost/commonmark@0.30.1-2", + "commonmark": "npm:@mattermost/commonmark@0.30.1-3", "commonmark-react-renderer": "github:mattermost/commonmark-react-renderer#81b5d27509652bae50b4b510ede777dd3bd923cf", "deep-equal": "2.2.3", "deepmerge": "4.3.1", "emoji-regex": "10.3.0", - "expo": "51.0.14", + "expo": "51.0.24", "expo-application": "5.9.1", - "expo-crypto": "~13.0.2", + "expo-crypto": "13.0.2", "expo-device": "6.0.2", - "expo-image": "1.12.12", + "expo-image": "1.12.13", "expo-linear-gradient": "13.0.2", "expo-store-review": "7.0.2", "expo-video-thumbnails": "8.0.0", "expo-web-browser": "13.0.3", "fuse.js": "7.0.0", "html-entities": "2.5.2", - "mime-db": "1.52.0", + "mime-db": "1.53.0", "moment-timezone": "0.5.45", + "node-html-parser": "6.1.13", "pako": "2.1.0", - "path-to-regexp": "6.2.2", + "path-to-regexp": "7.1.0", "react": "18.2.0", "react-freeze": "1.0.4", "react-intl": "6.6.8", - "react-native": "0.74.2", + "react-native": "0.74.5", "react-native-background-timer": "2.4.1", "react-native-document-picker": "9.3.0", "react-native-dotenv": "3.4.11", "react-native-exception-handler": "2.10.10", "react-native-file-viewer": "2.1.5", - "react-native-gesture-handler": "2.16.2", + "react-native-gesture-handler": "2.18.1", "react-native-haptic-feedback": "2.2.0", "react-native-image-picker": "7.1.2", "react-native-incall-manager": "4.2.0", "react-native-keyboard-aware-scroll-view": "0.9.5", "react-native-keychain": "8.2.0", - "react-native-localize": "3.2.0", + "react-native-localize": "3.2.1", "react-native-math-view": "3.9.5", - "react-native-navigation": "7.40.0", + "react-native-navigation": "7.40.1", "react-native-notifications": "5.1.0", "react-native-performance": "5.1.2", "react-native-permissions": "4.1.5", - "react-native-reanimated": "3.12.0", - "react-native-safe-area-context": "4.10.5", - "react-native-screens": "3.32.0", + "react-native-reanimated": "3.14.0", + "react-native-safe-area-context": "4.10.8", + "react-native-screens": "3.34.0", "react-native-section-list-get-item-layout": "2.2.3", "react-native-shadow-2": "7.1.0", "react-native-share": "10.2.1", - "react-native-svg": "15.3.0", + "react-native-svg": "15.4.0", "react-native-vector-icons": "10.1.0", - "react-native-video": "6.2.0", + "react-native-video": "6.4.3", "react-native-walkthrough-tooltip": "1.6.0", - "react-native-webrtc": "124.0.1", + "react-native-webrtc": "124.0.3", "react-syntax-highlighter": "15.5.0", - "semver": "7.6.2", + "semver": "7.6.3", "tinycolor2": "1.6.0", "url-parse": "1.5.10" }, "devDependencies": { - "@babel/cli": "7.24.7", - "@babel/core": "7.24.7", - "@babel/eslint-parser": "7.24.7", + "@babel/cli": "7.24.8", + "@babel/core": "7.25.2", + "@babel/eslint-parser": "7.25.1", "@babel/plugin-proposal-class-properties": "7.18.6", "@babel/plugin-proposal-decorators": "7.24.7", - "@babel/plugin-transform-flow-strip-types": "7.24.7", + "@babel/plugin-transform-flow-strip-types": "7.25.2", "@babel/plugin-transform-runtime": "7.24.7", - "@babel/preset-env": "7.24.7", + "@babel/preset-env": "7.25.3", "@babel/preset-typescript": "7.24.7", "@babel/register": "7.24.6", - "@babel/runtime": "7.24.7", - "@react-native/babel-preset": "0.74.84", - "@react-native/eslint-config": "0.74.84", - "@react-native/metro-config": "0.74.84", - "@react-native/typescript-config": "0.74.84", + "@babel/runtime": "7.25.0", + "@react-native/babel-preset": "0.74.87", + "@react-native/eslint-config": "0.74.87", + "@react-native/metro-config": "0.74.87", + "@react-native/typescript-config": "0.74.87", "@testing-library/react-hooks": "8.0.1", - "@testing-library/react-native": "12.5.1", + "@testing-library/react-native": "12.5.2", "@types/base-64": "1.0.2", "@types/commonmark": "0.27.9", "@types/commonmark-react-renderer": "4.3.4", "@types/deep-equal": "1.0.4", "@types/jest": "29.5.12", - "@types/lodash": "4.17.5", + "@types/lodash": "4.17.7", "@types/mime-db": "1.43.5", "@types/pako": "2.0.3", "@types/querystringify": "2.0.2", @@ -135,34 +136,34 @@ "@types/tinycolor2": "1.4.6", "@types/tough-cookie": "4.0.5", "@types/url-parse": "1.4.11", - "@types/uuid": "9.0.8", + "@types/uuid": "10.0.0", "@typescript-eslint/eslint-plugin": "7.13.1", "@typescript-eslint/parser": "7.13.1", - "axios": "1.7.2", + "axios": "1.7.5", "axios-cookiejar-support": "5.0.2", "babel-jest": "29.7.0", "babel-loader": "9.1.3", "babel-plugin-module-resolver": "5.0.2", - "detox": "20.20.1", + "detox": "20.25.2", "eslint": "8.57.0", "eslint-plugin-header": "3.1.1", "eslint-plugin-import": "2.29.1", - "eslint-plugin-jest": "28.6.0", - "eslint-plugin-react": "7.34.3", + "eslint-plugin-jest": "28.7.0", + "eslint-plugin-react": "7.35.0", "eslint-plugin-react-hooks": "4.6.2", - "husky": "9.0.11", + "husky": "9.1.4", "isomorphic-fetch": "3.0.0", "jest": "29.7.0", "jest-cli": "29.7.0", - "jest-expo": "51.0.2", + "jest-expo": "51.0.3", "jetifier": "2.0.0", "mmjstool": "github:mattermost/mattermost-utilities#83b1b311972b8f5e750aae4019457a40abb5aa44", "nock": "13.5.4", "patch-package": "8.0.0", - "react-devtools-core": "5.2.0", + "react-devtools-core": "5.3.1", "tough-cookie": "4.1.4", - "ts-jest": "29.1.5", - "typescript": "5.4.5", + "ts-jest": "29.2.4", + "typescript": "5.5.4", "uuid": "10.0.0" }, "engines": { @@ -343,10 +344,11 @@ } }, "node_modules/@babel/cli": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.24.7.tgz", - "integrity": "sha512-8dfPprJgV4O14WTx+AQyEA+opgUKPrsIXX/MdL50J1n06EQJ6m1T+CdsJe0qEC0B/Xl85i+Un5KVAxd/PACX9A==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.24.8.tgz", + "integrity": "sha512-isdp+G6DpRyKc+3Gqxy2rjzgF7Zj9K0mzLNnxz+E/fgeag8qT3vVulX4gY9dGO1q0y+0lUv6V3a+uhUzMzrwXg==", "dev": true, + "license": "MIT", "dependencies": { "@jridgewell/trace-mapping": "^0.3.25", "commander": "^6.2.0", @@ -393,28 +395,30 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.7.tgz", - "integrity": "sha512-qJzAIcv03PyaWqxRgO4mSU3lihncDT296vnyuE2O8uA4w3UHWI4S3hgeZd1L8W1Bft40w9JxJ2b412iDUFFRhw==", + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.25.2.tgz", + "integrity": "sha512-bYcppcpKBvX4znYaPEeFau03bp89ShqNMLs+rmdptMw+heSZh9+z84d2YG+K7cYLbWwzdjtDoW/uqZmPjulClQ==", + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.7.tgz", - "integrity": "sha512-nykK+LEK86ahTkX/3TgauT0ikKoNCfKHEaZYTUVupJdTLzGNvrblu4u6fa7DhZONAltdf8e662t/abY8idrd/g==", + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.25.2.tgz", + "integrity": "sha512-BBt3opiCOxUr9euZ5/ro/Xv8/V7yJ5bjYMqG/C1YAo8MIKAnumZalCN+msbci3Pigy4lIQfPUpfMM27HMGaYEA==", + "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.24.7", - "@babel/helper-compilation-targets": "^7.24.7", - "@babel/helper-module-transforms": "^7.24.7", - "@babel/helpers": "^7.24.7", - "@babel/parser": "^7.24.7", - "@babel/template": "^7.24.7", - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7", + "@babel/generator": "^7.25.0", + "@babel/helper-compilation-targets": "^7.25.2", + "@babel/helper-module-transforms": "^7.25.2", + "@babel/helpers": "^7.25.0", + "@babel/parser": "^7.25.0", + "@babel/template": "^7.25.0", + "@babel/traverse": "^7.25.2", + "@babel/types": "^7.25.2", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -438,10 +442,11 @@ } }, "node_modules/@babel/eslint-parser": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.24.7.tgz", - "integrity": "sha512-SO5E3bVxDuxyNxM5agFv480YA2HO6ohZbGxbazZdIk3KQOPOGVNw6q78I9/lbviIf95eq6tPozeYnJLbjnC8IA==", + "version": "7.25.1", + "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.25.1.tgz", + "integrity": "sha512-Y956ghgTT4j7rKesabkh5WeqgSFZVFwaPR0IWFm7KFHFmmJ4afbG49SmfW4S+GyRPx0Dy5jxEWA5t0rpxfElWg==", "dev": true, + "license": "MIT", "dependencies": { "@nicolo-ribaudo/eslint-scope-5-internals": "5.1.1-v1", "eslint-visitor-keys": "^2.1.0", @@ -465,11 +470,12 @@ } }, "node_modules/@babel/generator": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.7.tgz", - "integrity": "sha512-oipXieGC3i45Y1A41t4tAqpnEZWgB/lC6Ehh6+rOviR5XWpTtMmLN+fGjz9vOiNRt0p6RtO6DtD0pdU3vpqdSA==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.0.tgz", + "integrity": "sha512-3LEEcj3PVW8pW2R1SR1M89g/qrYk/m/mB/tLqn7dn4sbBUQyTqnlod+II2U4dqiGtUmkcnAmkMDralTFZttRiw==", + "license": "MIT", "dependencies": { - "@babel/types": "^7.24.7", + "@babel/types": "^7.25.0", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^2.5.1" @@ -502,13 +508,14 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.24.7.tgz", - "integrity": "sha512-ctSdRHBi20qWOfy27RUb4Fhp07KSJ3sXcuSvTrXrc4aG8NSYDo1ici3Vhg9bg69y5bj0Mr1lh0aeEgTvc12rMg==", + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.2.tgz", + "integrity": "sha512-U2U5LsSaZ7TAt3cfaymQ8WHh0pxvdHoEk6HVpaexxixjyEquMh0L0YNJNM6CTGKMXV1iksi0iZkGw4AcFkPaaw==", + "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.24.7", - "@babel/helper-validator-option": "^7.24.7", - "browserslist": "^4.22.2", + "@babel/compat-data": "^7.25.2", + "@babel/helper-validator-option": "^7.24.8", + "browserslist": "^4.23.1", "lru-cache": "^5.1.1", "semver": "^6.3.1" }, @@ -555,9 +562,10 @@ } }, "node_modules/@babel/helper-create-regexp-features-plugin": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.24.7.tgz", - "integrity": "sha512-03TCmXy2FtXJEZfbXDTSqq1fRJArk7lX9DOFC/47VthYcxyIOx+eXQmdo6DOQvrbpIix+KfXwvuXdFDZHxt+rA==", + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.25.2.tgz", + "integrity": "sha512-+wqVGP+DFmqwFD3EH6TMTfUNeqDehV3E/dl+Sd54eaXqm17tEUNbEIn4sVivVowbvUpOtIGxdo3GoXyDH9N/9g==", + "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.24.7", "regexpu-core": "^5.3.1", @@ -616,24 +624,14 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/helper-hoist-variables": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.24.7.tgz", - "integrity": "sha512-MJJwhkoGy5c4ehfoRyrJ/owKeMl19U54h27YYftT0o2teQ3FJ3nQUf/I3LlJsX4l3qlw7WRXUmiyajvHXoTubQ==", - "dependencies": { - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.24.7.tgz", - "integrity": "sha512-LGeMaf5JN4hAT471eJdBs/GK1DoYIJ5GCtZN/EsL6KUiiDZOvO/eKE11AMZJa2zP4zk4qe9V2O/hxAmkRc8p6w==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.24.8.tgz", + "integrity": "sha512-LABppdt+Lp/RlBxqrh4qgf1oEH/WxdzQNDJIu5gC/W1GyvPVrOBiItmmM8wan2fm4oYqFuFfkXmlGpLQhPY8CA==", + "license": "MIT", "dependencies": { - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/traverse": "^7.24.8", + "@babel/types": "^7.24.8" }, "engines": { "node": ">=6.9.0" @@ -652,15 +650,15 @@ } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.24.7.tgz", - "integrity": "sha512-1fuJEwIrp+97rM4RWdO+qrRsZlAeL1lQJoPqtCYWv0NL115XM93hIH4CSRln2w52SqvmY5hqdtauB6QFCDiZNQ==", + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.25.2.tgz", + "integrity": "sha512-BjyRAbix6j/wv83ftcVJmBt72QtHI56C7JXZoG2xATiLpmoC7dpd8WnkikExHDVPpi/3qCmO6WY1EaXOluiecQ==", + "license": "MIT", "dependencies": { - "@babel/helper-environment-visitor": "^7.24.7", "@babel/helper-module-imports": "^7.24.7", "@babel/helper-simple-access": "^7.24.7", - "@babel/helper-split-export-declaration": "^7.24.7", - "@babel/helper-validator-identifier": "^7.24.7" + "@babel/helper-validator-identifier": "^7.24.7", + "@babel/traverse": "^7.25.2" }, "engines": { "node": ">=6.9.0" @@ -681,21 +679,23 @@ } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.7.tgz", - "integrity": "sha512-Rq76wjt7yz9AAc1KnlRKNAi/dMSVWgDRx43FHoJEbcYU6xOWaE2dVPwcdTukJrjxS65GITyfbvEYHvkirZ6uEg==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.8.tgz", + "integrity": "sha512-FFWx5142D8h2Mgr/iPVGH5G7w6jDn4jUSpZTyDnQO0Yn7Ks2Kuz6Pci8H6MPCoUJegd/UZQ3tAvfLCxQSnWWwg==", + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-remap-async-to-generator": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.24.7.tgz", - "integrity": "sha512-9pKLcTlZ92hNZMQfGCHImUpDOlAgkkpqalWEeftW5FBya75k8Li2ilerxkM/uBEj01iBZXcCIB/bwvDYgWyibA==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.25.0.tgz", + "integrity": "sha512-NhavI2eWEIz/H9dbrG0TuOicDhNexze43i5z7lEqwYm0WEZVTwnPpA0EafUTP7+6/W79HWIP2cTe3Z5NiSTVpw==", + "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.24.7", - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-wrap-function": "^7.24.7" + "@babel/helper-wrap-function": "^7.25.0", + "@babel/traverse": "^7.25.0" }, "engines": { "node": ">=6.9.0" @@ -705,13 +705,14 @@ } }, "node_modules/@babel/helper-replace-supers": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.24.7.tgz", - "integrity": "sha512-qTAxxBM81VEyoAY0TtLrx1oAEJc09ZK67Q9ljQToqCnA+55eNwCORaxlKyu+rNfX86o8OXRUSNUnrtsAZXM9sg==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.25.0.tgz", + "integrity": "sha512-q688zIvQVYtZu+i2PsdIu/uWGRpfxzr5WESsfpShfZECkO+d2o+WROWezCi/Q6kJ0tfPa5+pUGUlfx2HhrA3Bg==", + "license": "MIT", "dependencies": { - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-member-expression-to-functions": "^7.24.7", - "@babel/helper-optimise-call-expression": "^7.24.7" + "@babel/helper-member-expression-to-functions": "^7.24.8", + "@babel/helper-optimise-call-expression": "^7.24.7", + "@babel/traverse": "^7.25.0" }, "engines": { "node": ">=6.9.0" @@ -756,9 +757,10 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.7.tgz", - "integrity": "sha512-7MbVt6xrwFQbunH2DNQsAP5sTGxfqQtErvBIvIMi6EQnbgUOuVYanvREcmFrOPhoXBrTtjhhP+lW+o5UfK+tDg==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.8.tgz", + "integrity": "sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==", + "license": "MIT", "engines": { "node": ">=6.9.0" } @@ -772,34 +774,36 @@ } }, "node_modules/@babel/helper-validator-option": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.24.7.tgz", - "integrity": "sha512-yy1/KvjhV/ZCL+SM7hBrvnZJ3ZuT9OuZgIJAGpPEToANvc3iM6iDvBnRjtElWibHU6n8/LPR/EjX9EtIEYO3pw==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.24.8.tgz", + "integrity": "sha512-xb8t9tD1MHLungh/AIoWYN+gVHaB9kwlu8gffXGSt3FFEIT7RjS+xWbc2vUD1UTZdIpKj/ab3rdqJ7ufngyi2Q==", + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-wrap-function": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.24.7.tgz", - "integrity": "sha512-N9JIYk3TD+1vq/wn77YnJOqMtfWhNewNE+DJV4puD2X7Ew9J4JvrzrFDfTfyv5EgEXVy9/Wt8QiOErzEmv5Ifw==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.25.0.tgz", + "integrity": "sha512-s6Q1ebqutSiZnEjaofc/UKDyC4SbzV5n5SrA2Gq8UawLycr3i04f1dX4OzoQVnexm6aOCh37SQNYlJ/8Ku+PMQ==", + "license": "MIT", "dependencies": { - "@babel/helper-function-name": "^7.24.7", - "@babel/template": "^7.24.7", - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/template": "^7.25.0", + "@babel/traverse": "^7.25.0", + "@babel/types": "^7.25.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.7.tgz", - "integrity": "sha512-NlmJJtvcw72yRJRcnCmGvSi+3jDEg8qFu3z0AFoymmzLx5ERVWyzd9kVXr7Th9/8yIJi2Zc6av4Tqz3wFs8QWg==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.25.0.tgz", + "integrity": "sha512-MjgLZ42aCm0oGjJj8CtSM3DB8NOOf8h2l7DCTePJs29u+v7yO/RBX9nShlKMgFnRks/Q4tBAe7Hxnov9VkGwLw==", + "license": "MIT", "dependencies": { - "@babel/template": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/template": "^7.25.0", + "@babel/types": "^7.25.0" }, "engines": { "node": ">=6.9.0" @@ -820,9 +824,13 @@ } }, "node_modules/@babel/parser": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.7.tgz", - "integrity": "sha512-9uUYRm6OqQrCqQdG1iCBwBPZgN8ciDBro2nIOFaiRz1/BCxaI7CNvQbDHvsArAC7Tw9Hda/B3U+6ui9u4HWXPw==", + "version": "7.25.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.3.tgz", + "integrity": "sha512-iLTJKDbJ4hMvFPgQwwsVoxtHyWpKKPBrxkANrSYewDPaPpT5py5yeVkgPIJ7XYXhndxJpaA3PyALSXQ7u8e/Dw==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.25.2" + }, "bin": { "parser": "bin/babel-parser.js" }, @@ -831,12 +839,28 @@ } }, "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.24.7.tgz", - "integrity": "sha512-TiT1ss81W80eQsN+722OaeQMY/G4yTb4G9JrqeiDADs3N8lbPMGldWi9x8tyqCW5NLx1Jh2AvkE6r6QvEltMMQ==", + "version": "7.25.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.25.3.tgz", + "integrity": "sha512-wUrcsxZg6rqBXG05HG1FPYgsP6EvwF4WpBbxIpWIIYnH8wG0gzx3yZY3dtEHas4sTAOGkbTsc9EGPxwff8lRoA==", + "license": "MIT", "dependencies": { - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.8", + "@babel/traverse": "^7.25.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-safari-class-field-initializer-scope": { + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.25.0.tgz", + "integrity": "sha512-Bm4bH2qsX880b/3ziJ8KD711LT7z4u8CFudmjqle65AZj/HNUFhEf90dqYv6O86buWvSBmeQDjv0Tn2aF/bIBA==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.8" }, "engines": { "node": ">=6.9.0" @@ -846,11 +870,12 @@ } }, "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.24.7.tgz", - "integrity": "sha512-unaQgZ/iRu/By6tsjMZzpeBZjChYfLYry6HrEXPoz3KmfF0sVBQ1l8zKMQ4xRGLWVsjuvB8nQfjNP/DcfEOCsg==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.25.0.tgz", + "integrity": "sha512-lXwdNZtTmeVOOFtwM/WDe7yg1PL8sYhRk/XH0FzbR2HDQ0xC+EnQ/JHeoMYSavtU115tnUk0q9CDyq8si+LMAA==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.8" }, "engines": { "node": ">=6.9.0" @@ -876,12 +901,13 @@ } }, "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.24.7.tgz", - "integrity": "sha512-utA4HuR6F4Vvcr+o4DnjL8fCOlgRFGbeeBEGNg3ZTrLFw6VWG5XmUrvcQ0FjIYMU2ST4XcR2Wsp7t9qOAPnxMg==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.25.0.tgz", + "integrity": "sha512-tggFrk1AIShG/RUQbEwt2Tr/E+ObkfwrPjR6BjbRvsx24+PSjK8zrq0GWPNCjo8qpRx4DuJzlcvWJqlm+0h3kw==", + "license": "MIT", "dependencies": { - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.8", + "@babel/traverse": "^7.25.0" }, "engines": { "node": ">=6.9.0" @@ -1380,14 +1406,15 @@ } }, "node_modules/@babel/plugin-transform-async-generator-functions": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.24.7.tgz", - "integrity": "sha512-o+iF77e3u7ZS4AoAuJvapz9Fm001PuD2V3Lp6OSE4FYQke+cSewYtnek+THqGRWyQloRCyvWL1OkyfNEl9vr/g==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.25.0.tgz", + "integrity": "sha512-uaIi2FdqzjpAMvVqvB51S42oC2JEVgh0LDsGfZVDysWE8LrJtQC2jvKmOqEYThKyB7bDEb7BP1GYWDm7tABA0Q==", + "license": "MIT", "dependencies": { - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-remap-async-to-generator": "^7.24.7", - "@babel/plugin-syntax-async-generators": "^7.8.4" + "@babel/helper-plugin-utils": "^7.24.8", + "@babel/helper-remap-async-to-generator": "^7.25.0", + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/traverse": "^7.25.0" }, "engines": { "node": ">=6.9.0" @@ -1550,6 +1577,22 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-transform-duplicate-named-capturing-groups-regex": { + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.25.0.tgz", + "integrity": "sha512-YLpb4LlYSc3sCUa35un84poXoraOiQucUTTu8X1j18JV+gNa8E0nyUf/CjZ171IRGr4jEguF+vzJU66QZhn29g==", + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.25.0", + "@babel/helper-plugin-utils": "^7.24.8" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, "node_modules/@babel/plugin-transform-dynamic-import": { "version": "7.24.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.24.7.tgz", @@ -1596,11 +1639,12 @@ } }, "node_modules/@babel/plugin-transform-flow-strip-types": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.24.7.tgz", - "integrity": "sha512-cjRKJ7FobOH2eakx7Ja+KpJRj8+y+/SiB3ooYm/n2UJfxu0oEaOoxOinitkJcPqv9KxS0kxTGPUaR7L2XcXDXA==", + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.25.2.tgz", + "integrity": "sha512-InBZ0O8tew5V0K6cHcQ+wgxlrjOw1W4wDXLkOTjLRD8GYhTSkxTVBtdy3MMtvYBrbAWa1Qm3hNoTc1620Yj+Mg==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.8", "@babel/plugin-syntax-flow": "^7.24.7" }, "engines": { @@ -1731,14 +1775,15 @@ } }, "node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.24.7.tgz", - "integrity": "sha512-GYQE0tW7YoaN13qFh3O1NCY4MPkUiAH3fiF7UcV/I3ajmDKEdG3l+UOcbAm4zUE3gnvUU+Eni7XrVKo9eO9auw==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.25.0.tgz", + "integrity": "sha512-YPJfjQPDXxyQWg/0+jHKj1llnY5f/R6a0p/vP4lPymxLu7Lvl4k2WMitqi08yxwQcCVUUdG9LCUj4TNEgAp3Jw==", + "license": "MIT", "dependencies": { - "@babel/helper-hoist-variables": "^7.24.7", - "@babel/helper-module-transforms": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-validator-identifier": "^7.24.7" + "@babel/helper-module-transforms": "^7.25.0", + "@babel/helper-plugin-utils": "^7.24.8", + "@babel/helper-validator-identifier": "^7.24.7", + "@babel/traverse": "^7.25.0" }, "engines": { "node": ">=6.9.0" @@ -1869,11 +1914,12 @@ } }, "node_modules/@babel/plugin-transform-optional-chaining": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.24.7.tgz", - "integrity": "sha512-tK+0N9yd4j+x/4hxF3F0e0fu/VdcxU18y5SevtyM/PCFlQvXbR0Zmlo2eBrKtVipGNFzpq56o8WsIIKcJFUCRQ==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.24.8.tgz", + "integrity": "sha512-5cTOLSMs9eypEy8JUVvIKOu6NgvbJMnpG62VpIHrTmROdQ+L5mDAaI40g25k5vXti55JWNX5jCkq3HZxXBQANw==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.8", "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7", "@babel/plugin-syntax-optional-chaining": "^7.8.3" }, @@ -1980,6 +2026,7 @@ "version": "7.24.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.24.7.tgz", "integrity": "sha512-QG9EnzoGn+Qar7rxuW+ZOsbWOt56FvvI93xInqsZDC5fsekx1AlIO4KIJ5M+D0p0SqSH156EpmZyXq630B8OlQ==", + "license": "MIT", "dependencies": { "@babel/plugin-transform-react-jsx": "^7.24.7" }, @@ -2022,6 +2069,7 @@ "version": "7.24.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.24.7.tgz", "integrity": "sha512-PLgBVk3fzbmEjBJ/u8kFzOqS9tUeDjiaWud/rRym/yjCo/M9cASPlnrd2ZmmZpQT40fOOrvR8jh+n8jikrOhNA==", + "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.24.7", "@babel/helper-plugin-utils": "^7.24.7" @@ -2147,11 +2195,12 @@ } }, "node_modules/@babel/plugin-transform-typeof-symbol": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.24.7.tgz", - "integrity": "sha512-VtR8hDy7YLB7+Pet9IarXjg/zgCMSF+1mNS/EQEiEaUPoFXCVsHG64SIxcaaI2zJgRiv+YmgaQESUfWAdbjzgg==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.24.8.tgz", + "integrity": "sha512-adNTUpDCVnmAE58VEqKlAA6ZBlNkMnWD0ZcW76lyNFN3MJniyGFZfNwERVk8Ap56MCnXztmDr19T4mPTztcuaw==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.8" }, "engines": { "node": ">=6.9.0" @@ -2237,18 +2286,20 @@ } }, "node_modules/@babel/preset-env": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.24.7.tgz", - "integrity": "sha512-1YZNsc+y6cTvWlDHidMBsQZrZfEFjRIo/BZCT906PMdzOyXtSLTgqGdrpcuTDCXyd11Am5uQULtDIcCfnTc8fQ==", + "version": "7.25.3", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.25.3.tgz", + "integrity": "sha512-QsYW7UeAaXvLPX9tdVliMJE7MD7M6MLYVTovRTIwhoYQVFHR1rM4wO8wqAezYi3/BpSD+NzVCZ69R6smWiIi8g==", + "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.24.7", - "@babel/helper-compilation-targets": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-validator-option": "^7.24.7", - "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.24.7", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.24.7", + "@babel/compat-data": "^7.25.2", + "@babel/helper-compilation-targets": "^7.25.2", + "@babel/helper-plugin-utils": "^7.24.8", + "@babel/helper-validator-option": "^7.24.8", + "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.25.3", + "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.25.0", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.25.0", "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.24.7", - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.24.7", + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.25.0", "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", "@babel/plugin-syntax-async-generators": "^7.8.4", "@babel/plugin-syntax-class-properties": "^7.12.13", @@ -2269,29 +2320,30 @@ "@babel/plugin-syntax-top-level-await": "^7.14.5", "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", "@babel/plugin-transform-arrow-functions": "^7.24.7", - "@babel/plugin-transform-async-generator-functions": "^7.24.7", + "@babel/plugin-transform-async-generator-functions": "^7.25.0", "@babel/plugin-transform-async-to-generator": "^7.24.7", "@babel/plugin-transform-block-scoped-functions": "^7.24.7", - "@babel/plugin-transform-block-scoping": "^7.24.7", + "@babel/plugin-transform-block-scoping": "^7.25.0", "@babel/plugin-transform-class-properties": "^7.24.7", "@babel/plugin-transform-class-static-block": "^7.24.7", - "@babel/plugin-transform-classes": "^7.24.7", + "@babel/plugin-transform-classes": "^7.25.0", "@babel/plugin-transform-computed-properties": "^7.24.7", - "@babel/plugin-transform-destructuring": "^7.24.7", + "@babel/plugin-transform-destructuring": "^7.24.8", "@babel/plugin-transform-dotall-regex": "^7.24.7", "@babel/plugin-transform-duplicate-keys": "^7.24.7", + "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.25.0", "@babel/plugin-transform-dynamic-import": "^7.24.7", "@babel/plugin-transform-exponentiation-operator": "^7.24.7", "@babel/plugin-transform-export-namespace-from": "^7.24.7", "@babel/plugin-transform-for-of": "^7.24.7", - "@babel/plugin-transform-function-name": "^7.24.7", + "@babel/plugin-transform-function-name": "^7.25.1", "@babel/plugin-transform-json-strings": "^7.24.7", - "@babel/plugin-transform-literals": "^7.24.7", + "@babel/plugin-transform-literals": "^7.25.2", "@babel/plugin-transform-logical-assignment-operators": "^7.24.7", "@babel/plugin-transform-member-expression-literals": "^7.24.7", "@babel/plugin-transform-modules-amd": "^7.24.7", - "@babel/plugin-transform-modules-commonjs": "^7.24.7", - "@babel/plugin-transform-modules-systemjs": "^7.24.7", + "@babel/plugin-transform-modules-commonjs": "^7.24.8", + "@babel/plugin-transform-modules-systemjs": "^7.25.0", "@babel/plugin-transform-modules-umd": "^7.24.7", "@babel/plugin-transform-named-capturing-groups-regex": "^7.24.7", "@babel/plugin-transform-new-target": "^7.24.7", @@ -2300,7 +2352,7 @@ "@babel/plugin-transform-object-rest-spread": "^7.24.7", "@babel/plugin-transform-object-super": "^7.24.7", "@babel/plugin-transform-optional-catch-binding": "^7.24.7", - "@babel/plugin-transform-optional-chaining": "^7.24.7", + "@babel/plugin-transform-optional-chaining": "^7.24.8", "@babel/plugin-transform-parameters": "^7.24.7", "@babel/plugin-transform-private-methods": "^7.24.7", "@babel/plugin-transform-private-property-in-object": "^7.24.7", @@ -2311,7 +2363,7 @@ "@babel/plugin-transform-spread": "^7.24.7", "@babel/plugin-transform-sticky-regex": "^7.24.7", "@babel/plugin-transform-template-literals": "^7.24.7", - "@babel/plugin-transform-typeof-symbol": "^7.24.7", + "@babel/plugin-transform-typeof-symbol": "^7.24.8", "@babel/plugin-transform-unicode-escapes": "^7.24.7", "@babel/plugin-transform-unicode-property-regex": "^7.24.7", "@babel/plugin-transform-unicode-regex": "^7.24.7", @@ -2320,7 +2372,7 @@ "babel-plugin-polyfill-corejs2": "^0.4.10", "babel-plugin-polyfill-corejs3": "^0.10.4", "babel-plugin-polyfill-regenerator": "^0.6.1", - "core-js-compat": "^3.31.0", + "core-js-compat": "^3.37.1", "semver": "^6.3.1" }, "engines": { @@ -2330,6 +2382,105 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/preset-env/node_modules/@babel/plugin-transform-block-scoping": { + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.25.0.tgz", + "integrity": "sha512-yBQjYoOjXlFv9nlXb3f1casSHOZkWr29NX+zChVanLg5Nc157CrbEX9D7hxxtTpuFy7Q0YzmmWfJxzvps4kXrQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.8" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-env/node_modules/@babel/plugin-transform-classes": { + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.25.0.tgz", + "integrity": "sha512-xyi6qjr/fYU304fiRwFbekzkqVJZ6A7hOjWZd+89FVcBqPV3S9Wuozz82xdpLspckeaafntbzglaW4pqpzvtSw==", + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.24.7", + "@babel/helper-compilation-targets": "^7.24.8", + "@babel/helper-plugin-utils": "^7.24.8", + "@babel/helper-replace-supers": "^7.25.0", + "@babel/traverse": "^7.25.0", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-env/node_modules/@babel/plugin-transform-destructuring": { + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.24.8.tgz", + "integrity": "sha512-36e87mfY8TnRxc7yc6M9g9gOB7rKgSahqkIKwLpz4Ppk2+zC2Cy1is0uwtuSG6AE4zlTOUa+7JGz9jCJGLqQFQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.8" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-env/node_modules/@babel/plugin-transform-function-name": { + "version": "7.25.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.25.1.tgz", + "integrity": "sha512-TVVJVdW9RKMNgJJlLtHsKDTydjZAbwIsn6ySBPQaEAUU5+gVvlJt/9nRmqVbsV/IBanRjzWoaAQKLoamWVOUuA==", + "license": "MIT", + "dependencies": { + "@babel/helper-compilation-targets": "^7.24.8", + "@babel/helper-plugin-utils": "^7.24.8", + "@babel/traverse": "^7.25.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-env/node_modules/@babel/plugin-transform-literals": { + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.25.2.tgz", + "integrity": "sha512-HQI+HcTbm9ur3Z2DkO+jgESMAMcYLuN/A7NRw9juzxAezN9AvqvUTnpKP/9kkYANz6u7dFlAyOu44ejuGySlfw==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.8" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-env/node_modules/@babel/plugin-transform-modules-commonjs": { + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.24.8.tgz", + "integrity": "sha512-WHsk9H8XxRs3JXKWFiqtQebdh9b/pTk4EgueygFzYlTKAg0Ud985mSevdNjdXdFBATSKVJGQXP1tv6aGbssLKA==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-transforms": "^7.24.8", + "@babel/helper-plugin-utils": "^7.24.8", + "@babel/helper-simple-access": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/preset-env/node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", @@ -2371,6 +2522,7 @@ "version": "7.24.7", "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.24.7.tgz", "integrity": "sha512-AAH4lEkpmzFWrGVlHaxJB7RLH21uPQ9+He+eFLWHmF9IuFQVugz8eAsamaW0DXRrTfco5zj1wWtpdcXJUOfsag==", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.24.7", "@babel/helper-validator-option": "^7.24.7", @@ -2428,9 +2580,10 @@ "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==" }, "node_modules/@babel/runtime": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.7.tgz", - "integrity": "sha512-UwgBRMjJP+xv857DCngvqXI3Iq6J4v0wXmwc6sapg+zyhbwmQX67LUEFrkK5tbyJ30jGuG3ZvWpBiB9LCy1kWw==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.0.tgz", + "integrity": "sha512-7dRy4DwXwtzBrPbZflqxnvfxLF8kdZXPkhymtDeFoFqE6ldzjQFgYTtYIFARcLEYDrqfBfYcZt1WqFxRoyC9Rw==", + "license": "MIT", "dependencies": { "regenerator-runtime": "^0.14.0" }, @@ -2451,31 +2604,30 @@ } }, "node_modules/@babel/template": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.7.tgz", - "integrity": "sha512-jYqfPrU9JTF0PmPy1tLYHW4Mp4KlgxJD9l2nP9fD6yT/ICi554DmrWBAEYpIelzjHf1msDP3PxJIRt/nFNfBig==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.0.tgz", + "integrity": "sha512-aOOgh1/5XzKvg1jvVz7AVrx2piJ2XBi227DHmbY6y+bM9H2FlN+IfecYu4Xl0cNiiVejlsCri89LUsbj8vJD9Q==", + "license": "MIT", "dependencies": { "@babel/code-frame": "^7.24.7", - "@babel/parser": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/parser": "^7.25.0", + "@babel/types": "^7.25.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.7.tgz", - "integrity": "sha512-yb65Ed5S/QAcewNPh0nZczy9JdYXkkAbIsEo+P7BE7yO3txAY30Y/oPa3QkQ5It3xVG2kpKMg9MsdxZaO31uKA==", + "version": "7.25.3", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.3.tgz", + "integrity": "sha512-HefgyP1x754oGCsKmV5reSmtV7IXj/kpaE1XYY+D9G5PvKKoFfSbiS4M77MdjuwlZKDIKFCffq9rPU+H/s3ZdQ==", + "license": "MIT", "dependencies": { "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.24.7", - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-function-name": "^7.24.7", - "@babel/helper-hoist-variables": "^7.24.7", - "@babel/helper-split-export-declaration": "^7.24.7", - "@babel/parser": "^7.24.7", - "@babel/types": "^7.24.7", + "@babel/generator": "^7.25.0", + "@babel/parser": "^7.25.3", + "@babel/template": "^7.25.0", + "@babel/types": "^7.25.2", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -2484,11 +2636,12 @@ } }, "node_modules/@babel/types": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.7.tgz", - "integrity": "sha512-XEFXSlxiG5td2EJRe8vOmRbaXVgfcBlszKujvVmWIK/UpywWljQCfzAv3RQCGujWQ1RD4YYWEAqDXfuJiy8f5Q==", + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.2.tgz", + "integrity": "sha512-YTnYtra7W9e6/oAZEHj0bJehPRUlLH9/fbpT5LfB0NhQXyALCRkRs3zH9v07IYhkgpqX6Z78FnuccZr/l4Fs4Q==", + "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.24.7", + "@babel/helper-string-parser": "^7.24.8", "@babel/helper-validator-identifier": "^7.24.7", "to-fast-properties": "^2.0.0" }, @@ -2686,6 +2839,7 @@ "engines": [ "node >=0.10.0" ], + "license": "MIT", "dependencies": { "uuid": "^8.0.0" }, @@ -2698,19 +2852,21 @@ "version": "8.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "license": "MIT", "bin": { "uuid": "dist/bin/uuid" } }, "node_modules/@expo/cli": { - "version": "0.18.19", - "resolved": "https://registry.npmjs.org/@expo/cli/-/cli-0.18.19.tgz", - "integrity": "sha512-8Rj18cTofpLl+7D++auMVS71KungldHbrArR44fpE8loMVAvYZA+U932lmd0K2lOYBASPhm7SVP9wzls//ESFQ==", + "version": "0.18.26", + "resolved": "https://registry.npmjs.org/@expo/cli/-/cli-0.18.26.tgz", + "integrity": "sha512-u9bTTXgcjaTloE9CHwxgrb8Me/Al4jiPykbVQpJydakH3GsIZfHy1zaLc7O39CoLjRz37WWi6Y5ZdgtQw9dCPQ==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.20.0", "@expo/code-signing-certificates": "0.0.5", "@expo/config": "~9.0.0-beta.0", - "@expo/config-plugins": "~8.0.0-beta.0", + "@expo/config-plugins": "~8.0.8", "@expo/devcert": "^1.0.0", "@expo/env": "~0.3.0", "@expo/image-utils": "^0.5.0", @@ -2719,11 +2875,11 @@ "@expo/osascript": "^2.0.31", "@expo/package-manager": "^1.5.0", "@expo/plist": "^0.1.0", - "@expo/prebuild-config": "7.0.6", + "@expo/prebuild-config": "7.0.8", "@expo/rudder-sdk-node": "1.1.1", "@expo/spawn-async": "^1.7.2", "@expo/xcpretty": "^4.3.0", - "@react-native/dev-middleware": "0.74.84", + "@react-native/dev-middleware": "0.74.85", "@urql/core": "2.3.6", "@urql/exchange-retry": "0.3.0", "accepts": "^1.3.8", @@ -2793,6 +2949,7 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "license": "MIT", "engines": { "node": ">=6" } @@ -2801,6 +2958,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -2811,18 +2969,11 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/@expo/cli/node_modules/bplist-creator": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/bplist-creator/-/bplist-creator-0.0.7.tgz", - "integrity": "sha512-xp/tcaV3T5PCiaY04mXga7o/TE+t95gqeLmADeBI1CvZtdWTbgBt3uLpvh4UWtenKeBhCV6oVxGk38yZr2uYEA==", - "dependencies": { - "stream-buffers": "~2.2.0" - } - }, "node_modules/@expo/cli/node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -2832,6 +2983,7 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -2847,6 +2999,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", "integrity": "sha512-8lgKz8LmCRYZZQDpRyT2m5rKJ08TnU4tR9FFFW2rxpxR1FzWi4PQ/NfyODchAatHaUgnSPVcx/R5w6NuTBzFiw==", + "license": "MIT", "dependencies": { "restore-cursor": "^2.0.0" }, @@ -2858,6 +3011,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, @@ -2868,12 +3022,14 @@ "node_modules/@expo/cli/node_modules/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==" + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" }, "node_modules/@expo/cli/node_modules/escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "license": "MIT", "engines": { "node": ">=0.8.0" } @@ -2882,6 +3038,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", @@ -2895,6 +3052,7 @@ "version": "8.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "license": "MIT", "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^4.0.0", @@ -2908,6 +3066,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", "engines": { "node": ">=8" } @@ -2916,6 +3075,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "license": "MIT", "optionalDependencies": { "graceful-fs": "^4.1.6" } @@ -2924,6 +3084,7 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz", "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==", + "license": "MIT", "dependencies": { "chalk": "^2.0.1" }, @@ -2935,6 +3096,7 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "license": "MIT", "dependencies": { "color-convert": "^1.9.0" }, @@ -2946,6 +3108,7 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "license": "MIT", "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -2959,6 +3122,7 @@ "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "license": "MIT", "dependencies": { "color-name": "1.1.3" } @@ -2966,12 +3130,14 @@ "node_modules/@expo/cli/node_modules/log-symbols/node_modules/color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "license": "MIT" }, "node_modules/@expo/cli/node_modules/log-symbols/node_modules/has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "license": "MIT", "engines": { "node": ">=4" } @@ -2980,6 +3146,7 @@ "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "license": "MIT", "dependencies": { "has-flag": "^3.0.0" }, @@ -2991,6 +3158,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", + "license": "MIT", "engines": { "node": ">=4" } @@ -2999,6 +3167,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -3010,6 +3179,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", "integrity": "sha512-oyyPpiMaKARvvcgip+JV+7zci5L8D1W9RZIz2l1o08AM3pfspitVWnPt3mzHcBPp12oYMTy0pqrFs/C+m3EwsQ==", + "license": "MIT", "dependencies": { "mimic-fn": "^1.0.0" }, @@ -3021,6 +3191,7 @@ "version": "8.4.2", "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", + "license": "MIT", "dependencies": { "define-lazy-prop": "^2.0.0", "is-docker": "^2.1.1", @@ -3037,6 +3208,7 @@ "version": "3.4.0", "resolved": "https://registry.npmjs.org/ora/-/ora-3.4.0.tgz", "integrity": "sha512-eNwHudNbO1folBP3JsZ19v9azXWtQZjICdr3Q0TDPIaeBQ3mXLrh54wM+er0+hSp+dWKf+Z8KM58CYzEyIYxYg==", + "license": "MIT", "dependencies": { "chalk": "^2.4.2", "cli-cursor": "^2.1.0", @@ -3053,6 +3225,7 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "license": "MIT", "dependencies": { "color-convert": "^1.9.0" }, @@ -3064,6 +3237,7 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "license": "MIT", "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -3077,6 +3251,7 @@ "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "license": "MIT", "dependencies": { "color-name": "1.1.3" } @@ -3084,12 +3259,14 @@ "node_modules/@expo/cli/node_modules/ora/node_modules/color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "license": "MIT" }, "node_modules/@expo/cli/node_modules/ora/node_modules/has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "license": "MIT", "engines": { "node": ">=4" } @@ -3098,6 +3275,7 @@ "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "license": "MIT", "dependencies": { "has-flag": "^3.0.0" }, @@ -3109,6 +3287,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-3.0.1.tgz", "integrity": "sha512-I3EurrIQMlRc9IaAZnqRR044Phh2DXY+55o7uJ0V+hYZAcQYSuFWsc9q5PvyDHUSCe1Qxn/iBz+78s86zWnGag==", + "license": "MIT", "engines": { "node": ">=10" }, @@ -3120,6 +3299,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", "integrity": "sha512-6IzJLuGi4+R14vwagDHX+JrXmPVtPpn4mffDJ1UdR7/Edm87fl6yi8mMBIVvFtJaNTUvjughmW4hwLhRG7gC1Q==", + "license": "MIT", "dependencies": { "onetime": "^2.0.0", "signal-exit": "^3.0.2" @@ -3132,6 +3312,7 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "license": "MIT", "dependencies": { "ansi-regex": "^4.1.0" }, @@ -3143,6 +3324,7 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -3154,14 +3336,16 @@ "version": "0.1.2", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "license": "MIT", "engines": { "node": ">= 4.0.0" } }, "node_modules/@expo/cli/node_modules/ws": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", - "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", + "license": "MIT", "engines": { "node": ">=10.0.0" }, @@ -3182,18 +3366,20 @@ "version": "0.0.5", "resolved": "https://registry.npmjs.org/@expo/code-signing-certificates/-/code-signing-certificates-0.0.5.tgz", "integrity": "sha512-BNhXkY1bblxKZpltzAx98G2Egj9g1Q+JRcvR7E99DOj862FTCX+ZPsAUtPTr7aHxwtrL7+fL3r0JSmM9kBm+Bw==", + "license": "MIT", "dependencies": { "node-forge": "^1.2.1", "nullthrows": "^1.1.1" } }, "node_modules/@expo/config": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/@expo/config/-/config-9.0.2.tgz", - "integrity": "sha512-BKQ4/qBf3OLT8hHp5kjObk2vxwoRQ1yYQBbG/OM9Jdz32yYtrU8opTbKRAxfZEWH5i3ZHdLrPdC1rO0I6WxtTw==", + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/@expo/config/-/config-9.0.3.tgz", + "integrity": "sha512-eOTNM8eOC8gZNHgenySRlc/lwmYY1NOgvjwA8LHuvPT7/eUwD93zrxu3lPD1Cc/P6C/2BcVdfH4hf0tLmDxnsg==", + "license": "MIT", "dependencies": { "@babel/code-frame": "~7.10.4", - "@expo/config-plugins": "~8.0.0", + "@expo/config-plugins": "~8.0.8", "@expo/config-types": "^51.0.0-unreleased", "@expo/json-file": "^8.3.0", "getenv": "^1.0.0", @@ -3206,9 +3392,10 @@ } }, "node_modules/@expo/config-plugins": { - "version": "8.0.5", - "resolved": "https://registry.npmjs.org/@expo/config-plugins/-/config-plugins-8.0.5.tgz", - "integrity": "sha512-VGseKX1dYvaf2qHUDGzIQwSOJrO5fomH0gE5cKSQyi6wn+Q6rcV2Dj2E5aga+9aKNPL6FxZ0dqRFC3t2sbhaSA==", + "version": "8.0.8", + "resolved": "https://registry.npmjs.org/@expo/config-plugins/-/config-plugins-8.0.8.tgz", + "integrity": "sha512-Fvu6IO13EUw0R9WeqxUO37FkM62YJBNcZb9DyJAOgMz7Ez/vaKQGEjKt9cwT+Q6uirtCATMgaq6VWAW7YW8xXw==", + "license": "MIT", "dependencies": { "@expo/config-types": "^51.0.0-unreleased", "@expo/json-file": "~8.3.0", @@ -3231,6 +3418,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -3245,6 +3433,7 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -3254,6 +3443,7 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -3269,6 +3459,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, @@ -3279,12 +3470,14 @@ "node_modules/@expo/config-plugins/node_modules/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==" + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" }, "node_modules/@expo/config-plugins/node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "license": "MIT", "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" @@ -3300,6 +3493,8 @@ "version": "7.1.6", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "license": "ISC", "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -3319,6 +3514,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", "engines": { "node": ">=8" } @@ -3327,6 +3523,7 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "license": "MIT", "dependencies": { "p-locate": "^5.0.0" }, @@ -3341,6 +3538,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -3352,6 +3550,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "license": "MIT", "dependencies": { "p-limit": "^3.0.2" }, @@ -3366,6 +3565,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "license": "MIT", "engines": { "node": ">=8" } @@ -3374,6 +3574,7 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -3382,9 +3583,10 @@ } }, "node_modules/@expo/config-types": { - "version": "51.0.0", - "resolved": "https://registry.npmjs.org/@expo/config-types/-/config-types-51.0.0.tgz", - "integrity": "sha512-acn03/u8mQvBhdTQtA7CNhevMltUhbSrpI01FYBJwpVntufkU++ncQujWKlgY/OwIajcfygk1AY4xcNZ5ImkRA==" + "version": "51.0.2", + "resolved": "https://registry.npmjs.org/@expo/config-types/-/config-types-51.0.2.tgz", + "integrity": "sha512-IglkIoiDwJMY01lYkF/ZSBoe/5cR+O3+Gx6fpLFjLfgZGBTdyPkKa1g8NWoWQCk+D3cKL2MDbszT2DyRRB0YqQ==", + "license": "MIT" }, "node_modules/@expo/config/node_modules/@babel/code-frame": { "version": "7.10.4", @@ -3437,6 +3639,7 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/@expo/devcert/-/devcert-1.1.2.tgz", "integrity": "sha512-FyWghLu7rUaZEZSTLt/XNRukm0c9GFfwP0iFaswoDWpV6alvVg+zRAfCLdIVQEz1SVcQ3zo1hMZFDrnKGvkCuQ==", + "license": "MIT", "dependencies": { "application-config-path": "^0.1.0", "command-exists": "^1.2.4", @@ -3457,6 +3660,7 @@ "version": "3.2.7", "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "license": "MIT", "dependencies": { "ms": "^2.1.1" } @@ -3466,6 +3670,7 @@ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", "deprecated": "Rimraf versions prior to v4 are no longer supported", + "license": "ISC", "dependencies": { "glob": "^7.1.3" }, @@ -3476,12 +3681,14 @@ "node_modules/@expo/devcert/node_modules/sudo-prompt": { "version": "8.2.5", "resolved": "https://registry.npmjs.org/sudo-prompt/-/sudo-prompt-8.2.5.tgz", - "integrity": "sha512-rlBo3HU/1zAJUrkY6jNxDOC9eVYliG6nS4JA8u8KAshITd07tafMc/Br7xQwCSseXwJ2iCcHCE8SNWX3q8Z+kw==" + "integrity": "sha512-rlBo3HU/1zAJUrkY6jNxDOC9eVYliG6nS4JA8u8KAshITd07tafMc/Br7xQwCSseXwJ2iCcHCE8SNWX3q8Z+kw==", + "license": "MIT" }, "node_modules/@expo/env": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/@expo/env/-/env-0.3.0.tgz", "integrity": "sha512-OtB9XVHWaXidLbHvrVDeeXa09yvTl3+IQN884sO6PhIi2/StXfgSH/9zC7IvzrDB8kW3EBJ1PPLuCUJ2hxAT7Q==", + "license": "MIT", "dependencies": { "chalk": "^4.0.0", "debug": "^4.3.4", @@ -3494,6 +3701,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -3508,6 +3716,7 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -3523,6 +3732,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, @@ -3533,12 +3743,14 @@ "node_modules/@expo/env/node_modules/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==" + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" }, "node_modules/@expo/env/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", "engines": { "node": ">=8" } @@ -3547,6 +3759,7 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -3558,6 +3771,7 @@ "version": "0.5.1", "resolved": "https://registry.npmjs.org/@expo/image-utils/-/image-utils-0.5.1.tgz", "integrity": "sha512-U/GsFfFox88lXULmFJ9Shfl2aQGcwoKPF7fawSCLixIKtMCpsI+1r0h+5i0nQnmt9tHuzXZDL8+Dg1z6OhkI9A==", + "license": "MIT", "dependencies": { "@expo/spawn-async": "^1.7.2", "chalk": "^4.0.0", @@ -3575,6 +3789,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -3589,6 +3804,7 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -3604,6 +3820,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, @@ -3614,12 +3831,14 @@ "node_modules/@expo/image-utils/node_modules/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==" + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" }, "node_modules/@expo/image-utils/node_modules/crypto-random-string": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-1.0.0.tgz", "integrity": "sha512-GsVpkFPlycH7/fRR7Dhcmnoii54gV1nz7y4CWyeFS14N+JVBBhY+r8amRHE4BwSYal7BPTDp8isvAlCxyFt3Hg==", + "license": "MIT", "engines": { "node": ">=4" } @@ -3628,6 +3847,7 @@ "version": "9.0.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.0.0.tgz", "integrity": "sha512-pmEYSk3vYsG/bF651KPUXZ+hvjpgWYw/Gc7W9NFUe3ZVLczKKWIij3IKpOrQcdw4TILtibFslZ0UmR8Vvzig4g==", + "license": "MIT", "dependencies": { "at-least-node": "^1.0.0", "graceful-fs": "^4.2.0", @@ -3642,6 +3862,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", "engines": { "node": ">=8" } @@ -3650,6 +3871,7 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -3661,6 +3883,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-1.0.0.tgz", "integrity": "sha512-xZFXEGbG7SNC3itwBzI3RYjq/cEhBkx2hJuKGIUOcEULmkQExXiHat2z/qkISYsuR+IKumhEfKKbV5qXmhICFQ==", + "license": "MIT", "engines": { "node": ">=4" } @@ -3669,6 +3892,7 @@ "version": "0.3.0", "resolved": "https://registry.npmjs.org/tempy/-/tempy-0.3.0.tgz", "integrity": "sha512-WrH/pui8YCwmeiAoxV+lpRH9HpRtgBhSR2ViBPgpGb/wnYDzp21R4MN45fsCGvLROvY67o3byhJRYRONJyImVQ==", + "license": "MIT", "dependencies": { "temp-dir": "^1.0.0", "type-fest": "^0.3.1", @@ -3682,6 +3906,7 @@ "version": "0.3.1", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.3.1.tgz", "integrity": "sha512-cUGJnCdr4STbePCgqNFbpVNCepa+kAVohJs1sLhxzdH+gnEoOd8VhbYa7pD3zZYGiURWM2xzEII3fQcRizDkYQ==", + "license": "(MIT OR CC0-1.0)", "engines": { "node": ">=6" } @@ -3690,6 +3915,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-1.0.0.tgz", "integrity": "sha512-ODgiYu03y5g76A1I9Gt0/chLCzQjvzDy7DsZGsLOE/1MrF6wriEskSncj1+/C58Xk/kPZDppSctDybCwOSaGAg==", + "license": "MIT", "dependencies": { "crypto-random-string": "^1.0.0" }, @@ -3701,6 +3927,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/universalify/-/universalify-1.0.0.tgz", "integrity": "sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug==", + "license": "MIT", "engines": { "node": ">= 10.0.0" } @@ -3734,9 +3961,10 @@ } }, "node_modules/@expo/metro-config": { - "version": "0.18.7", - "resolved": "https://registry.npmjs.org/@expo/metro-config/-/metro-config-0.18.7.tgz", - "integrity": "sha512-MzAyFP0fvoyj9IUc6SPnpy6/HLT23j/p5J+yWjGug2ddOpSuKNDHOOqlwWZbJp5KfZCEIVWNHeUoE+TaC/yhaQ==", + "version": "0.18.10", + "resolved": "https://registry.npmjs.org/@expo/metro-config/-/metro-config-0.18.10.tgz", + "integrity": "sha512-HTYQqKfV0JSuRp5aDvrPHezj5udXOWoXqHOjfTSnce2m13j6D0yYXTJNaKRhlgpPBrkg5DL7z1fL3zwDUpLM4w==", + "license": "MIT", "dependencies": { "@babel/core": "^7.20.0", "@babel/generator": "^7.20.5", @@ -3762,6 +3990,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -3776,6 +4005,7 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -3791,6 +4021,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, @@ -3801,12 +4032,14 @@ "node_modules/@expo/metro-config/node_modules/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==" + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" }, "node_modules/@expo/metro-config/node_modules/fs-extra": { "version": "9.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "license": "MIT", "dependencies": { "at-least-node": "^1.0.0", "graceful-fs": "^4.2.0", @@ -3821,6 +4054,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", "engines": { "node": ">=8" } @@ -3829,6 +4063,7 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -3840,6 +4075,7 @@ "version": "2.1.3", "resolved": "https://registry.npmjs.org/@expo/osascript/-/osascript-2.1.3.tgz", "integrity": "sha512-aOEkhPzDsaAfolSswObGiYW0Pf0ROfR9J2NBRLQACdQ6uJlyAMiPF45DVEVknAU9juKh0y8ZyvC9LXqLEJYohA==", + "license": "MIT", "dependencies": { "@expo/spawn-async": "^1.7.2", "exec-async": "^2.2.0" @@ -3852,6 +4088,7 @@ "version": "1.5.2", "resolved": "https://registry.npmjs.org/@expo/package-manager/-/package-manager-1.5.2.tgz", "integrity": "sha512-IuA9XtGBilce0q8cyxtWINqbzMB1Fia0Yrug/O53HNuRSwQguV/iqjV68bsa4z8mYerePhcFgtvISWLAlNEbUA==", + "license": "MIT", "dependencies": { "@expo/json-file": "^8.3.0", "@expo/spawn-async": "^1.7.2", @@ -3871,6 +4108,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -3885,6 +4123,7 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -3900,6 +4139,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", "integrity": "sha512-8lgKz8LmCRYZZQDpRyT2m5rKJ08TnU4tR9FFFW2rxpxR1FzWi4PQ/NfyODchAatHaUgnSPVcx/R5w6NuTBzFiw==", + "license": "MIT", "dependencies": { "restore-cursor": "^2.0.0" }, @@ -3911,6 +4151,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, @@ -3921,12 +4162,14 @@ "node_modules/@expo/package-manager/node_modules/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==" + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" }, "node_modules/@expo/package-manager/node_modules/escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "license": "MIT", "engines": { "node": ">=0.8.0" } @@ -3935,6 +4178,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "license": "MIT", "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" @@ -3950,6 +4194,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", "engines": { "node": ">=8" } @@ -3958,6 +4203,7 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "license": "MIT", "dependencies": { "p-locate": "^5.0.0" }, @@ -3972,6 +4218,7 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz", "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==", + "license": "MIT", "dependencies": { "chalk": "^2.0.1" }, @@ -3983,6 +4230,7 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "license": "MIT", "dependencies": { "color-convert": "^1.9.0" }, @@ -3994,6 +4242,7 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "license": "MIT", "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -4007,6 +4256,7 @@ "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "license": "MIT", "dependencies": { "color-name": "1.1.3" } @@ -4014,12 +4264,14 @@ "node_modules/@expo/package-manager/node_modules/log-symbols/node_modules/color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "license": "MIT" }, "node_modules/@expo/package-manager/node_modules/log-symbols/node_modules/has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "license": "MIT", "engines": { "node": ">=4" } @@ -4028,6 +4280,7 @@ "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "license": "MIT", "dependencies": { "has-flag": "^3.0.0" }, @@ -4039,6 +4292,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", + "license": "MIT", "engines": { "node": ">=4" } @@ -4047,6 +4301,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", "integrity": "sha512-oyyPpiMaKARvvcgip+JV+7zci5L8D1W9RZIz2l1o08AM3pfspitVWnPt3mzHcBPp12oYMTy0pqrFs/C+m3EwsQ==", + "license": "MIT", "dependencies": { "mimic-fn": "^1.0.0" }, @@ -4058,6 +4313,7 @@ "version": "3.4.0", "resolved": "https://registry.npmjs.org/ora/-/ora-3.4.0.tgz", "integrity": "sha512-eNwHudNbO1folBP3JsZ19v9azXWtQZjICdr3Q0TDPIaeBQ3mXLrh54wM+er0+hSp+dWKf+Z8KM58CYzEyIYxYg==", + "license": "MIT", "dependencies": { "chalk": "^2.4.2", "cli-cursor": "^2.1.0", @@ -4074,6 +4330,7 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "license": "MIT", "dependencies": { "color-convert": "^1.9.0" }, @@ -4085,6 +4342,7 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "license": "MIT", "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -4098,6 +4356,7 @@ "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "license": "MIT", "dependencies": { "color-name": "1.1.3" } @@ -4105,12 +4364,14 @@ "node_modules/@expo/package-manager/node_modules/ora/node_modules/color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "license": "MIT" }, "node_modules/@expo/package-manager/node_modules/ora/node_modules/has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "license": "MIT", "engines": { "node": ">=4" } @@ -4119,6 +4380,7 @@ "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "license": "MIT", "dependencies": { "has-flag": "^3.0.0" }, @@ -4130,6 +4392,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "license": "MIT", "dependencies": { "p-limit": "^3.0.2" }, @@ -4144,6 +4407,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", "integrity": "sha512-6IzJLuGi4+R14vwagDHX+JrXmPVtPpn4mffDJ1UdR7/Edm87fl6yi8mMBIVvFtJaNTUvjughmW4hwLhRG7gC1Q==", + "license": "MIT", "dependencies": { "onetime": "^2.0.0", "signal-exit": "^3.0.2" @@ -4156,6 +4420,7 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "license": "MIT", "dependencies": { "ansi-regex": "^4.1.0" }, @@ -4167,6 +4432,7 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "license": "MIT", "engines": { "node": ">=6" } @@ -4175,6 +4441,7 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -4193,16 +4460,17 @@ } }, "node_modules/@expo/prebuild-config": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/@expo/prebuild-config/-/prebuild-config-7.0.6.tgz", - "integrity": "sha512-Hts+iGBaG6OQ+N8IEMMgwQElzJeSTb7iUJ26xADEHkaexsucAK+V52dM8M4ceicvbZR9q8M+ebJEGj0MCNA3dQ==", + "version": "7.0.8", + "resolved": "https://registry.npmjs.org/@expo/prebuild-config/-/prebuild-config-7.0.8.tgz", + "integrity": "sha512-wH9NVg6HiwF5y9x0TxiMEeBF+ITPGDXy5/i6OUheSrKpPgb0lF1Mwzl/f2fLPXBEpl+ZXOQ8LlLW32b7K9lrNg==", + "license": "MIT", "dependencies": { "@expo/config": "~9.0.0-beta.0", - "@expo/config-plugins": "~8.0.0-beta.0", + "@expo/config-plugins": "~8.0.8", "@expo/config-types": "^51.0.0-unreleased", "@expo/image-utils": "^0.5.0", "@expo/json-file": "^8.3.0", - "@react-native/normalize-colors": "0.74.84", + "@react-native/normalize-colors": "0.74.85", "debug": "^4.3.1", "fs-extra": "^9.0.0", "resolve-from": "^5.0.0", @@ -4217,6 +4485,7 @@ "version": "9.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "license": "MIT", "dependencies": { "at-least-node": "^1.0.0", "graceful-fs": "^4.2.0", @@ -4231,6 +4500,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/@expo/rudder-sdk-node/-/rudder-sdk-node-1.1.1.tgz", "integrity": "sha512-uy/hS/awclDJ1S88w9UGpc6Nm9XnNUjzOAAib1A3PVAnGQIwebg8DpFqOthFBTlZxeuV/BKbZ5jmTbtNZkp1WQ==", + "license": "MIT", "dependencies": { "@expo/bunyan": "^4.0.0", "@segment/loosely-validate-event": "^2.0.0", @@ -4248,6 +4518,7 @@ "version": "8.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "license": "MIT", "bin": { "uuid": "dist/bin/uuid" } @@ -4255,12 +4526,14 @@ "node_modules/@expo/sdk-runtime-versions": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@expo/sdk-runtime-versions/-/sdk-runtime-versions-1.0.0.tgz", - "integrity": "sha512-Doz2bfiPndXYFPMRwPyGa1k5QaKDVpY806UJj570epIiMzWaYyCtobasyfC++qfIXVb5Ocy7r3tP9d62hAQ7IQ==" + "integrity": "sha512-Doz2bfiPndXYFPMRwPyGa1k5QaKDVpY806UJj570epIiMzWaYyCtobasyfC++qfIXVb5Ocy7r3tP9d62hAQ7IQ==", + "license": "MIT" }, "node_modules/@expo/spawn-async": { "version": "1.7.2", "resolved": "https://registry.npmjs.org/@expo/spawn-async/-/spawn-async-1.7.2.tgz", "integrity": "sha512-QdWi16+CHB9JYP7gma19OVVg0BFkvU8zNj9GjWorYI8Iv8FUxjOCcYRuAmX4s/h91e4e7BPsskc8cSrZYho9Ew==", + "license": "MIT", "dependencies": { "cross-spawn": "^7.0.3" }, @@ -4272,6 +4545,7 @@ "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "license": "MIT", "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -4293,6 +4567,7 @@ "version": "4.3.1", "resolved": "https://registry.npmjs.org/@expo/xcpretty/-/xcpretty-4.3.1.tgz", "integrity": "sha512-sqXgo1SCv+j4VtYEwl/bukuOIBrVgx6euIoCat3Iyx5oeoXwEA2USCoeL0IPubflMxncA2INkqJ/Wr3NGrSgzw==", + "license": "BSD-3-Clause", "dependencies": { "@babel/code-frame": "7.10.4", "chalk": "^4.1.0", @@ -4307,6 +4582,7 @@ "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", + "license": "MIT", "dependencies": { "@babel/highlight": "^7.10.4" } @@ -4315,6 +4591,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -4328,12 +4605,14 @@ "node_modules/@expo/xcpretty/node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "license": "Python-2.0" }, "node_modules/@expo/xcpretty/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -4349,6 +4628,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, @@ -4359,12 +4639,14 @@ "node_modules/@expo/xcpretty/node_modules/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==" + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" }, "node_modules/@expo/xcpretty/node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "license": "MIT", "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" @@ -4380,6 +4662,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", "engines": { "node": ">=8" } @@ -4388,6 +4671,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "license": "MIT", "dependencies": { "argparse": "^2.0.1" }, @@ -4399,6 +4683,7 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "license": "MIT", "dependencies": { "p-locate": "^5.0.0" }, @@ -4413,6 +4698,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "license": "MIT", "dependencies": { "p-limit": "^3.0.2" }, @@ -4427,6 +4713,7 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -4438,7 +4725,8 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/@flatten-js/interval-tree/-/interval-tree-1.1.3.tgz", "integrity": "sha512-xhFWUBoHJFF77cJO1D6REjdgJEMRf2Y2Z+eKEPav8evGKcLSnj1ud5pLXQSbGuxF3VSvT1rWhMfVpXEKJLTL+A==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@formatjs/ecma402-abstract": { "version": "2.0.0", @@ -4584,9 +4872,10 @@ } }, "node_modules/@gorhom/bottom-sheet": { - "version": "4.6.3", - "resolved": "https://registry.npmjs.org/@gorhom/bottom-sheet/-/bottom-sheet-4.6.3.tgz", - "integrity": "sha512-fSuSfbtoKsjmSeyz+tG2C0GtcEL7PS63iEXI23c9M+HeCT1IFK6ffmIa2pqyqB43L1jtkR+BWkpZwqXnN4H8xA==", + "version": "4.6.4", + "resolved": "https://registry.npmjs.org/@gorhom/bottom-sheet/-/bottom-sheet-4.6.4.tgz", + "integrity": "sha512-0itLMblLBvepE065w3a60S030c2rNUsGshPC7wbWDm31VyqoaU2xjzh/ojH62YIJOcobBr5QoC30IxBBKDGovQ==", + "license": "MIT", "dependencies": { "@gorhom/portal": "1.0.14", "invariant": "^2.2.4" @@ -4624,6 +4913,7 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.2.0.tgz", "integrity": "sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==", + "license": "MIT", "peerDependencies": { "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } @@ -4700,6 +4990,7 @@ "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "license": "ISC", "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", @@ -4716,6 +5007,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "license": "MIT", "engines": { "node": ">=12" }, @@ -4727,6 +5019,7 @@ "version": "6.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "license": "MIT", "engines": { "node": ">=12" }, @@ -4737,12 +5030,14 @@ "node_modules/@isaacs/cliui/node_modules/emoji-regex": { "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "license": "MIT" }, "node_modules/@isaacs/cliui/node_modules/string-width": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "license": "MIT", "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", @@ -4759,6 +5054,7 @@ "version": "7.1.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "license": "MIT", "dependencies": { "ansi-regex": "^6.0.1" }, @@ -4773,6 +5069,7 @@ "version": "8.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "license": "MIT", "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", @@ -5561,10 +5858,10 @@ } }, "node_modules/@mattermost/calls": { - "name": "@calls/common", - "version": "0.14.0", - "resolved": "git+ssh://git@github.com/mattermost/calls-common.git#cc5e65f6c103138fd013d6fa45eb33bfe7544f2a", - "integrity": "sha512-fWCVnjuU3LQ8RO35uI9Qypi7HSVWoALcbNIotNcWd52XQ+BLgt69nSpKRli9qbvj4d9W/UgPaT3bOsyBvIXQrA==" + "name": "@mattermost/calls-common", + "version": "0.27.2", + "resolved": "git+ssh://git@github.com/mattermost/calls-common.git#1ce6defb1ee0c1e0f106ddff8f46c37d10d60b76", + "integrity": "sha512-VeX0GT1g8bl7AqG5TJEnbkTTVbc+CqZttpPBKYDkMJd86CGjtURc4o83OUu3TNsEI3opSpJfbetScUTG9TWNLw==" }, "node_modules/@mattermost/commonmark": { "version": "0.30.1-2", @@ -5608,9 +5905,10 @@ } }, "node_modules/@mattermost/react-native-network-client": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@mattermost/react-native-network-client/-/react-native-network-client-1.6.0.tgz", - "integrity": "sha512-SUXmF0sF4iyAgVovK8VYtUCF38MchAI89GwrvhkvaB+MQKMruDsqaKBLiwW4Rs6jq4dVbmTLV/7FTP0akTiyRQ==", + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@mattermost/react-native-network-client/-/react-native-network-client-1.7.2.tgz", + "integrity": "sha512-UaFhUaQkmb6Hq2LOyutiwL0HeMVHmhBBpgojLi83zXXXB5OgNWDgqbDBo6Y986c5TwhCy1/LxENdHBc+mI4RZA==", + "license": "MIT", "dependencies": { "validator": "13.12.0", "zod": "3.23.8" @@ -5785,6 +6083,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-3.1.1.tgz", "integrity": "sha512-q9CRWjpHCMIh5sVyefoD1cA7PkvILqCZsnSOEUUivORLjxCO/Irmue2DprETiNgEqktDBZaM1Bi+jrarx1XdCg==", + "license": "ISC", "dependencies": { "semver": "^7.3.5" }, @@ -5881,15 +6180,17 @@ "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "license": "MIT", "optional": true, "engines": { "node": ">=14" } }, "node_modules/@react-native-camera-roll/camera-roll": { - "version": "7.8.1", - "resolved": "https://registry.npmjs.org/@react-native-camera-roll/camera-roll/-/camera-roll-7.8.1.tgz", - "integrity": "sha512-voVmDlzBS1MVADFk/Vb9GzWzhpoF4ESSjU9qwDNxf/ZA9tirAzX/M0TKcz2V+iFHeRR68cHtZ9tHUhQVG7kJcA==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@react-native-camera-roll/camera-roll/-/camera-roll-7.8.3.tgz", + "integrity": "sha512-F4xNVg/cqN+MiAhoqC11/RafIVQCzLxJVMYke1eBkqLmP5aJ+fKfPPClnRcvXfWHyXOuM17og45LP0S4H09wnQ==", + "license": "MIT", "engines": { "node": ">= 18.17.0" }, @@ -5912,18 +6213,19 @@ } }, "node_modules/@react-native-community/cli": { - "version": "13.6.8", - "resolved": "https://registry.npmjs.org/@react-native-community/cli/-/cli-13.6.8.tgz", - "integrity": "sha512-0lRdgLNaXixWY4BfFRl1J6Ao9Lapo2z+++iE7TD4GAbuxOWJSyFi+KUA8XNfSDyML4jFO02MZgyBPxAWdaminQ==", - "dependencies": { - "@react-native-community/cli-clean": "13.6.8", - "@react-native-community/cli-config": "13.6.8", - "@react-native-community/cli-debugger-ui": "13.6.8", - "@react-native-community/cli-doctor": "13.6.8", - "@react-native-community/cli-hermes": "13.6.8", - "@react-native-community/cli-server-api": "13.6.8", - "@react-native-community/cli-tools": "13.6.8", - "@react-native-community/cli-types": "13.6.8", + "version": "13.6.9", + "resolved": "https://registry.npmjs.org/@react-native-community/cli/-/cli-13.6.9.tgz", + "integrity": "sha512-hFJL4cgLPxncJJd/epQ4dHnMg5Jy/7Q56jFvA3MHViuKpzzfTCJCB+pGY54maZbtym53UJON9WTGpM3S81UfjQ==", + "license": "MIT", + "dependencies": { + "@react-native-community/cli-clean": "13.6.9", + "@react-native-community/cli-config": "13.6.9", + "@react-native-community/cli-debugger-ui": "13.6.9", + "@react-native-community/cli-doctor": "13.6.9", + "@react-native-community/cli-hermes": "13.6.9", + "@react-native-community/cli-server-api": "13.6.9", + "@react-native-community/cli-tools": "13.6.9", + "@react-native-community/cli-types": "13.6.9", "chalk": "^4.1.2", "commander": "^9.4.1", "deepmerge": "^4.3.0", @@ -5942,11 +6244,12 @@ } }, "node_modules/@react-native-community/cli-clean": { - "version": "13.6.8", - "resolved": "https://registry.npmjs.org/@react-native-community/cli-clean/-/cli-clean-13.6.8.tgz", - "integrity": "sha512-B1uxlm1N4BQuWFvBL3yRl3LVvydjswsdbTi7tMrHMtSxfRio1p9HjcmDzlzKco09Y+8qBGgakm3jcMZGLbhXQQ==", + "version": "13.6.9", + "resolved": "https://registry.npmjs.org/@react-native-community/cli-clean/-/cli-clean-13.6.9.tgz", + "integrity": "sha512-7Dj5+4p9JggxuVNOjPbduZBAP1SUgNhLKVw5noBUzT/3ZpUZkDM+RCSwyoyg8xKWoE4OrdUAXwAFlMcFDPKykA==", + "license": "MIT", "dependencies": { - "@react-native-community/cli-tools": "13.6.8", + "@react-native-community/cli-tools": "13.6.9", "chalk": "^4.1.2", "execa": "^5.0.0", "fast-glob": "^3.3.2" @@ -5956,6 +6259,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -5970,6 +6274,7 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -5985,6 +6290,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, @@ -5995,12 +6301,14 @@ "node_modules/@react-native-community/cli-clean/node_modules/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==" + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" }, "node_modules/@react-native-community/cli-clean/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", "engines": { "node": ">=8" } @@ -6009,6 +6317,7 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -6017,11 +6326,12 @@ } }, "node_modules/@react-native-community/cli-config": { - "version": "13.6.8", - "resolved": "https://registry.npmjs.org/@react-native-community/cli-config/-/cli-config-13.6.8.tgz", - "integrity": "sha512-RabCkIsWdP4Ex/sf1uSP9qxc30utm+0uIJAjrZkNQynm7T4Lyqn/kT3LKm4yM6M0Qk61YxGguiaXF4601vAduw==", + "version": "13.6.9", + "resolved": "https://registry.npmjs.org/@react-native-community/cli-config/-/cli-config-13.6.9.tgz", + "integrity": "sha512-rFfVBcNojcMm+KKHE/xqpqXg8HoKl4EC7bFHUrahMJ+y/tZll55+oX/PGG37rzB8QzP2UbMQ19DYQKC1G7kXeg==", + "license": "MIT", "dependencies": { - "@react-native-community/cli-tools": "13.6.8", + "@react-native-community/cli-tools": "13.6.9", "chalk": "^4.1.2", "cosmiconfig": "^5.1.0", "deepmerge": "^4.3.0", @@ -6033,6 +6343,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -6047,6 +6358,7 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -6062,6 +6374,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, @@ -6072,12 +6385,14 @@ "node_modules/@react-native-community/cli-config/node_modules/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==" + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" }, "node_modules/@react-native-community/cli-config/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", "engines": { "node": ">=8" } @@ -6086,6 +6401,7 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -6094,23 +6410,25 @@ } }, "node_modules/@react-native-community/cli-debugger-ui": { - "version": "13.6.8", - "resolved": "https://registry.npmjs.org/@react-native-community/cli-debugger-ui/-/cli-debugger-ui-13.6.8.tgz", - "integrity": "sha512-2cS+MX/Su6sVSjqpDftFOXbK7EuPg98xzsPkdPhkQnkZwvXqodK9CAMuDMbx3lBHHtrPrpMbBCpFmPN8iVOnlA==", + "version": "13.6.9", + "resolved": "https://registry.npmjs.org/@react-native-community/cli-debugger-ui/-/cli-debugger-ui-13.6.9.tgz", + "integrity": "sha512-TkN7IdFmGPPvTpAo3nCAH9uwGCPxWBEAwpqEZDrq0NWllI7Tdie8vDpGdrcuCcKalmhq6OYnkXzeBah7O1Ztpw==", + "license": "MIT", "dependencies": { "serve-static": "^1.13.1" } }, "node_modules/@react-native-community/cli-doctor": { - "version": "13.6.8", - "resolved": "https://registry.npmjs.org/@react-native-community/cli-doctor/-/cli-doctor-13.6.8.tgz", - "integrity": "sha512-/3Vdy9J3hyiu0y3nd/CU3kBqPlTRxnLXg7V6jrA1jbTOlZAMyV9imEkrqEaGK0SMOyMhh9Pipf98Ozhk0Nl4QA==", - "dependencies": { - "@react-native-community/cli-config": "13.6.8", - "@react-native-community/cli-platform-android": "13.6.8", - "@react-native-community/cli-platform-apple": "13.6.8", - "@react-native-community/cli-platform-ios": "13.6.8", - "@react-native-community/cli-tools": "13.6.8", + "version": "13.6.9", + "resolved": "https://registry.npmjs.org/@react-native-community/cli-doctor/-/cli-doctor-13.6.9.tgz", + "integrity": "sha512-5quFaLdWFQB+677GXh5dGU9I5eg2z6Vg4jOX9vKnc9IffwyIFAyJfCZHrxLSRPDGNXD7biDQUdoezXYGwb6P/A==", + "license": "MIT", + "dependencies": { + "@react-native-community/cli-config": "13.6.9", + "@react-native-community/cli-platform-android": "13.6.9", + "@react-native-community/cli-platform-apple": "13.6.9", + "@react-native-community/cli-platform-ios": "13.6.9", + "@react-native-community/cli-tools": "13.6.9", "chalk": "^4.1.2", "command-exists": "^1.2.8", "deepmerge": "^4.3.0", @@ -6129,6 +6447,7 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "license": "MIT", "engines": { "node": ">=6" } @@ -6137,6 +6456,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -6151,6 +6471,7 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -6166,6 +6487,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, @@ -6176,12 +6498,14 @@ "node_modules/@react-native-community/cli-doctor/node_modules/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==" + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" }, "node_modules/@react-native-community/cli-doctor/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", "engines": { "node": ">=8" } @@ -6190,6 +6514,7 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "license": "MIT", "dependencies": { "ansi-regex": "^4.1.0" }, @@ -6201,6 +6526,7 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -6209,12 +6535,13 @@ } }, "node_modules/@react-native-community/cli-hermes": { - "version": "13.6.8", - "resolved": "https://registry.npmjs.org/@react-native-community/cli-hermes/-/cli-hermes-13.6.8.tgz", - "integrity": "sha512-lZi/OBFuZUj5cLK94oEgtrtmxGoqeYVRcnHXl/R5c4put9PDl+qH2bEMlGZkFiw57ae3UZKr3TMk+1s4jh3FYQ==", + "version": "13.6.9", + "resolved": "https://registry.npmjs.org/@react-native-community/cli-hermes/-/cli-hermes-13.6.9.tgz", + "integrity": "sha512-GvwiwgvFw4Ws+krg2+gYj8sR3g05evmNjAHkKIKMkDTJjZ8EdyxbkifRUs1ZCq3TMZy2oeblZBXCJVOH4W7ZbA==", + "license": "MIT", "dependencies": { - "@react-native-community/cli-platform-android": "13.6.8", - "@react-native-community/cli-tools": "13.6.8", + "@react-native-community/cli-platform-android": "13.6.9", + "@react-native-community/cli-tools": "13.6.9", "chalk": "^4.1.2", "hermes-profile-transformer": "^0.0.6" } @@ -6223,6 +6550,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -6237,6 +6565,7 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -6252,6 +6581,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, @@ -6262,12 +6592,14 @@ "node_modules/@react-native-community/cli-hermes/node_modules/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==" + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" }, "node_modules/@react-native-community/cli-hermes/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", "engines": { "node": ">=8" } @@ -6276,6 +6608,7 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -6284,11 +6617,12 @@ } }, "node_modules/@react-native-community/cli-platform-android": { - "version": "13.6.8", - "resolved": "https://registry.npmjs.org/@react-native-community/cli-platform-android/-/cli-platform-android-13.6.8.tgz", - "integrity": "sha512-vWrqeLRRTwp2kO33nbrAgbYn8HR2c2CpIfyVJY9Ckk7HGUSwDyxdcSu7YBvt2ShdfLZH0HctWFNXsgGrfg6BDw==", + "version": "13.6.9", + "resolved": "https://registry.npmjs.org/@react-native-community/cli-platform-android/-/cli-platform-android-13.6.9.tgz", + "integrity": "sha512-9KsYGdr08QhdvT3Ht7e8phQB3gDX9Fs427NJe0xnoBh+PDPTI2BD5ks5ttsH8CzEw8/P6H8tJCHq6hf2nxd9cw==", + "license": "MIT", "dependencies": { - "@react-native-community/cli-tools": "13.6.8", + "@react-native-community/cli-tools": "13.6.9", "chalk": "^4.1.2", "execa": "^5.0.0", "fast-glob": "^3.3.2", @@ -6300,6 +6634,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -6314,6 +6649,7 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -6329,6 +6665,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, @@ -6339,12 +6676,14 @@ "node_modules/@react-native-community/cli-platform-android/node_modules/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==" + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" }, "node_modules/@react-native-community/cli-platform-android/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", "engines": { "node": ">=8" } @@ -6353,6 +6692,7 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -6361,11 +6701,12 @@ } }, "node_modules/@react-native-community/cli-platform-apple": { - "version": "13.6.8", - "resolved": "https://registry.npmjs.org/@react-native-community/cli-platform-apple/-/cli-platform-apple-13.6.8.tgz", - "integrity": "sha512-1JPohnlXPqU44zns3ALEzIbH2cKRw6JtEDJERgLuEUbs2r2NeJgqDbKyZ7fTTO8o+pegDnn6+Rr7qGVVOuUzzg==", + "version": "13.6.9", + "resolved": "https://registry.npmjs.org/@react-native-community/cli-platform-apple/-/cli-platform-apple-13.6.9.tgz", + "integrity": "sha512-KoeIHfhxMhKXZPXmhQdl6EE+jGKWwoO9jUVWgBvibpVmsNjo7woaG/tfJMEWfWF3najX1EkQAoJWpCDBMYWtlA==", + "license": "MIT", "dependencies": { - "@react-native-community/cli-tools": "13.6.8", + "@react-native-community/cli-tools": "13.6.9", "chalk": "^4.1.2", "execa": "^5.0.0", "fast-glob": "^3.3.2", @@ -6377,6 +6718,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -6391,6 +6733,7 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -6406,6 +6749,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, @@ -6416,12 +6760,14 @@ "node_modules/@react-native-community/cli-platform-apple/node_modules/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==" + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" }, "node_modules/@react-native-community/cli-platform-apple/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", "engines": { "node": ">=8" } @@ -6430,6 +6776,7 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -6438,11 +6785,12 @@ } }, "node_modules/@react-native-community/cli-platform-ios": { - "version": "13.6.8", - "resolved": "https://registry.npmjs.org/@react-native-community/cli-platform-ios/-/cli-platform-ios-13.6.8.tgz", - "integrity": "sha512-/IIcIRM8qaoD7iZqsvtf6Qq1AwtChWYfB9sTn3mTiolZ5Zd5bXH37g+6liPfAICRkj2Ptq3iXmjrDVUQAxrOXw==", + "version": "13.6.9", + "resolved": "https://registry.npmjs.org/@react-native-community/cli-platform-ios/-/cli-platform-ios-13.6.9.tgz", + "integrity": "sha512-CiUcHlGs8vE0CAB4oi1f+dzniqfGuhWPNrDvae2nm8dewlahTBwIcK5CawyGezjcJoeQhjBflh9vloska+nlnw==", + "license": "MIT", "dependencies": { - "@react-native-community/cli-platform-apple": "13.6.8" + "@react-native-community/cli-platform-apple": "13.6.9" } }, "node_modules/@react-native-community/cli-plugin-metro": { @@ -6452,25 +6800,27 @@ "peer": true }, "node_modules/@react-native-community/cli-server-api": { - "version": "13.6.8", - "resolved": "https://registry.npmjs.org/@react-native-community/cli-server-api/-/cli-server-api-13.6.8.tgz", - "integrity": "sha512-Lx664oWTzpVfbKUTy+3GIX7e+Mt5Zn+zdkM4ehllNdik/lbB3tM9Nrg8PSvOfI+tTXs2w55+nIydLfH+0FqJVg==", + "version": "13.6.9", + "resolved": "https://registry.npmjs.org/@react-native-community/cli-server-api/-/cli-server-api-13.6.9.tgz", + "integrity": "sha512-W8FSlCPWymO+tlQfM3E0JmM8Oei5HZsIk5S0COOl0MRi8h0NmHI4WSTF2GCfbFZkcr2VI/fRsocoN8Au4EZAug==", + "license": "MIT", "dependencies": { - "@react-native-community/cli-debugger-ui": "13.6.8", - "@react-native-community/cli-tools": "13.6.8", + "@react-native-community/cli-debugger-ui": "13.6.9", + "@react-native-community/cli-tools": "13.6.9", "compression": "^1.7.1", "connect": "^3.6.5", "errorhandler": "^1.5.1", "nocache": "^3.0.1", "pretty-format": "^26.6.2", "serve-static": "^1.13.1", - "ws": "^6.2.3" + "ws": "^6.2.2" } }, "node_modules/@react-native-community/cli-server-api/node_modules/@jest/types": { "version": "26.6.2", "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", + "license": "MIT", "dependencies": { "@types/istanbul-lib-coverage": "^2.0.0", "@types/istanbul-reports": "^3.0.0", @@ -6486,6 +6836,7 @@ "version": "15.0.19", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.19.tgz", "integrity": "sha512-2XUaGVmyQjgyAZldf0D0c14vvo/yv0MhQBSTJcejMMaitsn3nxCB6TmH4G0ZQf+uxROOa9mpanoSm8h6SG/1ZA==", + "license": "MIT", "dependencies": { "@types/yargs-parser": "*" } @@ -6494,6 +6845,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -6508,6 +6860,7 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -6523,6 +6876,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, @@ -6533,12 +6887,14 @@ "node_modules/@react-native-community/cli-server-api/node_modules/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==" + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" }, "node_modules/@react-native-community/cli-server-api/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", "engines": { "node": ">=8" } @@ -6547,6 +6903,7 @@ "version": "26.6.2", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==", + "license": "MIT", "dependencies": { "@jest/types": "^26.6.2", "ansi-regex": "^5.0.0", @@ -6560,12 +6917,14 @@ "node_modules/@react-native-community/cli-server-api/node_modules/react-is": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "license": "MIT" }, "node_modules/@react-native-community/cli-server-api/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -6577,14 +6936,16 @@ "version": "6.2.3", "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.3.tgz", "integrity": "sha512-jmTjYU0j60B+vHey6TfR3Z7RD61z/hmxBS3VMSGIrroOWXQEneK1zNuotOUrGyBHQj0yrpsLHPWtigEFd13ndA==", + "license": "MIT", "dependencies": { "async-limiter": "~1.0.0" } }, "node_modules/@react-native-community/cli-tools": { - "version": "13.6.8", - "resolved": "https://registry.npmjs.org/@react-native-community/cli-tools/-/cli-tools-13.6.8.tgz", - "integrity": "sha512-1MYlae9EkbjC7DBYOGMH5xF9yDoeNYUKgEdDjL6WAUBoF2gtwiZPM6igLKi/+dhb5sCtC7fiLrLi0Oevdf+RmQ==", + "version": "13.6.9", + "resolved": "https://registry.npmjs.org/@react-native-community/cli-tools/-/cli-tools-13.6.9.tgz", + "integrity": "sha512-OXaSjoN0mZVw3nrAwcY1PC0uMfyTd9fz7Cy06dh+EJc+h0wikABsVRzV8cIOPrVV+PPEEXE0DBrH20T2puZzgQ==", + "license": "MIT", "dependencies": { "appdirsjs": "^1.2.4", "chalk": "^4.1.2", @@ -6603,6 +6964,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -6617,6 +6979,7 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -6632,6 +6995,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, @@ -6642,12 +7006,14 @@ "node_modules/@react-native-community/cli-tools/node_modules/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==" + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" }, "node_modules/@react-native-community/cli-tools/node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "license": "MIT", "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" @@ -6663,6 +7029,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", "engines": { "node": ">=8" } @@ -6671,6 +7038,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", "integrity": "sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw==", + "license": "MIT", "engines": { "node": ">=4" } @@ -6679,6 +7047,7 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "license": "MIT", "dependencies": { "p-locate": "^5.0.0" }, @@ -6693,6 +7062,7 @@ "version": "6.4.0", "resolved": "https://registry.npmjs.org/open/-/open-6.4.0.tgz", "integrity": "sha512-IFenVPgF70fSm1keSd2iDBIDIBZkroLeuffXq+wKTzTJlBpesFWojV9lb8mzOfaAzM1sr7HQHuO0vtV0zYekGg==", + "license": "MIT", "dependencies": { "is-wsl": "^1.1.0" }, @@ -6704,6 +7074,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "license": "MIT", "dependencies": { "p-limit": "^3.0.2" }, @@ -6718,6 +7089,7 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -6726,9 +7098,10 @@ } }, "node_modules/@react-native-community/cli-types": { - "version": "13.6.8", - "resolved": "https://registry.npmjs.org/@react-native-community/cli-types/-/cli-types-13.6.8.tgz", - "integrity": "sha512-C4mVByy0i+/NPuPhdMLBR7ubEVkjVS1VwoQu/BoG1crJFNE+167QXAzH01eFbXndsjZaMWmD4Gerx7TYc6lHfA==", + "version": "13.6.9", + "resolved": "https://registry.npmjs.org/@react-native-community/cli-types/-/cli-types-13.6.9.tgz", + "integrity": "sha512-RLxDppvRxXfs3hxceW/mShi+6o5yS+kFPnPqZTaMKKR5aSg7LwDpLQW4K2D22irEG8e6RKDkZUeH9aL3vO2O0w==", + "license": "MIT", "dependencies": { "joi": "^17.2.1" } @@ -6737,6 +7110,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -6751,6 +7125,7 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -6766,6 +7141,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, @@ -6776,12 +7152,14 @@ "node_modules/@react-native-community/cli/node_modules/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==" + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" }, "node_modules/@react-native-community/cli/node_modules/commander": { "version": "9.5.0", "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", + "license": "MIT", "engines": { "node": "^12.20.0 || >=14" } @@ -6790,6 +7168,7 @@ "version": "8.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "license": "MIT", "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^4.0.0", @@ -6803,6 +7182,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", "engines": { "node": ">=8" } @@ -6811,6 +7191,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "license": "MIT", "optionalDependencies": { "graceful-fs": "^4.1.6" } @@ -6819,6 +7200,7 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -6830,23 +7212,29 @@ "version": "0.1.2", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "license": "MIT", "engines": { "node": ">= 4.0.0" } }, "node_modules/@react-native-community/datetimepicker": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/@react-native-community/datetimepicker/-/datetimepicker-8.1.1.tgz", - "integrity": "sha512-+P9odPpeUtjota9S85S8eVfC0YOfqLXNxzG6duE3TpKU5cXdGPgp0S8VUgOBq3u9cwP9hMr6Jea/7Kx6jUldJQ==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@react-native-community/datetimepicker/-/datetimepicker-8.2.0.tgz", + "integrity": "sha512-qrUPhiBvKGuG9Y+vOqsc56RPFcHa1SU2qbAMT0hfGkoFIj3FodE0VuPVrEa8fgy7kcD5NQmkZIKgHOBLV0+hWg==", + "license": "MIT", "dependencies": { "invariant": "^2.2.4" }, "peerDependencies": { + "expo": ">=50.0.0", "react": "*", "react-native": "*", "react-native-windows": "*" }, "peerDependenciesMeta": { + "expo": { + "optional": true + }, "react-native-windows": { "optional": true } @@ -7422,28 +7810,31 @@ } }, "node_modules/@react-native/assets-registry": { - "version": "0.74.84", - "resolved": "https://registry.npmjs.org/@react-native/assets-registry/-/assets-registry-0.74.84.tgz", - "integrity": "sha512-dzUhwyaX04QosWZ8zyaaNB/WYZIdeDN1lcpfQbqiOhZJShRH+FLTDVONE/dqlMQrP+EO7lDqF0RrlIt9lnOCQQ==", + "version": "0.74.87", + "resolved": "https://registry.npmjs.org/@react-native/assets-registry/-/assets-registry-0.74.87.tgz", + "integrity": "sha512-1XmRhqQchN+pXPKEKYdpJlwESxVomJOxtEnIkbo7GAlaN2sym84fHEGDXAjLilih5GVPpcpSmFzTy8jx3LtaFg==", + "license": "MIT", "engines": { "node": ">=18" } }, "node_modules/@react-native/babel-plugin-codegen": { - "version": "0.74.84", - "resolved": "https://registry.npmjs.org/@react-native/babel-plugin-codegen/-/babel-plugin-codegen-0.74.84.tgz", - "integrity": "sha512-UR4uiii5szIJA84mSC6GJOfYKDq7/ThyetOQT62+BBcyGeHVtHlNLNRzgaMeLqIQaT8Fq4pccMI+7QqLOMXzdw==", + "version": "0.74.87", + "resolved": "https://registry.npmjs.org/@react-native/babel-plugin-codegen/-/babel-plugin-codegen-0.74.87.tgz", + "integrity": "sha512-+vJYpMnENFrwtgvDfUj+CtVJRJuUnzAUYT0/Pb68Sq9RfcZ5xdcCuUgyf7JO+akW2VTBoJY427wkcxU30qrWWw==", + "license": "MIT", "dependencies": { - "@react-native/codegen": "0.74.84" + "@react-native/codegen": "0.74.87" }, "engines": { "node": ">=18" } }, "node_modules/@react-native/babel-preset": { - "version": "0.74.84", - "resolved": "https://registry.npmjs.org/@react-native/babel-preset/-/babel-preset-0.74.84.tgz", - "integrity": "sha512-WUfu6Y4aGuVdocQZvx33BJiQWFH6kRCHYbZfBn2psgFrSRLgQWEQrDCxqPFObNAVSayM0rNhp2FvI5K/Eyeqlg==", + "version": "0.74.87", + "resolved": "https://registry.npmjs.org/@react-native/babel-preset/-/babel-preset-0.74.87.tgz", + "integrity": "sha512-hyKpfqzN2nxZmYYJ0tQIHG99FQO0OWXp/gVggAfEUgiT+yNKas1C60LuofUsK7cd+2o9jrpqgqW4WzEDZoBlTg==", + "license": "MIT", "dependencies": { "@babel/core": "^7.20.0", "@babel/plugin-proposal-async-generator-functions": "^7.0.0", @@ -7485,7 +7876,7 @@ "@babel/plugin-transform-typescript": "^7.5.0", "@babel/plugin-transform-unicode-regex": "^7.0.0", "@babel/template": "^7.0.0", - "@react-native/babel-plugin-codegen": "0.74.84", + "@react-native/babel-plugin-codegen": "0.74.87", "babel-plugin-transform-flow-enums": "^0.0.2", "react-refresh": "^0.14.0" }, @@ -7497,9 +7888,10 @@ } }, "node_modules/@react-native/codegen": { - "version": "0.74.84", - "resolved": "https://registry.npmjs.org/@react-native/codegen/-/codegen-0.74.84.tgz", - "integrity": "sha512-0hXlnu9i0o8v+gXKQi+x6T471L85kCDwW4WrJiYAeOheWrQdNNW6rC3g8+LL7HXAf7QcHGU/8/d57iYfdVK2BQ==", + "version": "0.74.87", + "resolved": "https://registry.npmjs.org/@react-native/codegen/-/codegen-0.74.87.tgz", + "integrity": "sha512-GMSYDiD+86zLKgMMgz9z0k6FxmRn+z6cimYZKkucW4soGbxWsbjUAZoZ56sJwt2FJ3XVRgXCrnOCgXoH/Bkhcg==", + "license": "MIT", "dependencies": { "@babel/parser": "^7.20.0", "glob": "^7.1.1", @@ -7517,14 +7909,15 @@ } }, "node_modules/@react-native/community-cli-plugin": { - "version": "0.74.84", - "resolved": "https://registry.npmjs.org/@react-native/community-cli-plugin/-/community-cli-plugin-0.74.84.tgz", - "integrity": "sha512-GBKE+1sUh86fS2XXV46gMCNHMc1KetshMbYJ0AhDhldpaILZHqRBX50mdVsiYVvkzp4QjM0nmYqefuJ9NVwicQ==", - "dependencies": { - "@react-native-community/cli-server-api": "13.6.8", - "@react-native-community/cli-tools": "13.6.8", - "@react-native/dev-middleware": "0.74.84", - "@react-native/metro-babel-transformer": "0.74.84", + "version": "0.74.87", + "resolved": "https://registry.npmjs.org/@react-native/community-cli-plugin/-/community-cli-plugin-0.74.87.tgz", + "integrity": "sha512-EgJG9lSr8x3X67dHQKQvU6EkO+3ksVlJHYIVv6U/AmW9dN80BEFxgYbSJ7icXS4wri7m4kHdgeq2PQ7/3vvrTQ==", + "license": "MIT", + "dependencies": { + "@react-native-community/cli-server-api": "13.6.9", + "@react-native-community/cli-tools": "13.6.9", + "@react-native/dev-middleware": "0.74.87", + "@react-native/metro-babel-transformer": "0.74.87", "chalk": "^4.0.0", "execa": "^5.1.1", "metro": "^0.80.3", @@ -7538,10 +7931,44 @@ "node": ">=18" } }, + "node_modules/@react-native/community-cli-plugin/node_modules/@react-native/debugger-frontend": { + "version": "0.74.87", + "resolved": "https://registry.npmjs.org/@react-native/debugger-frontend/-/debugger-frontend-0.74.87.tgz", + "integrity": "sha512-MN95DJLYTv4EqJc+9JajA3AJZSBYJz2QEJ3uWlHrOky2vKrbbRVaW1ityTmaZa2OXIvNc6CZwSRSE7xCoHbXhQ==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=18" + } + }, + "node_modules/@react-native/community-cli-plugin/node_modules/@react-native/dev-middleware": { + "version": "0.74.87", + "resolved": "https://registry.npmjs.org/@react-native/dev-middleware/-/dev-middleware-0.74.87.tgz", + "integrity": "sha512-7TmZ3hTHwooYgIHqc/z87BMe1ryrIqAUi+AF7vsD+EHCGxHFdMjSpf1BZ2SUPXuLnF2cTiTfV2RwhbPzx0tYIA==", + "license": "MIT", + "dependencies": { + "@isaacs/ttlcache": "^1.4.1", + "@react-native/debugger-frontend": "0.74.87", + "@rnx-kit/chromium-edge-launcher": "^1.0.0", + "chrome-launcher": "^0.15.2", + "connect": "^3.6.5", + "debug": "^2.2.0", + "node-fetch": "^2.2.0", + "nullthrows": "^1.1.1", + "open": "^7.0.3", + "selfsigned": "^2.4.1", + "serve-static": "^1.13.1", + "temp-dir": "^2.0.0", + "ws": "^6.2.2" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/@react-native/community-cli-plugin/node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -7556,6 +7983,7 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -7571,6 +7999,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, @@ -7581,20 +8010,38 @@ "node_modules/@react-native/community-cli-plugin/node_modules/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==" + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" }, - "node_modules/@react-native/community-cli-plugin/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } + "node_modules/@react-native/community-cli-plugin/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/@react-native/community-cli-plugin/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@react-native/community-cli-plugin/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" }, "node_modules/@react-native/community-cli-plugin/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -7602,21 +8049,32 @@ "node": ">=8" } }, + "node_modules/@react-native/community-cli-plugin/node_modules/ws": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.3.tgz", + "integrity": "sha512-jmTjYU0j60B+vHey6TfR3Z7RD61z/hmxBS3VMSGIrroOWXQEneK1zNuotOUrGyBHQj0yrpsLHPWtigEFd13ndA==", + "license": "MIT", + "dependencies": { + "async-limiter": "~1.0.0" + } + }, "node_modules/@react-native/debugger-frontend": { - "version": "0.74.84", - "resolved": "https://registry.npmjs.org/@react-native/debugger-frontend/-/debugger-frontend-0.74.84.tgz", - "integrity": "sha512-YUEA03UNFbiYzHpYxlcS2D9+3eNT5YLGkl5yRg3nOSN6KbCc/OttGnNZme+tuSOJwjMN/vcvtDKYkTqjJw8U0A==", + "version": "0.74.85", + "resolved": "https://registry.npmjs.org/@react-native/debugger-frontend/-/debugger-frontend-0.74.85.tgz", + "integrity": "sha512-gUIhhpsYLUTYWlWw4vGztyHaX/kNlgVspSvKe2XaPA7o3jYKUoNLc3Ov7u70u/MBWfKdcEffWq44eSe3j3s5JQ==", + "license": "BSD-3-Clause", "engines": { "node": ">=18" } }, "node_modules/@react-native/dev-middleware": { - "version": "0.74.84", - "resolved": "https://registry.npmjs.org/@react-native/dev-middleware/-/dev-middleware-0.74.84.tgz", - "integrity": "sha512-veYw/WmyrAOQHUiIeULzn2duJQnXDPiKq2jZ/lcmDo6jsLirpp+Q73lx09TYgy/oVoPRuV0nfmU3x9B6EV/7qQ==", + "version": "0.74.85", + "resolved": "https://registry.npmjs.org/@react-native/dev-middleware/-/dev-middleware-0.74.85.tgz", + "integrity": "sha512-BRmgCK5vnMmHaKRO+h8PKJmHHH3E6JFuerrcfE3wG2eZ1bcSr+QTu8DAlpxsDWvJvHpCi8tRJGauxd+Ssj/c7w==", + "license": "MIT", "dependencies": { "@isaacs/ttlcache": "^1.4.1", - "@react-native/debugger-frontend": "0.74.84", + "@react-native/debugger-frontend": "0.74.85", "@rnx-kit/chromium-edge-launcher": "^1.0.0", "chrome-launcher": "^0.15.2", "connect": "^3.6.5", @@ -7627,7 +8085,7 @@ "selfsigned": "^2.4.1", "serve-static": "^1.13.1", "temp-dir": "^2.0.0", - "ws": "^6.2.3" + "ws": "^6.2.2" }, "engines": { "node": ">=18" @@ -7637,6 +8095,7 @@ "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", "dependencies": { "ms": "2.0.0" } @@ -7644,25 +8103,28 @@ "node_modules/@react-native/dev-middleware/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" }, "node_modules/@react-native/dev-middleware/node_modules/ws": { "version": "6.2.3", "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.3.tgz", "integrity": "sha512-jmTjYU0j60B+vHey6TfR3Z7RD61z/hmxBS3VMSGIrroOWXQEneK1zNuotOUrGyBHQj0yrpsLHPWtigEFd13ndA==", + "license": "MIT", "dependencies": { "async-limiter": "~1.0.0" } }, "node_modules/@react-native/eslint-config": { - "version": "0.74.84", - "resolved": "https://registry.npmjs.org/@react-native/eslint-config/-/eslint-config-0.74.84.tgz", - "integrity": "sha512-QpMeMzg6QPN54K0LFasuqX52nyRF0dPIoh7ErXW7OoDatOMfxjB5CK7jVc1MqhKnUe5mz50BdQ44SOpzYr3xvw==", + "version": "0.74.87", + "resolved": "https://registry.npmjs.org/@react-native/eslint-config/-/eslint-config-0.74.87.tgz", + "integrity": "sha512-5n1KdlUXxFA10VbcWXncRcV6eDO1AwnCAaNed1Yqzy1nbtyXmkMLoYJC/nnWJdMBqpzCU5HrwhWdyuj2K2OAUg==", "dev": true, + "license": "MIT", "dependencies": { "@babel/core": "^7.20.0", "@babel/eslint-parser": "^7.20.0", - "@react-native/eslint-plugin": "0.74.84", + "@react-native/eslint-plugin": "0.74.87", "@typescript-eslint/eslint-plugin": "^7.1.1", "@typescript-eslint/parser": "^7.1.1", "eslint-config-prettier": "^8.5.0", @@ -7820,37 +8282,41 @@ } }, "node_modules/@react-native/eslint-plugin": { - "version": "0.74.84", - "resolved": "https://registry.npmjs.org/@react-native/eslint-plugin/-/eslint-plugin-0.74.84.tgz", - "integrity": "sha512-pDzo4Qm1uPZQne2sv0QK89ePxP/i+ZHjrBW3rkTVStLvsDVdyFahMmt6bzJTdYL2cGgK2oyNmfXtvO57INOu3Q==", + "version": "0.74.87", + "resolved": "https://registry.npmjs.org/@react-native/eslint-plugin/-/eslint-plugin-0.74.87.tgz", + "integrity": "sha512-AtUQM3ShGlOAV8Z8p9NmnXfrN762Tb4NnInQQPheP1sjbJsFpxTui+S/uGrL7I2wBrdtADnOKkb6yYhRU0OKOg==", "dev": true, + "license": "MIT", "engines": { "node": ">=18" } }, "node_modules/@react-native/gradle-plugin": { - "version": "0.74.84", - "resolved": "https://registry.npmjs.org/@react-native/gradle-plugin/-/gradle-plugin-0.74.84.tgz", - "integrity": "sha512-wYWC5WWXqzCCe4PDogz9pNc4xH5ZamahW5XGSbrrYJ5V3walZ+7z43V6iEBJkZbLjj9YBcSttkXYGr1Xh4veAg==", + "version": "0.74.87", + "resolved": "https://registry.npmjs.org/@react-native/gradle-plugin/-/gradle-plugin-0.74.87.tgz", + "integrity": "sha512-T+VX0N1qP+U9V4oAtn7FTX7pfsoVkd1ocyw9swYXgJqU2fK7hC9famW7b3s3ZiufPGPr1VPJe2TVGtSopBjL6A==", + "license": "MIT", "engines": { "node": ">=18" } }, "node_modules/@react-native/js-polyfills": { - "version": "0.74.84", - "resolved": "https://registry.npmjs.org/@react-native/js-polyfills/-/js-polyfills-0.74.84.tgz", - "integrity": "sha512-+PgxuUjBw9JVlz6m4ECsIJMLbDopnr4rpLmsG32hQaJrg0wMuvHtsgAY/J/aVCSG2GNUXexfjrnhc+O9yGOZXQ==", + "version": "0.74.87", + "resolved": "https://registry.npmjs.org/@react-native/js-polyfills/-/js-polyfills-0.74.87.tgz", + "integrity": "sha512-M5Evdn76CuVEF0GsaXiGi95CBZ4IWubHqwXxV9vG9CC9kq0PSkoM2Pn7Lx7dgyp4vT7ccJ8a3IwHbe+5KJRnpw==", + "license": "MIT", "engines": { "node": ">=18" } }, "node_modules/@react-native/metro-babel-transformer": { - "version": "0.74.84", - "resolved": "https://registry.npmjs.org/@react-native/metro-babel-transformer/-/metro-babel-transformer-0.74.84.tgz", - "integrity": "sha512-YtVGq7jkgyUECv5yt4BOFbOXyW4ddUn8+dnwGGpJKdfhXYL5o5++AxNdE+2x+SZdkj3JUVekGKPwRabFECABaw==", + "version": "0.74.87", + "resolved": "https://registry.npmjs.org/@react-native/metro-babel-transformer/-/metro-babel-transformer-0.74.87.tgz", + "integrity": "sha512-UsJCO24sNax2NSPBmV1zLEVVNkS88kcgAiYrZHtYSwSjpl4WZ656tIeedBfiySdJ94Hr3kQmBYLipV5zk0NI1A==", + "license": "MIT", "dependencies": { "@babel/core": "^7.20.0", - "@react-native/babel-preset": "0.74.84", + "@react-native/babel-preset": "0.74.87", "hermes-parser": "0.19.1", "nullthrows": "^1.1.1" }, @@ -7862,13 +8328,14 @@ } }, "node_modules/@react-native/metro-config": { - "version": "0.74.84", - "resolved": "https://registry.npmjs.org/@react-native/metro-config/-/metro-config-0.74.84.tgz", - "integrity": "sha512-/8pvN4P5ldIN/4f+4+Wkd5FH8F5I7dx1xyPDmHNHqu5bLS+N3347NU8t97029LuZikv59fGj4B6nkvlc4GRHfg==", + "version": "0.74.87", + "resolved": "https://registry.npmjs.org/@react-native/metro-config/-/metro-config-0.74.87.tgz", + "integrity": "sha512-WjXk7GmzL7/+e5qqc9uumQSl6uCqNlpo8LaKuMxxlUfQ6DsWSXIdbLXmD1k5qTURjL0fZjVQGszgvT1xKYifaQ==", "dev": true, + "license": "MIT", "dependencies": { - "@react-native/js-polyfills": "0.74.84", - "@react-native/metro-babel-transformer": "0.74.84", + "@react-native/js-polyfills": "0.74.87", + "@react-native/metro-babel-transformer": "0.74.87", "metro-config": "^0.80.3", "metro-runtime": "^0.80.3" }, @@ -7877,20 +8344,23 @@ } }, "node_modules/@react-native/normalize-colors": { - "version": "0.74.84", - "resolved": "https://registry.npmjs.org/@react-native/normalize-colors/-/normalize-colors-0.74.84.tgz", - "integrity": "sha512-Y5W6x8cC5RuakUcTVUFNAIhUZ/tYpuqHZlRBoAuakrTwVuoNHXfQki8lj1KsYU7rW6e3VWgdEx33AfOQpdNp6A==" + "version": "0.74.85", + "resolved": "https://registry.npmjs.org/@react-native/normalize-colors/-/normalize-colors-0.74.85.tgz", + "integrity": "sha512-pcE4i0X7y3hsAE0SpIl7t6dUc0B0NZLd1yv7ssm4FrLhWG+CGyIq4eFDXpmPU1XHmL5PPySxTAjEMiwv6tAmOw==", + "license": "MIT" }, "node_modules/@react-native/typescript-config": { - "version": "0.74.84", - "resolved": "https://registry.npmjs.org/@react-native/typescript-config/-/typescript-config-0.74.84.tgz", - "integrity": "sha512-yMAyxl0wzRKT6JkQV0WCryiBQ1hj97u/JiT4LjXbwpPSErgiTRq+SKwhKH5vhSw9P0O8JCLIG6rS5rkbjranDg==", - "dev": true + "version": "0.74.87", + "resolved": "https://registry.npmjs.org/@react-native/typescript-config/-/typescript-config-0.74.87.tgz", + "integrity": "sha512-nBxot0relWwDlOmjHam0QYmArMFpcDLdJqe4aN1aeWv9KkrE4v36KT7H9h9imGkugZkMs8PJPGPo6eojD09/kA==", + "dev": true, + "license": "MIT" }, "node_modules/@react-native/virtualized-lists": { - "version": "0.74.84", - "resolved": "https://registry.npmjs.org/@react-native/virtualized-lists/-/virtualized-lists-0.74.84.tgz", - "integrity": "sha512-XcV+qdqt2WihaY4iRm/M1FdSy+18lecU9mRXNmy9YK8g9Th/8XbNtmmKI0qWBx3KxyuXMH/zd0ps05YTrX16kw==", + "version": "0.74.87", + "resolved": "https://registry.npmjs.org/@react-native/virtualized-lists/-/virtualized-lists-0.74.87.tgz", + "integrity": "sha512-lsGxoFMb0lyK/MiplNKJpD+A1EoEUumkLrCjH4Ht+ZlG8S0BfCxmskLZ6qXn3BiDSkLjfjI/qyZ3pnxNBvkXpQ==", + "license": "MIT", "dependencies": { "invariant": "^2.2.4", "nullthrows": "^1.1.1" @@ -7910,11 +8380,12 @@ } }, "node_modules/@react-navigation/bottom-tabs": { - "version": "6.5.20", - "resolved": "https://registry.npmjs.org/@react-navigation/bottom-tabs/-/bottom-tabs-6.5.20.tgz", - "integrity": "sha512-ow6Z06iS4VqBO8d7FP+HsGjJLWt2xTWIvuWjpoCvsM/uQXzCRDIjBv9HaKcXbF0yTW7IMir0oDAbU5PFzEDdgA==", + "version": "6.6.1", + "resolved": "https://registry.npmjs.org/@react-navigation/bottom-tabs/-/bottom-tabs-6.6.1.tgz", + "integrity": "sha512-9oD4cypEBjPuaMiu9tevWGiQ4w/d6l3HNhcJ1IjXZ24xvYDSs0mqjUcdt8SWUolCvRrYc/DmNBLlT83bk0bHTw==", + "license": "MIT", "dependencies": { - "@react-navigation/elements": "^1.3.30", + "@react-navigation/elements": "^1.3.31", "color": "^4.2.3", "warn-once": "^0.1.0" }, @@ -7927,25 +8398,27 @@ } }, "node_modules/@react-navigation/core": { - "version": "6.4.16", - "resolved": "https://registry.npmjs.org/@react-navigation/core/-/core-6.4.16.tgz", - "integrity": "sha512-UDTJBsHxnzgFETR3ZxhctP+RWr4SkyeZpbhpkQoIGOuwSCkt1SE0qjU48/u6r6w6XlX8OqVudn1Ab0QFXTHxuQ==", + "version": "6.4.17", + "resolved": "https://registry.npmjs.org/@react-navigation/core/-/core-6.4.17.tgz", + "integrity": "sha512-Nd76EpomzChWAosGqWOYE3ItayhDzIEzzZsT7PfGcRFDgW5miHV2t4MZcq9YIK4tzxZjVVpYbIynOOQQd1e0Cg==", + "license": "MIT", "dependencies": { "@react-navigation/routers": "^6.1.9", "escape-string-regexp": "^4.0.0", "nanoid": "^3.1.23", "query-string": "^7.1.3", "react-is": "^16.13.0", - "use-latest-callback": "^0.1.9" + "use-latest-callback": "^0.2.1" }, "peerDependencies": { "react": "*" } }, "node_modules/@react-navigation/elements": { - "version": "1.3.30", - "resolved": "https://registry.npmjs.org/@react-navigation/elements/-/elements-1.3.30.tgz", - "integrity": "sha512-plhc8UvCZs0UkV+sI+3bisIyn78wz9O/BiWZXpounu72k/R/Sj5PuZYFJ1fi6psvriUveMCGh4LeZckAZu2qiQ==", + "version": "1.3.31", + "resolved": "https://registry.npmjs.org/@react-navigation/elements/-/elements-1.3.31.tgz", + "integrity": "sha512-bUzP4Awlljx5RKEExw8WYtif8EuQni2glDaieYROKTnaxsu9kEIA515sXQgUDZU4Ob12VoL7+z70uO3qrlfXcQ==", + "license": "MIT", "peerDependencies": { "@react-navigation/native": "^6.0.0", "react": "*", @@ -7954,11 +8427,12 @@ } }, "node_modules/@react-navigation/native": { - "version": "6.1.17", - "resolved": "https://registry.npmjs.org/@react-navigation/native/-/native-6.1.17.tgz", - "integrity": "sha512-mer3OvfwWOHoUSMJyLa4vnBH3zpFmCwuzrBPlw7feXklurr/ZDiLjLxUScOot6jLRMz/67GyilEYMmP99LL0RQ==", + "version": "6.1.18", + "resolved": "https://registry.npmjs.org/@react-navigation/native/-/native-6.1.18.tgz", + "integrity": "sha512-mIT9MiL/vMm4eirLcmw2h6h/Nm5FICtnYSdohq4vTLA2FF/6PNhByM7s8ffqoVfE5L0uAa6Xda1B7oddolUiGg==", + "license": "MIT", "dependencies": { - "@react-navigation/core": "^6.4.16", + "@react-navigation/core": "^6.4.17", "escape-string-regexp": "^4.0.0", "fast-deep-equal": "^3.1.3", "nanoid": "^3.1.23" @@ -7972,16 +8446,18 @@ "version": "6.1.9", "resolved": "https://registry.npmjs.org/@react-navigation/routers/-/routers-6.1.9.tgz", "integrity": "sha512-lTM8gSFHSfkJvQkxacGM6VJtBt61ip2XO54aNfswD+KMw6eeZ4oehl7m0me3CR9hnDE4+60iAZR8sAhvCiI3NA==", + "license": "MIT", "dependencies": { "nanoid": "^3.1.23" } }, "node_modules/@react-navigation/stack": { - "version": "6.3.29", - "resolved": "https://registry.npmjs.org/@react-navigation/stack/-/stack-6.3.29.tgz", - "integrity": "sha512-tzlGkoRgB6P7vgw7rHuWo3TL7Gzu6xh5LMf+zSdCuEiKp/qASzxYfnTEr9tOLbVs/gf+qeukEDheCSAJKVpBXw==", + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/@react-navigation/stack/-/stack-6.4.1.tgz", + "integrity": "sha512-upMEHOKMtuMu4c9gmoPlO/JqI6mDlSqwXg1aXKOTQLXAF8H5koOLRfrmi7AkdiE9A7lDXWUAZoGuD9O88cYvDQ==", + "license": "MIT", "dependencies": { - "@react-navigation/elements": "^1.3.30", + "@react-navigation/elements": "^1.3.31", "color": "^4.2.3", "warn-once": "^0.1.0" }, @@ -8048,6 +8524,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/@rnx-kit/chromium-edge-launcher/-/chromium-edge-launcher-1.0.0.tgz", "integrity": "sha512-lzD84av1ZQhYUS+jsGqJiCMaJO2dn9u+RTT9n9q6D3SaKVwWqv+7AoRKqBu19bkwyE+iFRl1ymr40QS90jVFYg==", + "license": "Apache-2.0", "dependencies": { "@types/node": "^18.0.0", "escape-string-regexp": "^4.0.0", @@ -8064,6 +8541,7 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "license": "MIT", "bin": { "mkdirp": "bin/cmd.js" }, @@ -8120,6 +8598,15 @@ "node": ">=8" } }, + "node_modules/@sentry/babel-plugin-component-annotate": { + "version": "2.20.1", + "resolved": "https://registry.npmjs.org/@sentry/babel-plugin-component-annotate/-/babel-plugin-component-annotate-2.20.1.tgz", + "integrity": "sha512-4mhEwYTK00bIb5Y9UWIELVUfru587Vaeg0DQGswv4aIRHIiMKLyNqCEejaaybQ/fNChIZOKmvyqXk430YVd7Qg==", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, "node_modules/@sentry/browser": { "version": "7.117.0", "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-7.117.0.tgz", @@ -8332,10 +8819,12 @@ } }, "node_modules/@sentry/react-native": { - "version": "5.24.1", - "resolved": "https://registry.npmjs.org/@sentry/react-native/-/react-native-5.24.1.tgz", - "integrity": "sha512-+BB48ixYn+brEntbDX49V4mUivrGT+SJUzbIN7nOsd3kml619erp5bxETD4ZK6diw0Fu2LoVdnY43TICF4kleQ==", + "version": "5.27.0", + "resolved": "https://registry.npmjs.org/@sentry/react-native/-/react-native-5.27.0.tgz", + "integrity": "sha512-nCGn+lJNPVK6BZe9wUhEYpujBFBGXc4B5j7RZuoRvJSWGX5+0AD8urBe+GDUjNonlKnjPaDm6C+nlsm7BCas9g==", + "license": "MIT", "dependencies": { + "@sentry/babel-plugin-component-annotate": "2.20.1", "@sentry/browser": "7.117.0", "@sentry/cli": "2.31.2", "@sentry/core": "7.117.0", @@ -8471,10 +8960,11 @@ } }, "node_modules/@testing-library/react-native": { - "version": "12.5.1", - "resolved": "https://registry.npmjs.org/@testing-library/react-native/-/react-native-12.5.1.tgz", - "integrity": "sha512-PApr3f6DmSJF/EIiWYZfcBzuy6w7fK8TW4a6KfQHTeAcfZ6lADtRO7R0QM5WI+b7tJ33JvIPgzCg1MiuRz4v0g==", + "version": "12.5.2", + "resolved": "https://registry.npmjs.org/@testing-library/react-native/-/react-native-12.5.2.tgz", + "integrity": "sha512-utFXq9xVc2D/vZk80YzWPTPTNbVXh7jyLYW+DJ2mlmDhj5GaAKf1abf2Dwc6gitxAWn5R22GO3hBME03W0f82w==", "dev": true, + "license": "MIT", "dependencies": { "jest-matcher-utils": "^29.7.0", "pretty-format": "^29.7.0", @@ -8569,33 +9059,12 @@ "integrity": "sha512-tqdiS4otQP4KmY0PR3u6KbZ5EWvhNdUoS/jc93UuK23C220lOZ/9TvjfxdPcKvqwwDVtmtSCrnr0p/2dirAxkA==", "dev": true }, - "node_modules/@types/eslint": { - "version": "8.56.9", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.9.tgz", - "integrity": "sha512-W4W3KcqzjJ0sHg2vAq9vfml6OhsJ53TcUjUqfzzZf/EChUtwspszj/S0pzMxnfRcO55/iGq47dscXw71Fxc4Zg==", - "dev": true, - "peer": true, - "dependencies": { - "@types/estree": "*", - "@types/json-schema": "*" - } - }, - "node_modules/@types/eslint-scope": { - "version": "3.7.7", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", - "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", - "dev": true, - "peer": true, - "dependencies": { - "@types/eslint": "*", - "@types/estree": "*" - } - }, "node_modules/@types/estree": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", "dev": true, + "license": "MIT", "peer": true }, "node_modules/@types/graceful-fs": { @@ -8684,10 +9153,11 @@ "dev": true }, "node_modules/@types/lodash": { - "version": "4.17.5", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.5.tgz", - "integrity": "sha512-MBIOHVZqVqgfro1euRDWX7OO0fBVUUMrN6Pwm8LQsz8cWhEpihlvR70ENj3f40j58TNxZaWv2ndSkInykNBBJw==", - "dev": true + "version": "4.17.7", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.7.tgz", + "integrity": "sha512-8wTvZawATi/lsmNu10/j2hk1KEP0IvjubqPE3cu1Xz7xfXXt5oCq3SNUz4fMIP4XGF9Ky+Ue2tBA3hcS7LSBlA==", + "dev": true, + "license": "MIT" }, "node_modules/@types/mime-db": { "version": "1.43.5", @@ -8707,6 +9177,7 @@ "version": "1.3.11", "resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.3.11.tgz", "integrity": "sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ==", + "license": "MIT", "dependencies": { "@types/node": "*" } @@ -8826,10 +9297,11 @@ "dev": true }, "node_modules/@types/uuid": { - "version": "9.0.8", - "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz", - "integrity": "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==", - "dev": true + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==", + "dev": true, + "license": "MIT" }, "node_modules/@types/yargs": { "version": "17.0.32", @@ -8849,6 +9321,7 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.13.1.tgz", "integrity": "sha512-kZqi+WZQaZfPKnsflLJQCz6Ze9FFSMfXrrIOcyargekQxG37ES7DJNpJUE9Q/X5n3yTIP/WPutVNzgknQ7biLg==", "dev": true, + "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", "@typescript-eslint/scope-manager": "7.13.1", @@ -8877,11 +9350,127 @@ } } }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/scope-manager": { + "version": "7.13.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.13.1.tgz", + "integrity": "sha512-adbXNVEs6GmbzaCpymHQ0MB6E4TqoiVbC0iqG3uijR8ZYfpAXMGttouQzF4Oat3P2GxDVIrg7bMI/P65LiQZdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "7.13.1", + "@typescript-eslint/visitor-keys": "7.13.1" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/types": { + "version": "7.13.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.13.1.tgz", + "integrity": "sha512-7K7HMcSQIAND6RBL4kDl24sG/xKM13cA85dc7JnmQXw2cBDngg7c19B++JzvJHRG3zG36n9j1i451GBzRuHchw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/typescript-estree": { + "version": "7.13.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.13.1.tgz", + "integrity": "sha512-uxNr51CMV7npU1BxZzYjoVz9iyjckBduFBP0S5sLlh1tXYzHzgZ3BR9SVsNed+LmwKrmnqN3Kdl5t7eZ5TS1Yw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@typescript-eslint/types": "7.13.1", + "@typescript-eslint/visitor-keys": "7.13.1", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/utils": { + "version": "7.13.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.13.1.tgz", + "integrity": "sha512-h5MzFBD5a/Gh/fvNdp9pTfqJAbuQC4sCN2WzuXme71lqFJsZtLbjxfSk4r3p02WIArOF9N94pdsLiGutpDbrXQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "7.13.1", + "@typescript-eslint/types": "7.13.1", + "@typescript-eslint/typescript-estree": "7.13.1" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/visitor-keys": { + "version": "7.13.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.13.1.tgz", + "integrity": "sha512-k/Bfne7lrP7hcb7m9zSsgcBmo+8eicqqfNAJ7uUY+jkTFpKeH2FSkWpFRtimBxgkyvqfu9jTPRbYOvud6isdXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "7.13.1", + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, "node_modules/@typescript-eslint/parser": { "version": "7.13.1", "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.13.1.tgz", "integrity": "sha512-1ELDPlnLvDQ5ybTSrMhRTFDfOQEOXNM+eP+3HT/Yq7ruWpciQw+Avi73pdEbA4SooCawEWo3dtYbF68gN7Ed1A==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "@typescript-eslint/scope-manager": "7.13.1", "@typescript-eslint/types": "7.13.1", @@ -8905,11 +9494,12 @@ } } }, - "node_modules/@typescript-eslint/scope-manager": { + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/scope-manager": { "version": "7.13.1", "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.13.1.tgz", "integrity": "sha512-adbXNVEs6GmbzaCpymHQ0MB6E4TqoiVbC0iqG3uijR8ZYfpAXMGttouQzF4Oat3P2GxDVIrg7bMI/P65LiQZdg==", "dev": true, + "license": "MIT", "dependencies": { "@typescript-eslint/types": "7.13.1", "@typescript-eslint/visitor-keys": "7.13.1" @@ -8922,11 +9512,104 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/types": { + "version": "7.13.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.13.1.tgz", + "integrity": "sha512-7K7HMcSQIAND6RBL4kDl24sG/xKM13cA85dc7JnmQXw2cBDngg7c19B++JzvJHRG3zG36n9j1i451GBzRuHchw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/typescript-estree": { + "version": "7.13.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.13.1.tgz", + "integrity": "sha512-uxNr51CMV7npU1BxZzYjoVz9iyjckBduFBP0S5sLlh1tXYzHzgZ3BR9SVsNed+LmwKrmnqN3Kdl5t7eZ5TS1Yw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@typescript-eslint/types": "7.13.1", + "@typescript-eslint/visitor-keys": "7.13.1", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/visitor-keys": { + "version": "7.13.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.13.1.tgz", + "integrity": "sha512-k/Bfne7lrP7hcb7m9zSsgcBmo+8eicqqfNAJ7uUY+jkTFpKeH2FSkWpFRtimBxgkyvqfu9jTPRbYOvud6isdXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "7.13.1", + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.0.0.tgz", + "integrity": "sha512-V0aa9Csx/ZWWv2IPgTfY7T4agYwJyILESu/PVqFtTFz9RIS823mAze+NbnBI8xiwdX3iqeQbcTYlvB04G9wyQw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.0.0", + "@typescript-eslint/visitor-keys": "8.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, "node_modules/@typescript-eslint/type-utils": { "version": "7.13.1", "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.13.1.tgz", "integrity": "sha512-aWDbLu1s9bmgPGXSzNCxELu+0+HQOapV/y+60gPXafR8e2g1Bifxzevaa+4L2ytCWm+CHqpELq4CSoN9ELiwCg==", "dev": true, + "license": "MIT", "dependencies": { "@typescript-eslint/typescript-estree": "7.13.1", "@typescript-eslint/utils": "7.13.1", @@ -8949,11 +9632,30 @@ } } }, - "node_modules/@typescript-eslint/types": { + "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/scope-manager": { + "version": "7.13.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.13.1.tgz", + "integrity": "sha512-adbXNVEs6GmbzaCpymHQ0MB6E4TqoiVbC0iqG3uijR8ZYfpAXMGttouQzF4Oat3P2GxDVIrg7bMI/P65LiQZdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "7.13.1", + "@typescript-eslint/visitor-keys": "7.13.1" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/types": { "version": "7.13.1", "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.13.1.tgz", "integrity": "sha512-7K7HMcSQIAND6RBL4kDl24sG/xKM13cA85dc7JnmQXw2cBDngg7c19B++JzvJHRG3zG36n9j1i451GBzRuHchw==", "dev": true, + "license": "MIT", "engines": { "node": "^18.18.0 || >=20.0.0" }, @@ -8962,11 +9664,12 @@ "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@typescript-eslint/typescript-estree": { + "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/typescript-estree": { "version": "7.13.1", "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.13.1.tgz", "integrity": "sha512-uxNr51CMV7npU1BxZzYjoVz9iyjckBduFBP0S5sLlh1tXYzHzgZ3BR9SVsNed+LmwKrmnqN3Kdl5t7eZ5TS1Yw==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "@typescript-eslint/types": "7.13.1", "@typescript-eslint/visitor-keys": "7.13.1", @@ -8990,11 +9693,12 @@ } } }, - "node_modules/@typescript-eslint/utils": { + "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/utils": { "version": "7.13.1", "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.13.1.tgz", "integrity": "sha512-h5MzFBD5a/Gh/fvNdp9pTfqJAbuQC4sCN2WzuXme71lqFJsZtLbjxfSk4r3p02WIArOF9N94pdsLiGutpDbrXQ==", "dev": true, + "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "@typescript-eslint/scope-manager": "7.13.1", @@ -9012,11 +9716,12 @@ "eslint": "^8.56.0" } }, - "node_modules/@typescript-eslint/visitor-keys": { + "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/visitor-keys": { "version": "7.13.1", "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.13.1.tgz", "integrity": "sha512-k/Bfne7lrP7hcb7m9zSsgcBmo+8eicqqfNAJ7uUY+jkTFpKeH2FSkWpFRtimBxgkyvqfu9jTPRbYOvud6isdXA==", "dev": true, + "license": "MIT", "dependencies": { "@typescript-eslint/types": "7.13.1", "eslint-visitor-keys": "^3.4.3" @@ -9029,11 +9734,109 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@typescript-eslint/type-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.0.0.tgz", + "integrity": "sha512-wgdSGs9BTMWQ7ooeHtu5quddKKs5Z5dS+fHLbrQI+ID0XWJLODGMHRfhwImiHoeO2S5Wir2yXuadJN6/l4JRxw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.0.0.tgz", + "integrity": "sha512-5b97WpKMX+Y43YKi4zVcCVLtK5F98dFls3Oxui8LbnmRsseKenbbDinmvxrWegKDMmlkIq/XHuyy0UGLtpCDKg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@typescript-eslint/types": "8.0.0", + "@typescript-eslint/visitor-keys": "8.0.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.0.0.tgz", + "integrity": "sha512-k/oS/A/3QeGLRvOWCg6/9rATJL5rec7/5s1YmdS0ZU6LHveJyGFwBvLhSRBv6i9xaj7etmosp+l+ViN1I9Aj/Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "8.0.0", + "@typescript-eslint/types": "8.0.0", + "@typescript-eslint/typescript-estree": "8.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.0.0.tgz", + "integrity": "sha512-oN0K4nkHuOyF3PVMyETbpP5zp6wfyOvm7tWhTMfoqxSSsPmJIh6JNASuZDlODE8eE+0EB9uar+6+vxr9DBTYOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.0.0", + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { "version": "3.4.3", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true, + "license": "Apache-2.0", "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, @@ -9051,6 +9854,7 @@ "version": "2.3.6", "resolved": "https://registry.npmjs.org/@urql/core/-/core-2.3.6.tgz", "integrity": "sha512-PUxhtBh7/8167HJK6WqBv6Z0piuiaZHQGYbhwpNL9aIQmLROPEdaUYkY4wh45wPQXcTpnd11l0q3Pw+TI11pdw==", + "license": "MIT", "dependencies": { "@graphql-typed-document-node/core": "^3.1.0", "wonka": "^4.0.14" @@ -9063,6 +9867,7 @@ "version": "0.3.0", "resolved": "https://registry.npmjs.org/@urql/exchange-retry/-/exchange-retry-0.3.0.tgz", "integrity": "sha512-hHqer2mcdVC0eYnVNbWyi28AlGOPb2vjH3lP3/Bc8Lc8BjhMsDwFMm7WhoP5C1+cfbr/QJ6Er3H/L08wznXxfg==", + "license": "MIT", "dependencies": { "@urql/core": ">=2.3.1", "wonka": "^4.0.14" @@ -9335,9 +10140,10 @@ } }, "node_modules/acorn": { - "version": "8.11.3", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", - "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", + "version": "8.12.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", + "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", + "license": "MIT", "bin": { "acorn": "bin/acorn" }, @@ -9364,6 +10170,17 @@ "acorn": "^8" } }, + "node_modules/acorn-import-attributes": { + "version": "1.9.5", + "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz", + "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==", + "dev": true, + "license": "MIT", + "peer": true, + "peerDependencies": { + "acorn": "^8" + } + }, "node_modules/acorn-jsx": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", @@ -9398,6 +10215,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "license": "MIT", "dependencies": { "clean-stack": "^2.0.0", "indent-string": "^4.0.0" @@ -9543,7 +10361,8 @@ "node_modules/application-config-path": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/application-config-path/-/application-config-path-0.1.1.tgz", - "integrity": "sha512-zy9cHePtMP0YhwG+CfHm0bgwdnga2X3gZexpdCwEj//dpb+TKajtiC8REEUJUSq6Ab4f9cgNy2l8ObXzCXFkEw==" + "integrity": "sha512-zy9cHePtMP0YhwG+CfHm0bgwdnga2X3gZexpdCwEj//dpb+TKajtiC8REEUJUSq6Ab4f9cgNy2l8ObXzCXFkEw==", + "license": "MIT" }, "node_modules/applicationinsights": { "version": "2.7.3", @@ -9580,7 +10399,8 @@ "node_modules/arg": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", - "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==" + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "license": "MIT" }, "node_modules/argparse": { "version": "1.0.10", @@ -9718,18 +10538,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/array.prototype.toreversed": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/array.prototype.toreversed/-/array.prototype.toreversed-1.1.2.tgz", - "integrity": "sha512-wwDCoT4Ck4Cz7sLtgUmzR5UV3YF5mFHUlbChCzZBQZ+0m2cl/DH3tKgvphv1nKgFsJ48oCSg6p91q2Vm0I/ZMA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0" - } - }, "node_modules/array.prototype.tosorted": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz", @@ -9791,6 +10599,13 @@ "node": ">=4" } }, + "node_modules/async": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", + "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==", + "dev": true, + "license": "MIT" + }, "node_modules/async-hook-jl": { "version": "1.7.6", "resolved": "https://registry.npmjs.org/async-hook-jl/-/async-hook-jl-1.7.6.tgz", @@ -9858,10 +10673,11 @@ } }, "node_modules/axios": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.2.tgz", - "integrity": "sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==", + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.5.tgz", + "integrity": "sha512-fZu86yCo+svH3uqJ/yTdQ0QHpQu5oL+/QE+QPSv6BZSkDAoky9vytxp7u5qk83OJFS3kEBcesWni9WTZAv3tSw==", "dev": true, + "license": "MIT", "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.0", @@ -10231,71 +11047,170 @@ "@babel/helper-define-polyfill-provider": "^0.6.1", "core-js-compat": "^3.36.1" }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" - } - }, - "node_modules/babel-plugin-polyfill-regenerator": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.1.tgz", - "integrity": "sha512-JfTApdE++cgcTWjsiCQlLyFBMbTUft9ja17saCc93lgV33h4tuCVj7tlvu//qpLwaG+3yEz7/KhahGrUMkVq9g==", - "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.6.1" + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-plugin-polyfill-regenerator": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.1.tgz", + "integrity": "sha512-JfTApdE++cgcTWjsiCQlLyFBMbTUft9ja17saCc93lgV33h4tuCVj7tlvu//qpLwaG+3yEz7/KhahGrUMkVq9g==", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.6.1" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-plugin-react-compiler": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-react-compiler/-/babel-plugin-react-compiler-0.0.0.tgz", + "integrity": "sha512-Kigl0V36a/6hLVH7+CCe1CCtU3mFBqBd829V//VtuG7I/pyq+B2QZJqOefd63snQmdfCryNhO9XW1FbGPBvYDA==", + "license": "MIT" + }, + "node_modules/babel-plugin-react-native-web": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/babel-plugin-react-native-web/-/babel-plugin-react-native-web-0.19.12.tgz", + "integrity": "sha512-eYZ4+P6jNcB37lObWIg0pUbi7+3PKoU1Oie2j0C8UF3cXyXoR74tO2NBjI/FORb2LJyItJZEAmjU5pSaJYEL1w==", + "license": "MIT" + }, + "node_modules/babel-plugin-transform-flow-enums": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-flow-enums/-/babel-plugin-transform-flow-enums-0.0.2.tgz", + "integrity": "sha512-g4aaCrDDOsWjbm0PUUeVnkcVd6AKJsVc/MbnPhEotEpkeJQP6b8nzewohQi7+QS8UyPehOhGWn0nOwjvWpmMvQ==", + "dependencies": { + "@babel/plugin-syntax-flow": "^7.12.1" + } + }, + "node_modules/babel-preset-current-node-syntax": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", + "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", + "dev": true, + "dependencies": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.8.3", + "@babel/plugin-syntax-import-meta": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.8.3", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-top-level-await": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/babel-preset-expo": { + "version": "11.0.12", + "resolved": "https://registry.npmjs.org/babel-preset-expo/-/babel-preset-expo-11.0.12.tgz", + "integrity": "sha512-hUuKdzSo8+H1oXQvKvlHRMHTxl+nN6YhFGlKiIxPa0E+gYfMEp8FnnStc/2Hwmip5rgJzQs6KF63KKRUc75xAg==", + "license": "MIT", + "dependencies": { + "@babel/plugin-proposal-decorators": "^7.12.9", + "@babel/plugin-transform-export-namespace-from": "^7.22.11", + "@babel/plugin-transform-object-rest-spread": "^7.12.13", + "@babel/plugin-transform-parameters": "^7.22.15", + "@babel/preset-react": "^7.22.15", + "@babel/preset-typescript": "^7.23.0", + "@react-native/babel-preset": "0.74.85", + "babel-plugin-react-compiler": "^0.0.0-experimental-592953e-20240517", + "babel-plugin-react-native-web": "~0.19.10", + "react-refresh": "^0.14.2" + } + }, + "node_modules/babel-preset-expo/node_modules/@react-native/babel-plugin-codegen": { + "version": "0.74.85", + "resolved": "https://registry.npmjs.org/@react-native/babel-plugin-codegen/-/babel-plugin-codegen-0.74.85.tgz", + "integrity": "sha512-48TSDclRB5OMXiImiJkLxyCfRyLsqkCgI8buugCZzvXcYslfV7gCvcyFyQldtcOmerV+CK4RAj7QS4hmB5Mr8Q==", + "license": "MIT", + "dependencies": { + "@react-native/codegen": "0.74.85" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/babel-preset-expo/node_modules/@react-native/babel-preset": { + "version": "0.74.85", + "resolved": "https://registry.npmjs.org/@react-native/babel-preset/-/babel-preset-0.74.85.tgz", + "integrity": "sha512-yMHUlN8INbK5BBwiBuQMftdWkpm1IgCsoJTKcGD2OpSgZhwwm8RUSvGhdRMzB2w7bsqqBmaEMleGtW6aCR7B9w==", + "license": "MIT", + "dependencies": { + "@babel/core": "^7.20.0", + "@babel/plugin-proposal-async-generator-functions": "^7.0.0", + "@babel/plugin-proposal-class-properties": "^7.18.0", + "@babel/plugin-proposal-export-default-from": "^7.0.0", + "@babel/plugin-proposal-logical-assignment-operators": "^7.18.0", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.0", + "@babel/plugin-proposal-numeric-separator": "^7.0.0", + "@babel/plugin-proposal-object-rest-spread": "^7.20.0", + "@babel/plugin-proposal-optional-catch-binding": "^7.0.0", + "@babel/plugin-proposal-optional-chaining": "^7.20.0", + "@babel/plugin-syntax-dynamic-import": "^7.8.0", + "@babel/plugin-syntax-export-default-from": "^7.0.0", + "@babel/plugin-syntax-flow": "^7.18.0", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.0.0", + "@babel/plugin-syntax-optional-chaining": "^7.0.0", + "@babel/plugin-transform-arrow-functions": "^7.0.0", + "@babel/plugin-transform-async-to-generator": "^7.20.0", + "@babel/plugin-transform-block-scoping": "^7.0.0", + "@babel/plugin-transform-classes": "^7.0.0", + "@babel/plugin-transform-computed-properties": "^7.0.0", + "@babel/plugin-transform-destructuring": "^7.20.0", + "@babel/plugin-transform-flow-strip-types": "^7.20.0", + "@babel/plugin-transform-function-name": "^7.0.0", + "@babel/plugin-transform-literals": "^7.0.0", + "@babel/plugin-transform-modules-commonjs": "^7.0.0", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.0.0", + "@babel/plugin-transform-parameters": "^7.0.0", + "@babel/plugin-transform-private-methods": "^7.22.5", + "@babel/plugin-transform-private-property-in-object": "^7.22.11", + "@babel/plugin-transform-react-display-name": "^7.0.0", + "@babel/plugin-transform-react-jsx": "^7.0.0", + "@babel/plugin-transform-react-jsx-self": "^7.0.0", + "@babel/plugin-transform-react-jsx-source": "^7.0.0", + "@babel/plugin-transform-runtime": "^7.0.0", + "@babel/plugin-transform-shorthand-properties": "^7.0.0", + "@babel/plugin-transform-spread": "^7.0.0", + "@babel/plugin-transform-sticky-regex": "^7.0.0", + "@babel/plugin-transform-typescript": "^7.5.0", + "@babel/plugin-transform-unicode-regex": "^7.0.0", + "@babel/template": "^7.0.0", + "@react-native/babel-plugin-codegen": "0.74.85", + "babel-plugin-transform-flow-enums": "^0.0.2", + "react-refresh": "^0.14.0" + }, + "engines": { + "node": ">=18" }, "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" - } - }, - "node_modules/babel-plugin-react-native-web": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/babel-plugin-react-native-web/-/babel-plugin-react-native-web-0.19.12.tgz", - "integrity": "sha512-eYZ4+P6jNcB37lObWIg0pUbi7+3PKoU1Oie2j0C8UF3cXyXoR74tO2NBjI/FORb2LJyItJZEAmjU5pSaJYEL1w==" - }, - "node_modules/babel-plugin-transform-flow-enums": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-flow-enums/-/babel-plugin-transform-flow-enums-0.0.2.tgz", - "integrity": "sha512-g4aaCrDDOsWjbm0PUUeVnkcVd6AKJsVc/MbnPhEotEpkeJQP6b8nzewohQi7+QS8UyPehOhGWn0nOwjvWpmMvQ==", - "dependencies": { - "@babel/plugin-syntax-flow": "^7.12.1" + "@babel/core": "*" } }, - "node_modules/babel-preset-current-node-syntax": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", - "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", - "dev": true, + "node_modules/babel-preset-expo/node_modules/@react-native/codegen": { + "version": "0.74.85", + "resolved": "https://registry.npmjs.org/@react-native/codegen/-/codegen-0.74.85.tgz", + "integrity": "sha512-N7QwoS4Hq/uQmoH83Ewedy6D0M7xbQsOU3OMcQf0eY3ltQ7S2hd9/R4UTalQWRn1OUJfXR6OG12QJ4FStKgV6Q==", + "license": "MIT", "dependencies": { - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.8.3", - "@babel/plugin-syntax-import-meta": "^7.8.3", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.8.3", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-top-level-await": "^7.8.3" + "@babel/parser": "^7.20.0", + "glob": "^7.1.1", + "hermes-parser": "0.19.1", + "invariant": "^2.2.4", + "jscodeshift": "^0.14.0", + "mkdirp": "^0.5.1", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">=18" }, "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/babel-preset-expo": { - "version": "11.0.10", - "resolved": "https://registry.npmjs.org/babel-preset-expo/-/babel-preset-expo-11.0.10.tgz", - "integrity": "sha512-YBg40Om31gw9IPlRw5v8elzgtPUtNEh4GSibBi5MsmmYddGg4VPjWtCZIFJChN543qRmbGb/fa/kejvLX567hQ==", - "dependencies": { - "@babel/plugin-proposal-decorators": "^7.12.9", - "@babel/plugin-transform-export-namespace-from": "^7.22.11", - "@babel/plugin-transform-object-rest-spread": "^7.12.13", - "@babel/plugin-transform-parameters": "^7.22.15", - "@babel/preset-react": "^7.22.15", - "@babel/preset-typescript": "^7.23.0", - "@react-native/babel-preset": "0.74.84", - "babel-plugin-react-native-web": "~0.19.10", - "react-refresh": "^0.14.2" + "@babel/preset-env": "^7.1.6" } }, "node_modules/babel-preset-jest": { @@ -10347,6 +11262,7 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/better-opn/-/better-opn-3.0.2.tgz", "integrity": "sha512-aVNobHnJqLiUelTaHat9DZ1qM2w0C0Eym4LPI/3JxOnSokGVdsl1T1kN7TFvsEAD8G47A6VKQ0TVHqbBnYMJlQ==", + "license": "MIT", "dependencies": { "open": "^8.0.4" }, @@ -10358,6 +11274,7 @@ "version": "8.4.2", "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", + "license": "MIT", "dependencies": { "define-lazy-prop": "^2.0.0", "is-docker": "^2.1.1", @@ -10426,11 +11343,12 @@ "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==" }, "node_modules/bplist-creator": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/bplist-creator/-/bplist-creator-0.1.0.tgz", - "integrity": "sha512-sXaHZicyEEmY86WyueLTQesbeoH/mquvarJaQNbjuOQO+7gbFcDEWqKmcWA4cOTLzFlfgvkiVxolk1k5bBIpmg==", + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/bplist-creator/-/bplist-creator-0.0.7.tgz", + "integrity": "sha512-xp/tcaV3T5PCiaY04mXga7o/TE+t95gqeLmADeBI1CvZtdWTbgBt3uLpvh4UWtenKeBhCV6oVxGk38yZr2uYEA==", + "license": "MIT", "dependencies": { - "stream-buffers": "2.2.x" + "stream-buffers": "~2.2.0" } }, "node_modules/bplist-parser": { @@ -10467,12 +11385,13 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==", - "dev": true + "dev": true, + "license": "BSD-2-Clause" }, "node_modules/browserslist": { - "version": "4.23.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz", - "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==", + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.3.tgz", + "integrity": "sha512-btwCFJVjI4YWDNfau8RhZ+B1Q/VLoUITrm3RlP6y1tYGWIOa+InuYiRGXUBXo8nA1qKmHMyLB/iVQg5TT4eFoA==", "funding": [ { "type": "opencollective", @@ -10487,11 +11406,12 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001587", - "electron-to-chromium": "^1.4.668", - "node-releases": "^2.0.14", - "update-browserslist-db": "^1.0.13" + "caniuse-lite": "^1.0.30001646", + "electron-to-chromium": "^1.5.4", + "node-releases": "^2.0.18", + "update-browserslist-db": "^1.1.0" }, "bin": { "browserslist": "cli.js" @@ -10547,6 +11467,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", + "license": "MIT", "dependencies": { "buffer-alloc-unsafe": "^1.1.0", "buffer-fill": "^1.0.0" @@ -10555,12 +11476,14 @@ "node_modules/buffer-alloc-unsafe": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz", - "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==" + "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==", + "license": "MIT" }, "node_modules/buffer-fill": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", - "integrity": "sha512-T7zexNBwiiaCOGDg9xNX9PBmjrubblRkENuptryuI64URkXDFum9il/JGL8Lm8wYfAXpredVXXZz7eMHilimiQ==" + "integrity": "sha512-T7zexNBwiiaCOGDg9xNX9PBmjrubblRkENuptryuI64URkXDFum9il/JGL8Lm8wYfAXpredVXXZz7eMHilimiQ==", + "license": "MIT" }, "node_modules/buffer-from": { "version": "1.1.2", @@ -10570,13 +11493,15 @@ "node_modules/builtins": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/builtins/-/builtins-1.0.3.tgz", - "integrity": "sha512-uYBjakWipfaO/bXI7E8rq6kpwHRZK5cNYrUv2OzZSI/FvmdMyXJ2tG9dKcjEC5YHmHpUAwsargWIZNWdxb/bnQ==" + "integrity": "sha512-uYBjakWipfaO/bXI7E8rq6kpwHRZK5cNYrUv2OzZSI/FvmdMyXJ2tG9dKcjEC5YHmHpUAwsargWIZNWdxb/bnQ==", + "license": "MIT" }, "node_modules/bunyamin": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/bunyamin/-/bunyamin-1.6.1.tgz", - "integrity": "sha512-gm44niEx19vp7XYB8nATtQ8WOK9lCU8fN3F2c9nXnD5TYr0qoxNZB1jxfzOYTz5he7lDBxrtvswSDrbiFf7WiA==", + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/bunyamin/-/bunyamin-1.6.3.tgz", + "integrity": "sha512-m1hAijFhu8pFiidsVc0XEDic46uxPK+mKNLqkb5mluNx0nTolNzx/DjwMqHChQWCgfOLMjKYJJ2uPTQLE6t4Ng==", "dev": true, + "license": "MIT", "dependencies": { "@flatten-js/interval-tree": "^1.1.2", "multi-sort-stream": "^1.0.4", @@ -10607,6 +11532,7 @@ "engines": [ "node >=0.10.0" ], + "license": "MIT", "bin": { "bunyan": "bin/bunyan" }, @@ -10622,6 +11548,7 @@ "resolved": "https://registry.npmjs.org/bunyan-debug-stream/-/bunyan-debug-stream-3.1.0.tgz", "integrity": "sha512-VaFYbDVdiSn3ZpdozrjZ8mFpxHXl26t11C1DKRQtbo0EgffqeFNrRLOGIESKVeGEvVu4qMxMSSxzNlSw7oTj7w==", "dev": true, + "license": "MIT", "dependencies": { "chalk": "^4.1.2" }, @@ -10637,6 +11564,7 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, + "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -10652,6 +11580,7 @@ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -10668,6 +11597,7 @@ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, + "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, @@ -10679,13 +11609,15 @@ "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 + "dev": true, + "license": "MIT" }, "node_modules/bunyan-debug-stream/node_modules/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, + "license": "MIT", "engines": { "node": ">=8" } @@ -10695,6 +11627,7 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, + "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -10711,9 +11644,10 @@ } }, "node_modules/cacache": { - "version": "18.0.3", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-18.0.3.tgz", - "integrity": "sha512-qXCd4rh6I07cnDqh8V48/94Tc/WSfj+o3Gn6NZ0aZovS255bUx8O13uKxRFd2eWG0xgsco7+YItQNPaa5E85hg==", + "version": "18.0.4", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-18.0.4.tgz", + "integrity": "sha512-B+L5iIa9mgcjLbliir2th36yEwPftrzteHYujzsx3dFP/31GCHcIeS8f5MGd80odLOjaOvSpU3EEAmRQptkxLQ==", + "license": "ISC", "dependencies": { "@npmcli/fs": "^3.1.0", "fs-minipass": "^3.0.0", @@ -10733,38 +11667,36 @@ } }, "node_modules/cacache/node_modules/glob": { - "version": "10.4.1", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.1.tgz", - "integrity": "sha512-2jelhlq3E4ho74ZyVLN03oKdAZVUa6UDZzFLVH1H7dnoax+y9qyaq8zBkfDIggjniU19z0wU18y16jMB2eyVIw==", + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "license": "ISC", "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" }, - "engines": { - "node": ">=16 || 14 >=14.18" - }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/cacache/node_modules/lru-cache": { - "version": "10.2.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.2.tgz", - "integrity": "sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==", - "engines": { - "node": "14 || >=16.14" - } + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "license": "ISC" }, "node_modules/cacache/node_modules/minipass": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "license": "ISC", "engines": { "node": ">=16 || 14 >=14.17" } @@ -10841,9 +11773,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001609", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001609.tgz", - "integrity": "sha512-JFPQs34lHKx1B5t1EpQpWH4c+29zIyn/haGsbpfq3suuV9v56enjFt23zqijxGTMwy1p/4H2tjnQMY+p1WoAyA==", + "version": "1.0.30001649", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001649.tgz", + "integrity": "sha512-fJegqZZ0ZX8HOWr6rcafGr72+xcgJKI9oWfDW5DrD7ExUtgZC7a7R7ZYmZqplh7XDocFdGeIFn7roAxhOeYrPQ==", "funding": [ { "type": "opencollective", @@ -10857,7 +11789,8 @@ "type": "github", "url": "https://github.com/sponsors/ai" } - ] + ], + "license": "CC-BY-4.0" }, "node_modules/chalk": { "version": "2.4.2", @@ -10920,6 +11853,7 @@ "version": "0.0.2", "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==", + "license": "BSD-3-Clause", "engines": { "node": "*" } @@ -10964,6 +11898,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "license": "ISC", "engines": { "node": ">=10" } @@ -11044,6 +11979,7 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "license": "MIT", "engines": { "node": ">=6" } @@ -11249,9 +12185,9 @@ }, "node_modules/commonmark": { "name": "@mattermost/commonmark", - "version": "0.30.1-2", - "resolved": "https://registry.npmjs.org/@mattermost/commonmark/-/commonmark-0.30.1-2.tgz", - "integrity": "sha512-r/xhNVt49pEFTjOgmKvjnPNM5RA8OztWsUn3CSQBcXiH2r36QipnR6qxU1hHo3XCteXkYDu9ypn+voA+jaN4Xg==", + "version": "0.30.1-3", + "resolved": "https://registry.npmjs.org/@mattermost/commonmark/-/commonmark-0.30.1-3.tgz", + "integrity": "sha512-Kjsl/sZmb6R6PtpPVIifPfqBMrKs7z6Tukb1TJl/S0LfC5uNici3yol4waGxhGsJuvCF2kZqStwVQP7ieUbAgw==", "dependencies": { "entities": "~3.0.1", "mdurl": "~1.0.1", @@ -11286,6 +12222,7 @@ "version": "1.2.2", "resolved": "https://registry.npmjs.org/component-type/-/component-type-1.2.2.tgz", "integrity": "sha512-99VUHREHiN5cLeHm3YLq312p6v+HUEcwtLCAtelvUDI6+SH5g5Cr85oNR2S1o6ywzL0ykMbuwLzM2ANocjEOIA==", + "license": "MIT", "funding": { "url": "https://github.com/sponsors/sindresorhus" } @@ -11379,11 +12316,12 @@ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" }, "node_modules/core-js-compat": { - "version": "3.36.1", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.36.1.tgz", - "integrity": "sha512-Dk997v9ZCt3X/npqzyGdTlq6t7lDBhZwGvV94PKzDArjp7BTRm7WlDAXYd/OWdeFHO8OChQYRJNJvUCqCbrtKA==", + "version": "3.38.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.38.0.tgz", + "integrity": "sha512-75LAicdLa4OJVwFxFbQR3NdnZjNgX6ILpVcVzcC4T2smerB5lELMrJQQQoWV6TiuC/vlaFqgU2tKQx9w5s0e0A==", + "license": "MIT", "dependencies": { - "browserslist": "^4.23.0" + "browserslist": "^4.23.3" }, "funding": { "type": "opencollective", @@ -11592,6 +12530,7 @@ "version": "0.0.2", "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==", + "license": "BSD-3-Clause", "engines": { "node": "*" } @@ -11600,6 +12539,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", + "license": "MIT", "engines": { "node": ">=8" } @@ -11679,7 +12619,8 @@ "node_modules/dag-map": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/dag-map/-/dag-map-1.0.2.tgz", - "integrity": "sha512-+LSAiGFwQ9dRnRdOeaj7g47ZFJcOUPukAP8J3A3fuZ1g9Y44BG+P1sgApjLXTQPOzC4+7S9Wr8kXsfpINM4jpw==" + "integrity": "sha512-+LSAiGFwQ9dRnRdOeaj7g47ZFJcOUPukAP8J3A3fuZ1g9Y44BG+P1sgApjLXTQPOzC4+7S9Wr8kXsfpINM4jpw==", + "license": "MIT" }, "node_modules/data-urls": { "version": "3.0.2", @@ -11820,6 +12761,7 @@ "version": "0.2.2", "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", + "license": "MIT", "engines": { "node": ">=0.10" } @@ -11873,6 +12815,7 @@ "version": "0.6.0", "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "license": "MIT", "engines": { "node": ">=4.0.0" } @@ -11895,6 +12838,7 @@ "version": "4.2.0", "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-4.2.0.tgz", "integrity": "sha512-h6sMrVB1VMWVrW13mSc6ia/DwYYw5MN6+exNu1OaJeFac5aSAvwM7lZ0NVfTABuSkQelr4h5oebg3KB1XPdjgA==", + "license": "BSD-2-Clause", "dependencies": { "execa": "^1.0.0", "ip-regex": "^2.1.0" @@ -11907,6 +12851,7 @@ "version": "6.0.5", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "license": "MIT", "dependencies": { "nice-try": "^1.0.4", "path-key": "^2.0.1", @@ -11922,6 +12867,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "license": "MIT", "dependencies": { "cross-spawn": "^6.0.0", "get-stream": "^4.0.0", @@ -11939,6 +12885,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "license": "MIT", "dependencies": { "pump": "^3.0.0" }, @@ -11950,6 +12897,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -11958,6 +12906,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", "integrity": "sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==", + "license": "MIT", "dependencies": { "path-key": "^2.0.0" }, @@ -11969,6 +12918,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", + "license": "MIT", "engines": { "node": ">=4" } @@ -11977,6 +12927,7 @@ "version": "5.7.2", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "license": "ISC", "bin": { "semver": "bin/semver" } @@ -11985,6 +12936,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", + "license": "MIT", "dependencies": { "shebang-regex": "^1.0.0" }, @@ -11996,6 +12948,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -12004,6 +12957,7 @@ "version": "1.3.1", "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "license": "ISC", "dependencies": { "isexe": "^2.0.0" }, @@ -12042,6 +12996,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "license": "MIT", "engines": { "node": ">=8" } @@ -12066,6 +13021,7 @@ "version": "6.1.1", "resolved": "https://registry.npmjs.org/del/-/del-6.1.1.tgz", "integrity": "sha512-ua8BhapfP0JUJKC/zV9yHHDW/rDoDxP4Zhn3AkA6/xT6gY7jYXJiaeyBZznYVujhZZET+UgcbZiQ7sN3WqcImg==", + "license": "MIT", "dependencies": { "globby": "^11.0.1", "graceful-fs": "^4.2.4", @@ -12087,6 +13043,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "license": "MIT", "engines": { "node": ">=8" } @@ -12154,6 +13111,7 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", + "license": "Apache-2.0", "bin": { "detect-libc": "bin/detect-libc.js" }, @@ -12171,11 +13129,12 @@ } }, "node_modules/detox": { - "version": "20.20.1", - "resolved": "https://registry.npmjs.org/detox/-/detox-20.20.1.tgz", - "integrity": "sha512-QGdmz+XZhJg1T2ySomdWDTSV7uRXn0WS438XGfC7f7fxePFrPOhxaJioXsDtTeGITWGr78lo2jehwYhRTjh4Pw==", + "version": "20.25.2", + "resolved": "https://registry.npmjs.org/detox/-/detox-20.25.2.tgz", + "integrity": "sha512-2M6jeVw5mEEg9Pk4GmmzxuASZ0dL138/fycyKAZ3mS/kF8ohnjg6Om/pw1gQaWWmFQ2BEwp6vHQtZMT0yTqdbQ==", "dev": true, "hasInstallScript": true, + "license": "MIT", "dependencies": { "ajv": "^8.6.3", "bunyan": "^1.8.12", @@ -12189,7 +13148,7 @@ "funpermaproxy": "^1.1.0", "glob": "^8.0.3", "ini": "^1.3.4", - "jest-environment-emit": "^1.0.5", + "jest-environment-emit": "^1.0.8", "json-cycle": "^1.3.0", "lodash": "^4.17.11", "multi-sort-stream": "^1.0.3", @@ -12536,6 +13495,7 @@ "version": "11.0.6", "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-11.0.6.tgz", "integrity": "sha512-8NHi73otpWsZGBSZwwknTXS5pqMOrk9+Ssrna8xCaxkzEpU9OTf9R5ArQGVw03//Zmk9MOwLPng9WwndvpAJ5g==", + "license": "BSD-2-Clause", "dependencies": { "dotenv": "^16.4.4" }, @@ -12552,6 +13512,7 @@ "integrity": "sha512-b7Z7cNtHPhH9EJhNNbbeqTcXB8LGFFZhq1PGgEvpeHlzd36bhbdTWoE/Ba/YguqpBSlAPKnARWhVlhunCMwfxg==", "dev": true, "hasInstallScript": true, + "license": "BSD-2-Clause", "optional": true, "dependencies": { "nan": "^2.14.0" @@ -12572,13 +13533,15 @@ "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "license": "MIT" }, "node_modules/easy-stack": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/easy-stack/-/easy-stack-1.0.1.tgz", "integrity": "sha512-wK2sCs4feiiJeFXn3zvY0p41mdU5VUgbgs1rNsc/y5ngFUijdWd+iIN8eoyuZHKB8xN6BL4PdWmzqFmxNg6V2w==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.0.0" } @@ -12588,10 +13551,27 @@ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, + "node_modules/ejs": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", + "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "jake": "^10.8.5" + }, + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/electron-to-chromium": { - "version": "1.4.735", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.735.tgz", - "integrity": "sha512-pkYpvwg8VyOTQAeBqZ7jsmpCjko1Qc6We1ZtZCjRyYbT5v4AIUKDy5cQTRotQlSSZmMr8jqpEt6JtOj5k7lR7A==" + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.4.tgz", + "integrity": "sha512-orzA81VqLyIGUEA77YkVA1D+N+nNfl2isJVjjmOyrlxuooZ19ynb+dOlaDTqd/idKRS9lDCSBmtzM+kyCsMnkA==", + "license": "ISC" }, "node_modules/emitter-listener": { "version": "1.1.2", @@ -12636,10 +13616,11 @@ } }, "node_modules/enhanced-resolve": { - "version": "5.16.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.16.0.tgz", - "integrity": "sha512-O+QWCviPNSSLAD9Ucn8Awv+poAkqn3T1XY5/N7kR7rQO9yfSGWkYZDwpJ+iKF7B8rxaQKWngSqACpgzeapSyoA==", + "version": "5.17.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz", + "integrity": "sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==", "dev": true, + "license": "MIT", "peer": true, "dependencies": { "graceful-fs": "^4.2.4", @@ -12664,6 +13645,7 @@ "version": "0.4.2", "resolved": "https://registry.npmjs.org/env-editor/-/env-editor-0.4.2.tgz", "integrity": "sha512-ObFo8v4rQJAE59M69QzwloxPZtd33TpYEIjtKD1rrFDcM1Gd7IkDxEBU+HriziN6HSHQnBJi8Dmy+JWkav5HKA==", + "license": "MIT", "engines": { "node": ">=8" } @@ -12682,7 +13664,8 @@ "node_modules/eol": { "version": "0.9.1", "resolved": "https://registry.npmjs.org/eol/-/eol-0.9.1.tgz", - "integrity": "sha512-Ds/TEoZjwggRoz/Q2O7SE3i4Jm66mqTDfmdHdq/7DKVk3bro9Q8h6WdXKdPqFLMoqxrDK5SVRzHVPOS6uuGtrg==" + "integrity": "sha512-Ds/TEoZjwggRoz/Q2O7SE3i4Jm66mqTDfmdHdq/7DKVk3bro9Q8h6WdXKdPqFLMoqxrDK5SVRzHVPOS6uuGtrg==", + "license": "MIT" }, "node_modules/err-code": { "version": "1.1.2", @@ -13192,18 +14175,19 @@ } }, "node_modules/eslint-plugin-jest": { - "version": "28.6.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-28.6.0.tgz", - "integrity": "sha512-YG28E1/MIKwnz+e2H7VwYPzHUYU4aMa19w0yGcwXnnmJH6EfgHahTJ2un3IyraUxNfnz/KUhJAFXNNwWPo12tg==", + "version": "28.7.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-28.7.0.tgz", + "integrity": "sha512-fzPGN7awL2ftVRQh/bsCi+16ArUZWujZnD1b8EGJqy8nr4//7tZ3BIdc/9edcJBtB3hpci3GtdMNFVDwHU0Eag==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/utils": "^6.0.0 || ^7.0.0" + "@typescript-eslint/utils": "^6.0.0 || ^7.0.0 || ^8.0.0" }, "engines": { "node": "^16.10.0 || ^18.12.0 || >=20.0.0" }, "peerDependencies": { - "@typescript-eslint/eslint-plugin": "^6.0.0 || ^7.0.0", + "@typescript-eslint/eslint-plugin": "^6.0.0 || ^7.0.0 || ^8.0.0", "eslint": "^7.0.0 || ^8.0.0 || ^9.0.0", "jest": "*" }, @@ -13238,35 +14222,36 @@ } }, "node_modules/eslint-plugin-react": { - "version": "7.34.3", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.34.3.tgz", - "integrity": "sha512-aoW4MV891jkUulwDApQbPYTVZmeuSyFrudpbTAQuj5Fv8VL+o6df2xIGpw8B0hPjAaih1/Fb0om9grCdyFYemA==", + "version": "7.35.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.35.0.tgz", + "integrity": "sha512-v501SSMOWv8gerHkk+IIQBkcGRGrO2nfybfj5pLxuJNFTPxxA3PSryhXTK+9pNbtkggheDdsC0E9Q8CuPk6JKA==", "dev": true, + "license": "MIT", "dependencies": { "array-includes": "^3.1.8", "array.prototype.findlast": "^1.2.5", "array.prototype.flatmap": "^1.3.2", - "array.prototype.toreversed": "^1.1.2", "array.prototype.tosorted": "^1.1.4", "doctrine": "^2.1.0", "es-iterator-helpers": "^1.0.19", "estraverse": "^5.3.0", + "hasown": "^2.0.2", "jsx-ast-utils": "^2.4.1 || ^3.0.0", "minimatch": "^3.1.2", "object.entries": "^1.1.8", "object.fromentries": "^2.0.8", - "object.hasown": "^1.1.4", "object.values": "^1.2.0", "prop-types": "^15.8.1", "resolve": "^2.0.0-next.5", "semver": "^6.3.1", - "string.prototype.matchall": "^4.0.11" + "string.prototype.matchall": "^4.0.11", + "string.prototype.repeat": "^1.0.0" }, "engines": { "node": ">=4" }, "peerDependencies": { - "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" } }, "node_modules/eslint-plugin-react-hooks": { @@ -13761,6 +14746,7 @@ "resolved": "https://registry.npmjs.org/event-pubsub/-/event-pubsub-4.3.0.tgz", "integrity": "sha512-z7IyloorXvKbFx9Bpie2+vMJKKx1fH1EN5yiTfp8CiLOTptSYy1g8H4yDpGlEdshL1PBiFtBHepF2cNsqeEeFQ==", "dev": true, + "license": "Unlicense", "engines": { "node": ">=4.0.0" } @@ -13786,7 +14772,8 @@ "node_modules/exec-async": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/exec-async/-/exec-async-2.2.0.tgz", - "integrity": "sha512-87OpwcEiMia/DeiKFzaQNBNFeN3XkkpYIh9FyOqq5mS2oKv3CBE67PXoEKcr6nodWdXNogTiQ0jE2NGuoffXPw==" + "integrity": "sha512-87OpwcEiMia/DeiKFzaQNBNFeN3XkkpYIh9FyOqq5mS2oKv3CBE67PXoEKcr6nodWdXNogTiQ0jE2NGuoffXPw==", + "license": "MIT" }, "node_modules/execa": { "version": "5.1.1", @@ -13828,6 +14815,7 @@ "resolved": "https://registry.npmjs.org/exeunt/-/exeunt-1.1.0.tgz", "integrity": "sha512-dd++Yn/0Fp+gtJ04YHov7MeAii+LFivJc6KqnJNfplzLVUkUDrfKoQDTLlCgzcW15vY5hKlHasWeIsQJ8agHsw==", "dev": true, + "license": "MPL-2.0", "engines": { "node": ">=0.10" } @@ -13858,23 +14846,24 @@ } }, "node_modules/expo": { - "version": "51.0.14", - "resolved": "https://registry.npmjs.org/expo/-/expo-51.0.14.tgz", - "integrity": "sha512-99BAMSYBH1aq1TIEJqM03kRpsZjN8OqZXDqYHRq9/PXT67axRUOvRjwMMLprnCmqkAVM7m7FpiECNWN4U0gvLQ==", + "version": "51.0.24", + "resolved": "https://registry.npmjs.org/expo/-/expo-51.0.24.tgz", + "integrity": "sha512-HoOuNIWXzS6Gxifcb0N+qRt5K6iR9YitQaWIVNB8elyupvQdyI566IMgMBiO45NgpO5es0sfFNNBasxBHLkbUw==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.20.0", - "@expo/cli": "0.18.19", - "@expo/config": "9.0.1", - "@expo/config-plugins": "8.0.5", - "@expo/metro-config": "0.18.7", + "@expo/cli": "0.18.26", + "@expo/config": "9.0.3", + "@expo/config-plugins": "8.0.8", + "@expo/metro-config": "0.18.10", "@expo/vector-icons": "^14.0.0", - "babel-preset-expo": "~11.0.10", - "expo-asset": "~10.0.9", + "babel-preset-expo": "~11.0.12", + "expo-asset": "~10.0.10", "expo-file-system": "~17.0.1", - "expo-font": "~12.0.7", + "expo-font": "~12.0.9", "expo-keep-awake": "~13.0.2", "expo-modules-autolinking": "1.11.1", - "expo-modules-core": "1.12.15", + "expo-modules-core": "1.12.20", "fbemitter": "^3.0.0", "whatwg-url-without-unicode": "8.0.0-3" }, @@ -13891,9 +14880,10 @@ } }, "node_modules/expo-asset": { - "version": "10.0.9", - "resolved": "https://registry.npmjs.org/expo-asset/-/expo-asset-10.0.9.tgz", - "integrity": "sha512-KX7LPtVf9eeMidUvYZafXZldrVdzfjZNKKFAjFvDy2twg7sTa2R0L4VdCXp32eGLWZyk+i/rpOUSbyD1YFyJnA==", + "version": "10.0.10", + "resolved": "https://registry.npmjs.org/expo-asset/-/expo-asset-10.0.10.tgz", + "integrity": "sha512-0qoTIihB79k+wGus9wy0JMKq7DdenziVx3iUkGvMAy2azscSgWH6bd2gJ9CGnhC6JRd3qTMFBL0ou/fx7WZl7A==", + "license": "MIT", "dependencies": { "expo-constants": "~16.0.0", "invariant": "^2.2.4", @@ -13907,6 +14897,7 @@ "version": "16.0.2", "resolved": "https://registry.npmjs.org/expo-constants/-/expo-constants-16.0.2.tgz", "integrity": "sha512-9tNY3OVO0jfiMzl7ngb6IOyR5VFzNoN5OOazUWoeGfmMqVB5kltTemRvKraK9JRbBKIw+SOYLEmF0sEqgFZ6OQ==", + "license": "MIT", "dependencies": { "@expo/config": "~9.0.0", "@expo/env": "~0.3.0" @@ -13968,9 +14959,10 @@ } }, "node_modules/expo-font": { - "version": "12.0.7", - "resolved": "https://registry.npmjs.org/expo-font/-/expo-font-12.0.7.tgz", - "integrity": "sha512-rbSdpjtT/A3M+u9xchR9tdD+5VGSxptUis7ngX5zfAVp3O5atOcPNSA82Jeo15HkrQE+w/upfFBOvi56lsGdsQ==", + "version": "12.0.9", + "resolved": "https://registry.npmjs.org/expo-font/-/expo-font-12.0.9.tgz", + "integrity": "sha512-seTCyf0tbgkAnp3ZI9ZfK9QVtURQUgFnuj+GuJ5TSnN0XsOtVe1s2RxTvmMgkfuvfkzcjJ69gyRpsZS1cC8hjw==", + "license": "MIT", "dependencies": { "fontfaceobserver": "^2.1.0" }, @@ -13979,9 +14971,10 @@ } }, "node_modules/expo-image": { - "version": "1.12.12", - "resolved": "https://registry.npmjs.org/expo-image/-/expo-image-1.12.12.tgz", - "integrity": "sha512-zZutUhKYqcqTH12o87pGCVLsuQeRK2vaNwxa8beznbDnmWevm3dmbOTCxaOhGgjyDxwcdwDa483Q4IKCXL6tBw==", + "version": "1.12.13", + "resolved": "https://registry.npmjs.org/expo-image/-/expo-image-1.12.13.tgz", + "integrity": "sha512-Dmuc5qmkIsl1nFj8C3Ux3wL2bN4QYW4dM9fkGA8kYiP5Fxf1lT36ldkHk2O2lPFRSFJDvLxT8Tz+7GTko5fzwQ==", + "license": "MIT", "peerDependencies": { "expo": "*" } @@ -14006,6 +14999,7 @@ "version": "1.11.1", "resolved": "https://registry.npmjs.org/expo-modules-autolinking/-/expo-modules-autolinking-1.11.1.tgz", "integrity": "sha512-2dy3lTz76adOl7QUvbreMCrXyzUiF8lygI7iFJLjgIQIVH+43KnFWE5zBumpPbkiaq0f0uaFpN9U0RGQbnKiMw==", + "license": "MIT", "dependencies": { "chalk": "^4.1.0", "commander": "^7.2.0", @@ -14021,6 +15015,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -14035,6 +15030,7 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -14050,6 +15046,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, @@ -14060,12 +15057,14 @@ "node_modules/expo-modules-autolinking/node_modules/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==" + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" }, "node_modules/expo-modules-autolinking/node_modules/commander": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "license": "MIT", "engines": { "node": ">= 10" } @@ -14074,6 +15073,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "license": "MIT", "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" @@ -14089,6 +15089,7 @@ "version": "9.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "license": "MIT", "dependencies": { "at-least-node": "^1.0.0", "graceful-fs": "^4.2.0", @@ -14103,6 +15104,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", "engines": { "node": ">=8" } @@ -14111,6 +15113,7 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "license": "MIT", "dependencies": { "p-locate": "^5.0.0" }, @@ -14125,6 +15128,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "license": "MIT", "dependencies": { "p-limit": "^3.0.2" }, @@ -14139,6 +15143,7 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -14147,9 +15152,10 @@ } }, "node_modules/expo-modules-core": { - "version": "1.12.15", - "resolved": "https://registry.npmjs.org/expo-modules-core/-/expo-modules-core-1.12.15.tgz", - "integrity": "sha512-VjDPIgUyhCZzf692NF4p2iFTsKAQMcU3jc0pg33eNvN/kdrJqkeucqCDuuwoNxg0vIBKtoqAJDuPnWiemldsTg==", + "version": "1.12.20", + "resolved": "https://registry.npmjs.org/expo-modules-core/-/expo-modules-core-1.12.20.tgz", + "integrity": "sha512-CCXjlgT8lDAufgt912P1W7TwD+KAylfIttc1Doh1a0hAfkdkUsDRmrgthnYrrxEo2ECVpbaB71Epp1bnZ1rRrA==", + "license": "MIT", "dependencies": { "invariant": "^2.2.4" } @@ -14178,72 +15184,6 @@ "expo": "*" } }, - "node_modules/expo/node_modules/@babel/code-frame": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", - "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", - "dependencies": { - "@babel/highlight": "^7.10.4" - } - }, - "node_modules/expo/node_modules/@expo/config": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/@expo/config/-/config-9.0.1.tgz", - "integrity": "sha512-0tjaXBstTbXmD4z+UMFBkh2SZFwilizSQhW6DlaTMnPG5ezuw93zSFEWAuEC3YzkpVtNQTmYzxAYjxwh6seOGg==", - "dependencies": { - "@babel/code-frame": "~7.10.4", - "@expo/config-plugins": "~8.0.0-beta.0", - "@expo/config-types": "^51.0.0-unreleased", - "@expo/json-file": "^8.3.0", - "getenv": "^1.0.0", - "glob": "7.1.6", - "require-from-string": "^2.0.2", - "resolve-from": "^5.0.0", - "semver": "^7.6.0", - "slugify": "^1.3.4", - "sucrase": "3.34.0" - } - }, - "node_modules/expo/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/expo/node_modules/glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/expo/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", @@ -14289,9 +15229,9 @@ "dev": true }, "node_modules/fast-xml-parser": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.3.6.tgz", - "integrity": "sha512-M2SovcRxD4+vC493Uc2GZVcZaj66CCJhWurC4viynVSTvrpErCShNcDz1lAho6n9REQKvL/ll4A4/fw6Y9z8nw==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.4.1.tgz", + "integrity": "sha512-xkjOecfnKGkSsOwtZ5Pz7Us/T6mrbPQrq0nh+aCO5V9nk5NLWmasAHumTKjiPJPWANe+kAZ84Jc8ooJkzZ88Sw==", "funding": [ { "type": "github", @@ -14302,6 +15242,7 @@ "url": "https://paypal.me/naturalintelligence" } ], + "license": "MIT", "dependencies": { "strnum": "^1.0.5" }, @@ -14384,7 +15325,8 @@ "node_modules/fetch-retry": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/fetch-retry/-/fetch-retry-4.1.1.tgz", - "integrity": "sha512-e6eB7zN6UBSwGVwrbWVH+gdLnkW9WwHhmq2YDK1Sh30pzx1onRVGBvogTlUeWxwTa+L86NYdo4hFkh7O8ZjSnA==" + "integrity": "sha512-e6eB7zN6UBSwGVwrbWVH+gdLnkW9WwHhmq2YDK1Sh30pzx1onRVGBvogTlUeWxwTa+L86NYdo4hFkh7O8ZjSnA==", + "license": "MIT" }, "node_modules/file-entry-cache": { "version": "6.0.1", @@ -14478,6 +15420,29 @@ "node": ">=10" } }, + "node_modules/filelist": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", + "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "minimatch": "^5.0.1" + } + }, + "node_modules/filelist/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -14493,6 +15458,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz", "integrity": "sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ==", + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -14635,7 +15601,8 @@ "node_modules/fontfaceobserver": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/fontfaceobserver/-/fontfaceobserver-2.3.0.tgz", - "integrity": "sha512-6FPvD/IVyT4ZlNe7Wcn5Fb/4ChigpucKYSvD6a+0iMoLn2inpo711eyIcKjmDtE5XNcgAkSH9uN/nfAeZzHEfg==" + "integrity": "sha512-6FPvD/IVyT4ZlNe7Wcn5Fb/4ChigpucKYSvD6a+0iMoLn2inpo711eyIcKjmDtE5XNcgAkSH9uN/nfAeZzHEfg==", + "license": "BSD-2-Clause" }, "node_modules/for-each": { "version": "0.3.3", @@ -14649,6 +15616,7 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.2.1.tgz", "integrity": "sha512-PXUUyLqrR2XCWICfv6ukppP96sdFwWbNEnfEMt7jNsISjMsvaLNinAHNDYyvkyU+SZG2BTSbT5NjG+vZslfGTA==", + "license": "ISC", "dependencies": { "cross-spawn": "^7.0.0", "signal-exit": "^4.0.1" @@ -14664,6 +15632,7 @@ "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "license": "MIT", "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -14677,6 +15646,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "license": "ISC", "engines": { "node": ">=14" }, @@ -14709,6 +15679,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/freeport-async/-/freeport-async-2.0.0.tgz", "integrity": "sha512-K7od3Uw45AJg00XUmy15+Hae2hOcgKcmN3/EF6Y7i01O0gaqiRx8sUSpsb9+BRNL8RPBrhzPsVfy8q9ADlJuWQ==", + "license": "MIT", "engines": { "node": ">=8" } @@ -14739,6 +15710,7 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-3.0.3.tgz", "integrity": "sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw==", + "license": "ISC", "dependencies": { "minipass": "^7.0.3" }, @@ -14750,6 +15722,7 @@ "version": "7.1.2", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "license": "ISC", "engines": { "node": ">=16 || 14 >=14.17" } @@ -14816,6 +15789,7 @@ "resolved": "https://registry.npmjs.org/funpermaproxy/-/funpermaproxy-1.1.0.tgz", "integrity": "sha512-2Sp1hWuO8m5fqeFDusyhKqYPT+7rGLw34N3qonDcdRP8+n7M7Gl/yKp/q7oCxnnJ6pWCectOmLFJpsMU/++KrQ==", "dev": true, + "license": "Apache-2.0", "engines": { "node": ">=8.3.0" } @@ -14962,6 +15936,7 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/get-port/-/get-port-3.2.0.tgz", "integrity": "sha512-x5UJKlgeUiNT8nyo/AcnwLnZuZNcSjSw0kogRB+Whd1fjjFq4B1hySFxSFWWSn4mIBzg3sRNUDFYc4g5gjPoLg==", + "license": "MIT", "engines": { "node": ">=4" } @@ -15133,6 +16108,7 @@ "version": "15.8.0", "resolved": "https://registry.npmjs.org/graphql/-/graphql-15.8.0.tgz", "integrity": "sha512-5gghUc24tP9HRznNpV2+FIoq3xKkj5dTQqf4v0CpdPbFVwFkWoxOM+o+2OC9ZSvjEMTjfmG9QT+gcvggTwW1zw==", + "license": "MIT", "engines": { "node": ">= 10.x" } @@ -15141,6 +16117,7 @@ "version": "2.12.6", "resolved": "https://registry.npmjs.org/graphql-tag/-/graphql-tag-2.12.6.tgz", "integrity": "sha512-FdSNcu2QQcWnM2VNvSCCDCVS5PpPqpzgFT8+GXzqJuoDd0CBncxCY278u4mhRO7tMgo2JjgJA5aZ+nWSQ/Z+xg==", + "license": "MIT", "dependencies": { "tslib": "^2.1.0" }, @@ -15267,6 +16244,14 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "bin": { + "he": "bin/he" + } + }, "node_modules/hermes-estree": { "version": "0.19.1", "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.19.1.tgz", @@ -15319,6 +16304,7 @@ "version": "3.0.8", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-3.0.8.tgz", "integrity": "sha512-aXpmwoOhRBrw6X3j0h5RloK4x1OzsxMPyxqIHyNfSe2pypkVTZFpEiRoSipPEPlMrh0HW/XsjkJ5WgnCirpNUw==", + "license": "ISC", "dependencies": { "lru-cache": "^6.0.0" }, @@ -15330,6 +16316,7 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "license": "ISC", "dependencies": { "yallist": "^4.0.0" }, @@ -15340,7 +16327,8 @@ "node_modules/hosted-git-info/node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC" }, "node_modules/html-encoding-sniffer": { "version": "3.0.0", @@ -15478,12 +16466,13 @@ } }, "node_modules/husky": { - "version": "9.0.11", - "resolved": "https://registry.npmjs.org/husky/-/husky-9.0.11.tgz", - "integrity": "sha512-AB6lFlbwwyIqMdHYhwPe+kjOC3Oc5P3nThEoW/AaO2BX3vJDjWPFxYLxokUZOo6RNX20He3AaT8sESs9NJcmEw==", + "version": "9.1.4", + "resolved": "https://registry.npmjs.org/husky/-/husky-9.1.4.tgz", + "integrity": "sha512-bho94YyReb4JV7LYWRWxZ/xr6TtOTt8cMfmQ39MQYJ7f/YE268s3GdghGwi+y4zAeqewE5zYLvuhV0M0ijsDEA==", "dev": true, + "license": "MIT", "bin": { - "husky": "bin.mjs" + "husky": "bin.js" }, "engines": { "node": ">=18" @@ -15657,6 +16646,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-4.3.0.tgz", "integrity": "sha512-S1zBo1D6zcsyuC6PMmY5+55YMILQ9av8lotMx447Bq6SAgo/sDK6y6uUKmuYhW7eacnIhFfsPmCNYdDzsnnDCg==", + "license": "MIT", "dependencies": { "default-gateway": "^4.2.0", "ipaddr.js": "^1.9.0" @@ -15722,6 +16712,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", "integrity": "sha512-58yWmlHpp7VYfcdTwMTvwMmqx/Elfxjd9RXTDyMsbL7lLWmhMylLEqiYVLKuLzOZqVgiWXD9MfR62Vv89VRxkw==", + "license": "MIT", "engines": { "node": ">=4" } @@ -15730,6 +16721,7 @@ "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", "engines": { "node": ">= 0.10" } @@ -15848,7 +16840,8 @@ "node_modules/is-buffer": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "license": "MIT" }, "node_modules/is-callable": { "version": "1.2.7", @@ -16015,6 +17008,7 @@ "version": "0.1.0", "resolved": "https://registry.npmjs.org/is-invalid-path/-/is-invalid-path-0.1.0.tgz", "integrity": "sha512-aZMG0T3F34mTg4eTdszcGXx54oiZ4NtHSft3hWNJMGJXUUqdIj3cOZuHcU0nCWWcY3jd7yRe/3AEm3vSNTpBGQ==", + "license": "MIT", "dependencies": { "is-glob": "^2.0.0" }, @@ -16026,6 +17020,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", "integrity": "sha512-7Q+VbVafe6x2T+Tu6NcOf6sRklazEPmBoB3IWk3WdGZM2iGUwU/Oe3Wtq5lSEkDTTlpp8yx+5t4pzO/i9Ty1ww==", + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -16034,6 +17029,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", "integrity": "sha512-a1dBeB19NXsf/E0+FHqkagizel/LQw2DjSQpvQrj3zT+jYPpaUCryPnrQajXKFLCMuf4I6FhRpaGtw4lPrG6Eg==", + "license": "MIT", "dependencies": { "is-extglob": "^1.0.0" }, @@ -16089,6 +17085,7 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==", + "license": "MIT", "engines": { "node": ">=6" } @@ -16235,6 +17232,7 @@ "version": "0.1.1", "resolved": "https://registry.npmjs.org/is-valid-path/-/is-valid-path-0.1.1.tgz", "integrity": "sha512-+kwPrVDu9Ms03L90Qaml+79+6DZHqHyRoANI6IsZJ/g8frhnfchDOBCa0RbQ6/kdHt5CS5OeIEyrYznNuVN+8A==", + "license": "MIT", "dependencies": { "is-invalid-path": "^0.1.0" }, @@ -16443,15 +17441,13 @@ } }, "node_modules/jackspeak": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.0.tgz", - "integrity": "sha512-JVYhQnN59LVPFCEcVa2C3CrEKYacvjRfqIQl+h8oi91aLYQVWRYbxjPcv1bUiUy/kLmQaANrYfNMCO3kuEDHfw==", + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "license": "BlueOak-1.0.0", "dependencies": { "@isaacs/cliui": "^8.0.2" }, - "engines": { - "node": ">=14" - }, "funding": { "url": "https://github.com/sponsors/isaacs" }, @@ -16459,6 +17455,125 @@ "@pkgjs/parseargs": "^0.11.0" } }, + "node_modules/jake": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.2.tgz", + "integrity": "sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "async": "^3.2.3", + "chalk": "^4.0.2", + "filelist": "^1.0.4", + "minimatch": "^3.1.2" + }, + "bin": { + "jake": "bin/cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jake/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jake/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/jake/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jake/node_modules/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, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jake/node_modules/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, + "license": "MIT" + }, + "node_modules/jake/node_modules/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, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/jake/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/jake/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/jest": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", @@ -17020,10 +18135,11 @@ } }, "node_modules/jest-environment-emit": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/jest-environment-emit/-/jest-environment-emit-1.0.7.tgz", - "integrity": "sha512-/0AYqbL3zrfRTtGyzTZwgRxQZiDXEM8ZUfY7Uscla/XGs9vszx4f0XTSZqAk3CQaiwYAoKvFZkB2vSKm1Q08fQ==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/jest-environment-emit/-/jest-environment-emit-1.0.8.tgz", + "integrity": "sha512-WNqvxBLH0yNojHJQ99Y21963aT7UTavxV3PgiBQFi8zwrlnKU6HvkB6LOvQrbk5I8mI8JEKvcoOrQOvBVMLIXQ==", "dev": true, + "license": "MIT", "dependencies": { "bunyamin": "^1.5.2", "bunyan": "^2.0.5", @@ -17070,6 +18186,7 @@ "engines": [ "node >=0.10.0" ], + "license": "MIT", "dependencies": { "exeunt": "1.1.0" }, @@ -17127,12 +18244,13 @@ } }, "node_modules/jest-expo": { - "version": "51.0.2", - "resolved": "https://registry.npmjs.org/jest-expo/-/jest-expo-51.0.2.tgz", - "integrity": "sha512-ijIcjEASh2uORA3DBubOiIJTrPZXp8J3FedaEdnZPT09FkyTH8tZXp/ZRv37LKUomGA5XEHDYR2FY3UMfdIa7g==", + "version": "51.0.3", + "resolved": "https://registry.npmjs.org/jest-expo/-/jest-expo-51.0.3.tgz", + "integrity": "sha512-r49OuS9X2S/dH+lSfNmarBS2L/tgvBhzOgKHYFyDJWo+Bb5uVs7Rg/GZal/RD/NDkKFJuByGAaW1F6zHYnjZnw==", "dev": true, + "license": "MIT", "dependencies": { - "@expo/config": "~9.0.0", + "@expo/config": "~9.0.0-beta.0", "@expo/json-file": "^8.3.0", "@jest/create-cache-key-function": "^29.2.1", "babel-jest": "^29.2.1", @@ -18448,7 +19566,8 @@ "node_modules/jimp-compact": { "version": "0.16.1", "resolved": "https://registry.npmjs.org/jimp-compact/-/jimp-compact-0.16.1.tgz", - "integrity": "sha512-dZ6Ra7u1G8c4Letq/B5EzAxj4tLFHL+cGtdpR+PVm4yzPDj+lCk+AbivWt1eOM+ikzkowtyV7qSqX6qr3t71Ww==" + "integrity": "sha512-dZ6Ra7u1G8c4Letq/B5EzAxj4tLFHL+cGtdpR+PVm4yzPDj+lCk+AbivWt1eOM+ikzkowtyV7qSqX6qr3t71Ww==", + "license": "MIT" }, "node_modules/joi": { "version": "17.13.1", @@ -18465,13 +19584,15 @@ "node_modules/join-component": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/join-component/-/join-component-1.1.0.tgz", - "integrity": "sha512-bF7vcQxbODoGK1imE2P9GS9aw4zD0Sd+Hni68IMZLj7zRnquH7dXUmMw9hDI5S/Jzt7q+IyTXN0rSg2GI0IKhQ==" + "integrity": "sha512-bF7vcQxbODoGK1imE2P9GS9aw4zD0Sd+Hni68IMZLj7zRnquH7dXUmMw9hDI5S/Jzt7q+IyTXN0rSg2GI0IKhQ==", + "license": "MIT" }, "node_modules/js-message": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/js-message/-/js-message-1.0.7.tgz", "integrity": "sha512-efJLHhLjIyKRewNS9EGZ4UpI8NguuL6fKkhRxVuMmrGV2xN/0APGdQYwLFky5w9naebSZ0OwAGp0G6/2Cg90rA==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.6.0" } @@ -18481,6 +19602,7 @@ "resolved": "https://registry.npmjs.org/js-queue/-/js-queue-2.0.2.tgz", "integrity": "sha512-pbKLsbCfi7kriM3s1J4DDCo7jQkI58zPLHi0heXPzPlj0hjUsm+FesPUbE0DSbIVIK503A36aUBoCN7eMFedkA==", "dev": true, + "license": "MIT", "dependencies": { "easy-stack": "^1.0.1" }, @@ -18762,6 +19884,7 @@ "version": "0.13.0", "resolved": "https://registry.npmjs.org/json-schema-deref-sync/-/json-schema-deref-sync-0.13.0.tgz", "integrity": "sha512-YBOEogm5w9Op337yb6pAT6ZXDqlxAsQCanM3grid8lMWNxRJO/zWEJi3ZzqDL8boWfwhTFym5EFrNgWwpqcBRg==", + "license": "MIT", "dependencies": { "clone": "^2.1.2", "dag-map": "~1.0.0", @@ -18780,6 +19903,7 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", + "license": "MIT", "engines": { "node": ">=0.8" } @@ -18788,6 +19912,7 @@ "version": "2.2.1", "resolved": "https://registry.npmjs.org/md5/-/md5-2.2.1.tgz", "integrity": "sha512-PlGG4z5mBANDGCKsYQe0CaUYHdZYZt8ZPZLmEt+Urf0W4GlpTX4HescwHU+dc9+Z/G/vZKYZYFrwgm9VxK6QOQ==", + "license": "BSD-3-Clause", "dependencies": { "charenc": "~0.0.1", "crypt": "~0.0.1", @@ -18977,6 +20102,7 @@ "version": "1.19.0", "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.19.0.tgz", "integrity": "sha512-yV5UR7og+Og7lQC+70DA7a8ta1uiOPnWPJfxa0wnxylev5qfo4P+4iMpzWAdYWOca4jdNQZii+bDL/l+4hUXIA==", + "license": "MPL-2.0", "dependencies": { "detect-libc": "^1.0.3" }, @@ -19005,6 +20131,7 @@ "cpu": [ "arm64" ], + "license": "MPL-2.0", "optional": true, "os": [ "darwin" @@ -19024,6 +20151,7 @@ "cpu": [ "x64" ], + "license": "MPL-2.0", "optional": true, "os": [ "darwin" @@ -19043,6 +20171,7 @@ "cpu": [ "arm" ], + "license": "MPL-2.0", "optional": true, "os": [ "linux" @@ -19062,6 +20191,7 @@ "cpu": [ "arm64" ], + "license": "MPL-2.0", "optional": true, "os": [ "linux" @@ -19081,6 +20211,7 @@ "cpu": [ "arm64" ], + "license": "MPL-2.0", "optional": true, "os": [ "linux" @@ -19100,6 +20231,7 @@ "cpu": [ "x64" ], + "license": "MPL-2.0", "optional": true, "os": [ "linux" @@ -19119,6 +20251,7 @@ "cpu": [ "x64" ], + "license": "MPL-2.0", "optional": true, "os": [ "linux" @@ -19138,6 +20271,7 @@ "cpu": [ "x64" ], + "license": "MPL-2.0", "optional": true, "os": [ "win32" @@ -19562,6 +20696,7 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==", + "license": "BSD-3-Clause", "dependencies": { "charenc": "0.0.2", "crypt": "0.0.2", @@ -19572,6 +20707,7 @@ "version": "3.2.3", "resolved": "https://registry.npmjs.org/md5-file/-/md5-file-3.2.3.tgz", "integrity": "sha512-3Tkp1piAHaworfcCgH0jKbTvj1jWWFgbvh2cXaNCgHwyTCBxxvD1Y04rmfpvdPm1P4oXMOpm6+2H7sr7v9v8Fw==", + "license": "MIT", "dependencies": { "buffer-alloc": "^1.1.0" }, @@ -19585,7 +20721,8 @@ "node_modules/md5hex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/md5hex/-/md5hex-1.0.0.tgz", - "integrity": "sha512-c2YOUbp33+6thdCUi34xIyOU/a7bvGKj/3DB1iaPMTuPHf/Q2d5s4sn1FaCOO43XkXggnb08y5W2PU8UNYNLKQ==" + "integrity": "sha512-c2YOUbp33+6thdCUi34xIyOU/a7bvGKj/3DB1iaPMTuPHf/Q2d5s4sn1FaCOO43XkXggnb08y5W2PU8UNYNLKQ==", + "license": "MIT" }, "node_modules/mdn-data": { "version": "2.0.14", @@ -19619,7 +20756,8 @@ "node_modules/memory-cache": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/memory-cache/-/memory-cache-0.2.0.tgz", - "integrity": "sha512-OcjA+jzjOYzKmKS6IQVALHLVz+rNTMPoJvCztFaZxwG14wtAW7VRZjwTQu06vKCYOxh4jVnik7ya0SXTB0W+xA==" + "integrity": "sha512-OcjA+jzjOYzKmKS6IQVALHLVz+rNTMPoJvCztFaZxwG14wtAW7VRZjwTQu06vKCYOxh4jVnik7ya0SXTB0W+xA==", + "license": "BSD-2-Clause" }, "node_modules/merge-stream": { "version": "2.0.0", @@ -20038,11 +21176,12 @@ "integrity": "sha512-kYmyrCirqJf3zZ9t/0wGgRZ4/ZJw//VwaRVGA75C4nhE60vtnIzhl9J9ndkX/h6hxSN7pjg/cE0VxbnNM+bnDQ==" }, "node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "license": "MIT", "dependencies": { - "braces": "^3.0.2", + "braces": "^3.0.3", "picomatch": "^2.3.1" }, "engines": { @@ -20061,9 +21200,10 @@ } }, "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "version": "1.53.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.53.0.tgz", + "integrity": "sha512-oHlN/w+3MQ3rba9rqFr6V/ypF10LSkdwUysQL7GkXoTgIWeV+tcXGA852TBxH+gsh8UWoyhR1hKcoMJTuWflpg==", + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -20079,6 +21219,15 @@ "node": ">= 0.6" } }, + "node_modules/mime-types/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/mimic-fn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", @@ -20131,6 +21280,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-2.0.1.tgz", "integrity": "sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw==", + "license": "ISC", "dependencies": { "minipass": "^7.0.3" }, @@ -20142,6 +21292,7 @@ "version": "7.1.2", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "license": "ISC", "engines": { "node": ">=16 || 14 >=14.17" } @@ -20150,6 +21301,7 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", + "license": "ISC", "dependencies": { "minipass": "^3.0.0" }, @@ -20161,6 +21313,7 @@ "version": "3.3.6", "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "license": "ISC", "dependencies": { "yallist": "^4.0.0" }, @@ -20171,12 +21324,14 @@ "node_modules/minipass-flush/node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC" }, "node_modules/minipass-pipeline": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", + "license": "ISC", "dependencies": { "minipass": "^3.0.0" }, @@ -20188,6 +21343,7 @@ "version": "3.3.6", "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "license": "ISC", "dependencies": { "yallist": "^4.0.0" }, @@ -20198,12 +21354,14 @@ "node_modules/minipass-pipeline/node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC" }, "node_modules/minizlib": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "license": "MIT", "dependencies": { "minipass": "^3.0.0", "yallist": "^4.0.0" @@ -20216,6 +21374,7 @@ "version": "3.3.6", "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "license": "ISC", "dependencies": { "yallist": "^4.0.0" }, @@ -20226,7 +21385,8 @@ "node_modules/minizlib/node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC" }, "node_modules/mj-context-menu": { "version": "0.6.1", @@ -20294,7 +21454,8 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/multi-sort-stream/-/multi-sort-stream-1.0.4.tgz", "integrity": "sha512-hAZ8JOEQFbgdLe8HWZbb7gdZg0/yAIHF00Qfo3kd0rXFv96nXe+/bPTrKHZ2QMHugGX4FiAyET1Lt+jiB+7Qlg==", - "dev": true + "dev": true, + "license": "bsd" }, "node_modules/multipipe": { "version": "4.0.0", @@ -20390,10 +21551,11 @@ } }, "node_modules/nan": { - "version": "2.19.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.19.0.tgz", - "integrity": "sha512-nO1xXxfh/RWNxfd/XPfbIfFk5vgLsAxUR9y5O0cHMJu/AW9U95JLXqthYHjEp+8gQ5p96K9jUp8nbVOxCdRbtw==", + "version": "2.20.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.20.0.tgz", + "integrity": "sha512-bk3gXBZDGILuuo/6sKtr0DQmSThYHLtNCdSdXk9YkxD/jK6X2vmCyyXBBxyqZ4XcnzTyYEAThfX3DCEnLf6igw==", "dev": true, + "license": "MIT", "optional": true }, "node_modules/nanoid": { @@ -20444,7 +21606,8 @@ "node_modules/nested-error-stacks": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/nested-error-stacks/-/nested-error-stacks-2.0.1.tgz", - "integrity": "sha512-SrQrok4CATudVzBS7coSz26QRSmlK9TzzoFbeKfcPBUFPjcQM9Rqvr/DlJkOrwI/0KcgvMub1n1g5Jt9EgRn4A==" + "integrity": "sha512-SrQrok4CATudVzBS7coSz26QRSmlK9TzzoFbeKfcPBUFPjcQM9Rqvr/DlJkOrwI/0KcgvMub1n1g5Jt9EgRn4A==", + "license": "MIT" }, "node_modules/nice-try": { "version": "1.0.5", @@ -20532,10 +21695,20 @@ "version": "1.3.1", "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", + "license": "(BSD-3-Clause OR GPL-2.0)", "engines": { "node": ">= 6.13.0" } }, + "node_modules/node-html-parser": { + "version": "6.1.13", + "resolved": "https://registry.npmjs.org/node-html-parser/-/node-html-parser-6.1.13.tgz", + "integrity": "sha512-qIsTMOY4C/dAa5Q5vsobRpOOvPfC4pB61UVW2uSwZNUp0QU/jCekTal1vMmbO0DgdHeLUJpv/ARmDqErVxA3Sg==", + "dependencies": { + "css-select": "^5.1.0", + "he": "1.2.0" + } + }, "node_modules/node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", @@ -20546,6 +21719,7 @@ "resolved": "https://registry.npmjs.org/node-ipc/-/node-ipc-9.2.1.tgz", "integrity": "sha512-mJzaM6O3xHf9VT8BULvJSbdVbmHUKRNOH7zDDkCrA1/T+CVjq2WVIDfLt0azZRXpgArJtl3rtmEozrbXPZ9GaQ==", "dev": true, + "license": "MIT", "dependencies": { "event-pubsub": "4.3.0", "js-message": "1.0.7", @@ -20556,9 +21730,10 @@ } }, "node_modules/node-releases": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", - "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==" + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", + "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", + "license": "MIT" }, "node_modules/node-stream-zip": { "version": "1.15.0", @@ -20593,6 +21768,7 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-7.0.0.tgz", "integrity": "sha512-xXxr8y5U0kl8dVkz2oK7yZjPBvqM2fwaO5l3Yg13p03v8+E3qQcD0JNhHzjL1vyGgxcKkD0cco+NLR72iuPk3g==", + "license": "ISC", "dependencies": { "hosted-git-info": "^3.0.2", "osenv": "^0.1.5", @@ -20604,6 +21780,7 @@ "version": "5.7.2", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "license": "ISC", "bin": { "semver": "bin/semver" } @@ -20751,23 +21928,6 @@ "node": ">= 0.4" } }, - "node_modules/object.hasown": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.4.tgz", - "integrity": "sha512-FZ9LZt9/RHzGySlBARE3VF+gE26TxR38SdmqOqliuTnl9wrKulaQs+4dee1V+Io8VfxqzAfHu6YuRgUy8OHoTg==", - "dev": true, - "dependencies": { - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/object.values": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.0.tgz", @@ -20948,6 +22108,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", "integrity": "sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ==", + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -21043,6 +22204,7 @@ "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", "deprecated": "This package is no longer supported.", + "license": "ISC", "dependencies": { "os-homedir": "^1.0.0", "os-tmpdir": "^1.0.0" @@ -21117,6 +22279,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "license": "MIT", "dependencies": { "aggregate-error": "^3.0.0" }, @@ -21135,6 +22298,12 @@ "node": ">=6" } }, + "node_modules/package-json-from-dist": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz", + "integrity": "sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==", + "license": "BlueOak-1.0.0" + }, "node_modules/pako": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz", @@ -21191,6 +22360,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/parse-png/-/parse-png-2.1.0.tgz", "integrity": "sha512-Nt/a5SfCLiTnQAjx3fHlqp8hRgTL3z7kTQZzvIMS9uCAepnCyjpdEc6M/sz69WqMBdaDBw9sF1F1UaHROYzGkQ==", + "license": "MIT", "dependencies": { "pngjs": "^3.3.0" }, @@ -21242,6 +22412,7 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/password-prompt/-/password-prompt-1.1.3.tgz", "integrity": "sha512-HkrjG2aJlvF0t2BMH0e2LB/EHf3Lcq3fNMzy4GYHcQblAvOl+QQji1Lx7WRBMqpVK8p+KR7bCg7oqAMXtdgqyw==", + "license": "0BSD", "dependencies": { "ansi-escapes": "^4.3.2", "cross-spawn": "^7.0.3" @@ -21251,6 +22422,7 @@ "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "license": "MIT", "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -21462,9 +22634,13 @@ } }, "node_modules/path-to-regexp": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.2.2.tgz", - "integrity": "sha512-GQX3SSMokngb36+whdpRXE+3f9V8UzyAorlYvOGx87ufGHehNTn5lCxrKtLyZ4Yl/wEKnNnr98ZzOwwDZV5ogw==" + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-7.1.0.tgz", + "integrity": "sha512-ZToe+MbUF4lBqk6dV8GKot4DKfzrxXsplOddH8zN3YK+qw9/McvP7+4ICjZvOne0jQhN4eJwHsX6tT0Ns19fvw==", + "license": "MIT", + "engines": { + "node": ">=16" + } }, "node_modules/path-type": { "version": "4.0.0", @@ -21475,9 +22651,10 @@ } }, "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", + "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", + "license": "ISC" }, "node_modules/picomatch": { "version": "2.3.1", @@ -21650,6 +22827,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/plist/-/plist-3.1.0.tgz", "integrity": "sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ==", + "license": "MIT", "dependencies": { "@xmldom/xmldom": "^0.8.8", "base64-js": "^1.5.1", @@ -21663,6 +22841,7 @@ "version": "0.8.10", "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.10.tgz", "integrity": "sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==", + "license": "MIT", "engines": { "node": ">=10.0.0" } @@ -21671,6 +22850,7 @@ "version": "15.1.1", "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz", "integrity": "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==", + "license": "MIT", "engines": { "node": ">=8.0" } @@ -21679,6 +22859,7 @@ "version": "3.4.0", "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-3.4.0.tgz", "integrity": "sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w==", + "license": "MIT", "engines": { "node": ">=4.0.0" } @@ -21692,9 +22873,9 @@ } }, "node_modules/postcss": { - "version": "8.4.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", - "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", + "version": "8.4.40", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.40.tgz", + "integrity": "sha512-YF2kKIUzAofPMpfH6hOi2cGnv/HrUlfucspc7pDyvv7kGdqXrfj8SCl/t8owkEgKEuu8ZcRjSOxFxVLqwChZ2Q==", "funding": [ { "type": "opencollective", @@ -21709,9 +22890,10 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { "nanoid": "^3.3.7", - "picocolors": "^1.0.0", + "picocolors": "^1.0.1", "source-map-js": "^1.2.0" }, "engines": { @@ -21759,6 +22941,7 @@ "version": "5.6.0", "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==", + "license": "MIT", "engines": { "node": ">=6" }, @@ -21943,6 +23126,7 @@ "version": "7.1.3", "resolved": "https://registry.npmjs.org/query-string/-/query-string-7.1.3.tgz", "integrity": "sha512-hh2WYhq4fi8+b+/2Kg9CEge4fDPvHS534aOOvOZeQ3+Vf2mCFsaFBYj0i+iXcAq6I9Vzp5fjMFBlONvayDC1qg==", + "license": "MIT", "dependencies": { "decode-uri-component": "^0.2.2", "filter-obj": "^1.1.0", @@ -21961,6 +23145,7 @@ "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.1.tgz", "integrity": "sha512-wkvS7mL/JMugcup3/rMitHmd9ecIGd2lhFhK9N3UUQ450h66d1r3Y9nvXzQAW1Lq+wyx61k/1pfKS5KuKiyEbg==", "deprecated": "The querystring API is considered Legacy. new code should use the URLSearchParams API instead.", + "license": "MIT", "engines": { "node": ">=0.4.x" } @@ -22019,6 +23204,7 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", "dependencies": { "deep-extend": "^0.6.0", "ini": "~1.3.0", @@ -22033,6 +23219,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -22049,9 +23236,10 @@ } }, "node_modules/react-devtools-core": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/react-devtools-core/-/react-devtools-core-5.2.0.tgz", - "integrity": "sha512-vZK+/gvxxsieAoAyYaiRIVFxlajb7KXhgBDV7OsoMzaAE+IqGpoxusBjIgq5ibqA2IloKu0p9n7tE68z1xs18A==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/react-devtools-core/-/react-devtools-core-5.3.1.tgz", + "integrity": "sha512-7FSb9meX0btdBQLwdFOwt6bGqvRPabmVMMslv8fgoSPqXyuGpgQe36kx8gR86XPw7aV1yVouTp6fyZ0EH+NfUw==", + "license": "MIT", "dependencies": { "shell-quote": "^1.6.1", "ws": "^7" @@ -22126,21 +23314,22 @@ "integrity": "sha512-txfpPCQYiazVdcbMRhatqWKcAxJweUu2wDXvts5/7Wyp6+Y9cHojqXHsLPEckzutfHlxZhG8Oiundbmp8Fd6eQ==" }, "node_modules/react-native": { - "version": "0.74.2", - "resolved": "https://registry.npmjs.org/react-native/-/react-native-0.74.2.tgz", - "integrity": "sha512-EBMBjPPL4/GjHMP4NqsZabT3gI5WU9cSmduABGAGrd8uIcmTZ5F2Ng9k6gFmRm7n8e8CULxDNu98ZpQfBjl7Bw==", + "version": "0.74.5", + "resolved": "https://registry.npmjs.org/react-native/-/react-native-0.74.5.tgz", + "integrity": "sha512-Bgg2WvxaGODukJMTZFTZBNMKVaROHLwSb8VAGEdrlvKwfb1hHg/3aXTUICYk7dwgAnb+INbGMwnF8yeAgIUmqw==", + "license": "MIT", "dependencies": { "@jest/create-cache-key-function": "^29.6.3", - "@react-native-community/cli": "13.6.8", - "@react-native-community/cli-platform-android": "13.6.8", - "@react-native-community/cli-platform-ios": "13.6.8", - "@react-native/assets-registry": "0.74.84", - "@react-native/codegen": "0.74.84", - "@react-native/community-cli-plugin": "0.74.84", - "@react-native/gradle-plugin": "0.74.84", - "@react-native/js-polyfills": "0.74.84", - "@react-native/normalize-colors": "0.74.84", - "@react-native/virtualized-lists": "0.74.84", + "@react-native-community/cli": "13.6.9", + "@react-native-community/cli-platform-android": "13.6.9", + "@react-native-community/cli-platform-ios": "13.6.9", + "@react-native/assets-registry": "0.74.87", + "@react-native/codegen": "0.74.87", + "@react-native/community-cli-plugin": "0.74.87", + "@react-native/gradle-plugin": "0.74.87", + "@react-native/js-polyfills": "0.74.87", + "@react-native/normalize-colors": "0.74.87", + "@react-native/virtualized-lists": "0.74.87", "abort-controller": "^3.0.0", "anser": "^1.4.9", "ansi-regex": "^5.0.0", @@ -22165,7 +23354,7 @@ "scheduler": "0.24.0-canary-efb381bbf-20230505", "stacktrace-parser": "^0.1.10", "whatwg-fetch": "^3.0.0", - "ws": "^6.2.3", + "ws": "^6.2.2", "yargs": "^17.6.2" }, "bin": { @@ -22239,14 +23428,14 @@ } }, "node_modules/react-native-gesture-handler": { - "version": "2.16.2", - "resolved": "https://registry.npmjs.org/react-native-gesture-handler/-/react-native-gesture-handler-2.16.2.tgz", - "integrity": "sha512-vGFlrDKlmyI+BT+FemqVxmvO7nqxU33cgXVsn6IKAFishvlG3oV2Ds67D5nPkHMea8T+s1IcuMm0bF8ntZtAyg==", + "version": "2.18.1", + "resolved": "https://registry.npmjs.org/react-native-gesture-handler/-/react-native-gesture-handler-2.18.1.tgz", + "integrity": "sha512-WF2fxQ5kTaxHghlkBM4YxO86SyGWVwrSNgJ1E8z/ZtL2xD5B3bg5agvuVFfOzvceC114yq71s6E9vKPz94ZxRw==", + "license": "MIT", "dependencies": { "@egjs/hammerjs": "^2.0.17", "hoist-non-react-statics": "^3.3.0", "invariant": "^2.2.4", - "lodash": "^4.17.21", "prop-types": "^15.7.2" }, "peerDependencies": { @@ -22258,6 +23447,7 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/react-native-haptic-feedback/-/react-native-haptic-feedback-2.2.0.tgz", "integrity": "sha512-3tqJOjCguWhIrX0nkURn4yw6kXdsSDjjrvZCRjKXYGlL28hdQmoW2okAHduDTD9FWj9lA+lHgwFWgGs4aFNN7A==", + "license": "MIT", "peerDependencies": { "react-native": ">=0.60.0" } @@ -22305,9 +23495,10 @@ "integrity": "sha512-SkRtd9McIl1Ss2XSWNLorG+KMEbgeVqX+gV+t3u1EAAqT8q2/OpRmRbxpneT2vnb/dMhiU7g6K/pf3nxLUXRvA==" }, "node_modules/react-native-localize": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/react-native-localize/-/react-native-localize-3.2.0.tgz", - "integrity": "sha512-Wi486WdDoBcDKUNxzr2/8f64ZwkWExlIvn0lcfFhsRNP2CVXGkjLyXhr3h6jf3Utkv5EmoFKiNqcmZn9kjYDxQ==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/react-native-localize/-/react-native-localize-3.2.1.tgz", + "integrity": "sha512-XZsK9+V98XygBkutaPVxTIrwV48wXKk4AQfbjUOmrrsGCqa8WzWsBuEafwWVCMOEQ5coQt0SfM3XKT0Qg7DFXw==", + "license": "MIT", "peerDependencies": { "react": ">=18.1.0", "react-native": ">=0.70.0", @@ -23115,9 +24306,10 @@ } }, "node_modules/react-native-navigation": { - "version": "7.40.0", - "resolved": "https://registry.npmjs.org/react-native-navigation/-/react-native-navigation-7.40.0.tgz", - "integrity": "sha512-ZOtRCEEMtN+LfLuUrOmow/M2u3TeL0sXYJZdTM5pjk32cr6XHRusxQrzGCdl/GvMLHcBMGk41K2VhHvSYb+oFg==", + "version": "7.40.1", + "resolved": "https://registry.npmjs.org/react-native-navigation/-/react-native-navigation-7.40.1.tgz", + "integrity": "sha512-GQ6Azdhf6EtkUVdYv/3hbid3tDiEQnJf47TL6Joc0R92DUEA3c1tpXG0z9Y/D7lzYFOaIBPg3efnMHrpk74GeA==", + "license": "MIT", "dependencies": { "hoist-non-react-statics": "3.x.x", "lodash": "4.17.x", @@ -23177,9 +24369,10 @@ } }, "node_modules/react-native-reanimated": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-3.12.0.tgz", - "integrity": "sha512-Bwa4jKL/QttnV8pQYiX+1N5a6XgL8yZtMyH+7UbEaW7ZeNCv1Pxp2PmYu/bP3LafCkmS9Yj/hY2adYNKe58g9A==", + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-3.14.0.tgz", + "integrity": "sha512-TAxLtCfRyC/nOLeWoX/8MhdIF+Fi1e1NbLhIgEm5Kv9/gioAwSNaqLUYxjIClU1RaLwSTE8iaiHNVhTU4TS9DA==", + "license": "MIT", "dependencies": { "@babel/plugin-transform-arrow-functions": "^7.0.0-0", "@babel/plugin-transform-nullish-coalescing-operator": "^7.0.0-0", @@ -23197,18 +24390,20 @@ } }, "node_modules/react-native-safe-area-context": { - "version": "4.10.5", - "resolved": "https://registry.npmjs.org/react-native-safe-area-context/-/react-native-safe-area-context-4.10.5.tgz", - "integrity": "sha512-Wyb0Nqw2XJ6oZxW/cK8k5q7/UAhg/wbEG6UVf89rQqecDZTDA5ic//P9J6VvJRVZerzGmxWQpVuM7f+PRYUM4g==", + "version": "4.10.8", + "resolved": "https://registry.npmjs.org/react-native-safe-area-context/-/react-native-safe-area-context-4.10.8.tgz", + "integrity": "sha512-Jx1lovhvIdYygg0UsMCBUJN0Wvj9GlA5bbcBLzjZf93uJpNHzaiHC4hR280+sNVK1+/pMHEyEkXVHDZE5JWn0w==", + "license": "MIT", "peerDependencies": { "react": "*", "react-native": "*" } }, "node_modules/react-native-screens": { - "version": "3.32.0", - "resolved": "https://registry.npmjs.org/react-native-screens/-/react-native-screens-3.32.0.tgz", - "integrity": "sha512-wybqZAHX7v8ipOXhh90CqGLkBHw5JYqKNRBX7R/b0c2WQisTOgu0M0yGwBMM6LyXRBT+4k3NTGHdDbpJVpq0yQ==", + "version": "3.34.0", + "resolved": "https://registry.npmjs.org/react-native-screens/-/react-native-screens-3.34.0.tgz", + "integrity": "sha512-8ri3Pd9QcpfXnVckOe/Lnto+BXmSPHV/Q0RB0XW0gDKsCv5wi5k7ez7g1SzgiYHl29MSdiqgjH30zUyOOowOaw==", + "license": "MIT", "dependencies": { "react-freeze": "^1.0.0", "warn-once": "^0.1.0" @@ -23244,12 +24439,14 @@ } }, "node_modules/react-native-svg": { - "version": "15.3.0", - "resolved": "https://registry.npmjs.org/react-native-svg/-/react-native-svg-15.3.0.tgz", - "integrity": "sha512-mBHu/fdlzUbpGX8SZFxgbKvK/sgqLfDLP8uh8G7Us+zJgdjO8OSEeqHQs+kPRdQmdLJQiqPJX2WXgCl7ToTWqw==", + "version": "15.4.0", + "resolved": "https://registry.npmjs.org/react-native-svg/-/react-native-svg-15.4.0.tgz", + "integrity": "sha512-zkBEbme/Dba4yqreg/oI2P6/6LrLywWY7HhaSwpU7Pb5COpTd2fV6/ShsgZz8GRFFdidUPwWmx01FITUsjhkmw==", + "license": "MIT", "dependencies": { "css-select": "^5.1.0", - "css-tree": "^1.1.3" + "css-tree": "^1.1.3", + "warn-once": "0.1.1" }, "peerDependencies": { "react": "*", @@ -23307,9 +24504,10 @@ } }, "node_modules/react-native-video": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/react-native-video/-/react-native-video-6.2.0.tgz", - "integrity": "sha512-7TWKGBkKrwR9V+SI6NB9YWWDZbto3fpT/02s8RnrFEac0h5aEJYe6NqTmz3TcFmIRSNepg2UsEvWGKNpWn4sCA==", + "version": "6.4.3", + "resolved": "https://registry.npmjs.org/react-native-video/-/react-native-video-6.4.3.tgz", + "integrity": "sha512-KDuIz/JsD3mYT1Ma2oDBE3GIEHRO3a9GzMAEV8pKMtIh29j7X2DLr2L9oD4Kv94ggYag/+RuZpOtftxnDHDoXA==", + "license": "MIT", "peerDependencies": { "react": "*", "react-native": "*" @@ -23328,9 +24526,10 @@ } }, "node_modules/react-native-webrtc": { - "version": "124.0.1", - "resolved": "https://registry.npmjs.org/react-native-webrtc/-/react-native-webrtc-124.0.1.tgz", - "integrity": "sha512-ne1AXNvpa3yC31SkzrtsqHhq7fjp10QTcl2xCGkVDOhzZtaf/2+c35xs1ps5zjUTIsx+XKh3mjAN7DKqteFDvg==", + "version": "124.0.3", + "resolved": "https://registry.npmjs.org/react-native-webrtc/-/react-native-webrtc-124.0.3.tgz", + "integrity": "sha512-uU+FxcAlBjLQ1EZnw3q+nje8xw+7hwZ7yDpSK4f1OJ25LrLA6fAjgxHCtMn9D20D9tR4+pRkBugBzkfqmboIFg==", + "license": "MIT", "dependencies": { "base64-js": "1.5.1", "debug": "4.3.4", @@ -24163,6 +25362,12 @@ "node": ">= 10.14.2" } }, + "node_modules/react-native/node_modules/@react-native/normalize-colors": { + "version": "0.74.87", + "resolved": "https://registry.npmjs.org/@react-native/normalize-colors/-/normalize-colors-0.74.87.tgz", + "integrity": "sha512-Xh7Nyk/MPefkb0Itl5Z+3oOobeG9lfLb7ZOY2DKpFnoCE1TzBmib9vMNdFaLdSxLIP+Ec6icgKtdzYg8QUPYzA==", + "license": "MIT" + }, "node_modules/react-native/node_modules/@types/yargs": { "version": "15.0.19", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.19.tgz", @@ -24581,7 +25786,8 @@ "node_modules/remove-trailing-slash": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/remove-trailing-slash/-/remove-trailing-slash-0.1.1.tgz", - "integrity": "sha512-o4S4Qh6L2jpnCy83ysZDau+VORNvnFw07CKSAymkd6ICNVEPisMyzlc00KlvvicsxKck94SEwhDnMNdICzO+tA==" + "integrity": "sha512-o4S4Qh6L2jpnCy83ysZDau+VORNvnFw07CKSAymkd6ICNVEPisMyzlc00KlvvicsxKck94SEwhDnMNdICzO+tA==", + "license": "MIT" }, "node_modules/require-directory": { "version": "2.1.1", @@ -24635,6 +25841,7 @@ "version": "1.7.1", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.7.1.tgz", "integrity": "sha512-c7rwLofp8g1U+h1KNyHL/jicrKg1Ek4q+Lr33AL65uZTinUZHe30D5HlyN5V9NW0JX1D5dXQ4jqW5l7Sy/kGfw==", + "license": "MIT", "dependencies": { "path-parse": "^1.0.5" } @@ -24828,9 +26035,10 @@ } }, "node_modules/sax": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.3.0.tgz", - "integrity": "sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA==" + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", + "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==", + "license": "ISC" }, "node_modules/saxes": { "version": "6.0.0", @@ -24875,6 +26083,7 @@ "version": "2.4.1", "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.4.1.tgz", "integrity": "sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q==", + "license": "MIT", "dependencies": { "@types/node-forge": "^1.3.0", "node-forge": "^1" @@ -24884,9 +26093,10 @@ } }, "node_modules/semver": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", - "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "license": "ISC", "bin": { "semver": "bin/semver.js" }, @@ -25169,12 +26379,22 @@ "version": "1.3.1", "resolved": "https://registry.npmjs.org/simple-plist/-/simple-plist-1.3.1.tgz", "integrity": "sha512-iMSw5i0XseMnrhtIzRb7XpQEXepa9xhWxGUojHBL43SIpQuDQkh3Wpy67ZbDzZVr6EKxvwVChnVpdl8hEVLDiw==", + "license": "MIT", "dependencies": { "bplist-creator": "0.1.0", "bplist-parser": "0.3.1", "plist": "^3.0.5" } }, + "node_modules/simple-plist/node_modules/bplist-creator": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/bplist-creator/-/bplist-creator-0.1.0.tgz", + "integrity": "sha512-sXaHZicyEEmY86WyueLTQesbeoH/mquvarJaQNbjuOQO+7gbFcDEWqKmcWA4cOTLzFlfgvkiVxolk1k5bBIpmg==", + "license": "MIT", + "dependencies": { + "stream-buffers": "2.2.x" + } + }, "node_modules/simple-swizzle": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", @@ -25258,6 +26478,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", + "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } @@ -25305,6 +26526,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz", "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==", + "license": "MIT", "dependencies": { "through": "2" }, @@ -25316,6 +26538,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz", "integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==", + "license": "MIT", "engines": { "node": ">=6" } @@ -25334,6 +26557,7 @@ "version": "10.0.6", "resolved": "https://registry.npmjs.org/ssri/-/ssri-10.0.6.tgz", "integrity": "sha512-MGrFH9Z4NP9Iyhqn16sDtBpRRNJ0Y2hNa6D65h736fVSaPCHr4DM4sWUNvVaSuC+0OBGhwsrydQwmgfg5LncqQ==", + "license": "ISC", "dependencies": { "minipass": "^7.0.3" }, @@ -25345,6 +26569,7 @@ "version": "7.1.2", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "license": "ISC", "engines": { "node": ">=16 || 14 >=14.17" } @@ -25460,6 +26685,7 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/stream-buffers/-/stream-buffers-2.2.0.tgz", "integrity": "sha512-uyQK/mx5QjHun80FLJTfaWE7JtwfRMKBLkMne6udYOmvH0CawotVa7TfgYHzAnpphn4+TweIx1QKMnRIbipmUg==", + "license": "Unlicense", "engines": { "node": ">= 0.10.0" } @@ -25468,13 +26694,15 @@ "version": "2.2.5", "resolved": "https://registry.npmjs.org/stream-chain/-/stream-chain-2.2.5.tgz", "integrity": "sha512-1TJmBx6aSWqZ4tx7aTpBDXK0/e2hhcNSTV8+CbFJtDjbb+I1mZ8lHit0Grw9GRT+6JbIrrDd8esncgBi8aBXGA==", - "dev": true + "dev": true, + "license": "BSD-3-Clause" }, "node_modules/stream-json": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/stream-json/-/stream-json-1.8.0.tgz", "integrity": "sha512-HZfXngYHUAr1exT4fxlbc1IOce1RYxp2ldeaf97LYCOPSoOqY/1Psp7iGvpb+6JIOgkra9zDYnPX01hGAHzEPw==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "stream-chain": "^2.2.5" } @@ -25483,6 +26711,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz", "integrity": "sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==", + "license": "MIT", "engines": { "node": ">=4" } @@ -25532,6 +26761,7 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -25544,12 +26774,14 @@ "node_modules/string-width-cjs/node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" }, "node_modules/string-width-cjs/node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", "engines": { "node": ">=8" } @@ -25664,6 +26896,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" }, @@ -25728,7 +26961,8 @@ "node_modules/structured-headers": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/structured-headers/-/structured-headers-0.4.1.tgz", - "integrity": "sha512-0MP/Cxx5SzeeZ10p/bZI0S6MpgD+yxAhi1BOQ34jgnMXsCq3j1t6tQnZu+KdlL7dvJTLT3g9xN8tl10TqgFMcg==" + "integrity": "sha512-0MP/Cxx5SzeeZ10p/bZI0S6MpgD+yxAhi1BOQ34jgnMXsCq3j1t6tQnZu+KdlL7dvJTLT3g9xN8tl10TqgFMcg==", + "license": "MIT" }, "node_modules/sucrase": { "version": "3.34.0", @@ -25810,6 +27044,7 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz", "integrity": "sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==", + "license": "MIT", "dependencies": { "has-flag": "^4.0.0", "supports-color": "^7.0.0" @@ -25822,6 +27057,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", "engines": { "node": ">=8" } @@ -25830,6 +27066,7 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -25859,6 +27096,7 @@ "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", "dev": true, + "license": "MIT", "peer": true, "engines": { "node": ">=6" @@ -25868,6 +27106,7 @@ "version": "6.2.1", "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", + "license": "ISC", "dependencies": { "chownr": "^2.0.0", "fs-minipass": "^2.0.0", @@ -25884,6 +27123,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "license": "ISC", "dependencies": { "minipass": "^3.0.0" }, @@ -25895,6 +27135,7 @@ "version": "3.3.6", "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "license": "ISC", "dependencies": { "yallist": "^4.0.0" }, @@ -25906,6 +27147,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "license": "ISC", "engines": { "node": ">=8" } @@ -25914,6 +27156,7 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "license": "MIT", "bin": { "mkdirp": "bin/cmd.js" }, @@ -25924,7 +27167,8 @@ "node_modules/tar/node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC" }, "node_modules/telnet-client": { "version": "1.2.8", @@ -26005,6 +27249,7 @@ "version": "0.7.1", "resolved": "https://registry.npmjs.org/tempy/-/tempy-0.7.1.tgz", "integrity": "sha512-vXPxwOyaNVi9nyczO16mxmHGpl6ASC5/TVhRRHpqeYHvKQm58EaWNvZXxAhR0lYYnBOQFjXjhzeLsaXdjxLjRg==", + "license": "MIT", "dependencies": { "del": "^6.0.0", "is-stream": "^2.0.0", @@ -26023,6 +27268,7 @@ "version": "0.16.0", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.16.0.tgz", "integrity": "sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg==", + "license": "(MIT OR CC0-1.0)", "engines": { "node": ">=10" }, @@ -26034,6 +27280,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", "integrity": "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==", + "license": "MIT", "dependencies": { "ansi-escapes": "^4.2.1", "supports-hyperlinks": "^2.0.0" @@ -26264,7 +27511,8 @@ "node_modules/through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==" + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", + "license": "MIT" }, "node_modules/through2": { "version": "2.0.5", @@ -26357,6 +27605,7 @@ "resolved": "https://registry.npmjs.org/trace-event-lib/-/trace-event-lib-1.4.1.tgz", "integrity": "sha512-TOgFolKG8JFY+9d5EohGWMvwvteRafcyfPWWNIqcuD1W/FUvxWcy2MSCZ/beYHM63oYPHYHCd3tkbgCctHVP7w==", "dev": true, + "license": "MIT", "dependencies": { "browser-process-hrtime": "^1.0.0" }, @@ -26376,6 +27625,7 @@ "version": "0.6.9", "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.6.9.tgz", "integrity": "sha512-7bBrcF+/LQzSgFmT0X5YclVqQxtv7TDJ1f8Wj7ibBu/U6BMLeOpUxuZjV7rMc44UtKxlnMFigdhFAIszSX1DMg==", + "license": "MIT", "dependencies": { "gopd": "^1.0.1", "typedarray.prototype.slice": "^1.0.3", @@ -26415,12 +27665,14 @@ "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==" }, "node_modules/ts-jest": { - "version": "29.1.5", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.1.5.tgz", - "integrity": "sha512-UuClSYxM7byvvYfyWdFI+/2UxMmwNyJb0NPkZPQE2hew3RurV7l7zURgOHAd/1I1ZdPpe3GUsXNXAcN8TFKSIg==", + "version": "29.2.4", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.2.4.tgz", + "integrity": "sha512-3d6tgDyhCI29HlpwIq87sNuI+3Q6GLTTCeYRHCs7vDz+/3GCMwEtV9jezLyl4ZtnBgx00I7hm8PCP8cTksMGrw==", "dev": true, + "license": "MIT", "dependencies": { "bs-logger": "0.x", + "ejs": "^3.1.10", "fast-json-stable-stringify": "2.x", "jest-util": "^29.0.0", "json5": "^2.2.3", @@ -26624,6 +27876,7 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/typedarray.prototype.slice/-/typedarray.prototype.slice-1.0.3.tgz", "integrity": "sha512-8WbVAQAUlENo1q3c3zZYuy5k9VzBQvp8AX9WOtbvyWlLM1v5JaSRmjubLjzHF4JFtptjH/5c/i95yaElvcjC0A==", + "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -26640,10 +27893,11 @@ } }, "node_modules/typescript": { - "version": "5.4.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", - "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", + "version": "5.5.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz", + "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==", "devOptional": true, + "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -26733,6 +27987,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-3.0.0.tgz", "integrity": "sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g==", + "license": "ISC", "dependencies": { "unique-slug": "^4.0.0" }, @@ -26744,6 +27999,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-4.0.0.tgz", "integrity": "sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ==", + "license": "ISC", "dependencies": { "imurmurhash": "^0.1.4" }, @@ -26755,6 +28011,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", + "license": "MIT", "dependencies": { "crypto-random-string": "^2.0.0" }, @@ -26788,9 +28045,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.0.13", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", - "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz", + "integrity": "sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==", "funding": [ { "type": "opencollective", @@ -26805,9 +28062,10 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0" + "escalade": "^3.1.2", + "picocolors": "^1.0.1" }, "bin": { "update-browserslist-db": "cli.js" @@ -26828,7 +28086,8 @@ "node_modules/url-join": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.0.tgz", - "integrity": "sha512-EGXjXJZhIHiQMK2pQukuFcL303nskqIRzWvPvV5O8miOfwoUb9G+a/Cld60kUyeaybEI94wvVClT10DtfeAExA==" + "integrity": "sha512-EGXjXJZhIHiQMK2pQukuFcL303nskqIRzWvPvV5O8miOfwoUb9G+a/Cld60kUyeaybEI94wvVClT10DtfeAExA==", + "license": "MIT" }, "node_modules/url-parse": { "version": "1.5.10", @@ -26840,9 +28099,10 @@ } }, "node_modules/use-latest-callback": { - "version": "0.1.9", - "resolved": "https://registry.npmjs.org/use-latest-callback/-/use-latest-callback-0.1.9.tgz", - "integrity": "sha512-CL/29uS74AwreI/f2oz2hLTW7ZqVeV5+gxFeGudzQrgkCytrHw33G4KbnQOrRlAEzzAFXi7dDLMC9zhWcVpzmw==", + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/use-latest-callback/-/use-latest-callback-0.2.1.tgz", + "integrity": "sha512-QWlq8Is8BGWBf883QOEQP5HWYX/kMI+JTbJ5rdtvJLmXTIh9XoHIO3PQcmQl8BU44VKxow1kbQUHa6mQSMALDQ==", + "license": "MIT", "peerDependencies": { "react": ">=16.8" } @@ -27047,6 +28307,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-3.0.0.tgz", "integrity": "sha512-M6w37eVCMMouJ9V/sdPGnC5H4uDr73/+xdq0FBLO3TFFX1+7wiUY6Es328NN+y43tmY+doUdN9g9J21vqB7iLw==", + "license": "ISC", "dependencies": { "builtins": "^1.0.3" } @@ -27125,22 +28386,22 @@ "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" }, "node_modules/webpack": { - "version": "5.91.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.91.0.tgz", - "integrity": "sha512-rzVwlLeBWHJbmgTC/8TvAcu5vpJNII+MelQpylD4jNERPwpBJOE2lEcko1zJX3QJeLjTTAnQxn/OJ8bjDzVQaw==", + "version": "5.94.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.94.0.tgz", + "integrity": "sha512-KcsGn50VT+06JH/iunZJedYGUJS5FGjow8wb9c0v5n1Om8O1g4L6LjtfxwlXIATopoQu+vOXXa7gYisWxCoPyg==", "dev": true, + "license": "MIT", "peer": true, "dependencies": { - "@types/eslint-scope": "^3.7.3", "@types/estree": "^1.0.5", "@webassemblyjs/ast": "^1.12.1", "@webassemblyjs/wasm-edit": "^1.12.1", "@webassemblyjs/wasm-parser": "^1.12.1", "acorn": "^8.7.1", - "acorn-import-assertions": "^1.9.0", + "acorn-import-attributes": "^1.9.5", "browserslist": "^4.21.10", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.16.0", + "enhanced-resolve": "^5.17.1", "es-module-lexer": "^1.2.1", "eslint-scope": "5.1.1", "events": "^3.2.0", @@ -27490,7 +28751,8 @@ "node_modules/wonka": { "version": "4.0.15", "resolved": "https://registry.npmjs.org/wonka/-/wonka-4.0.15.tgz", - "integrity": "sha512-U0IUQHKXXn6PFo9nqsHphVCE5m3IntqZNB9Jjn7EB1lrR7YTDY3YWgFvEvwniTzXSvOH/XMzAZaIfJF/LvHYXg==" + "integrity": "sha512-U0IUQHKXXn6PFo9nqsHphVCE5m3IntqZNB9Jjn7EB1lrR7YTDY3YWgFvEvwniTzXSvOH/XMzAZaIfJF/LvHYXg==", + "license": "MIT" }, "node_modules/wrap-ansi": { "version": "7.0.0", @@ -27513,6 +28775,7 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -27529,6 +28792,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -27543,6 +28807,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, @@ -27553,7 +28818,8 @@ "node_modules/wrap-ansi-cjs/node_modules/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==" + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" }, "node_modules/wrap-ansi/node_modules/ansi-styles": { "version": "4.3.0", @@ -27627,6 +28893,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/xcode/-/xcode-3.0.1.tgz", "integrity": "sha512-kCz5k7J7XbJtjABOvkc5lJmkiDh8VhjVCGNiqdKCscmVpdVUpEAyXv1xmCLkQJ5dsHqx3IPO4XW+NTDhU/fatA==", + "license": "Apache-2.0", "dependencies": { "simple-plist": "^1.1.0", "uuid": "^7.0.3" @@ -27639,6 +28906,7 @@ "version": "7.0.3", "resolved": "https://registry.npmjs.org/uuid/-/uuid-7.0.3.tgz", "integrity": "sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg==", + "license": "MIT", "bin": { "uuid": "dist/bin/uuid" } @@ -27701,6 +28969,7 @@ "version": "0.6.0", "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.6.0.tgz", "integrity": "sha512-eLTh0kA8uHceqesPqSE+VvO1CDDJWMwlQfB6LuN6T8w6MaDJ8Txm8P7s5cHD0miF0V+GGTZrDQfxPZQVsur33w==", + "license": "MIT", "dependencies": { "sax": ">=0.6.0", "xmlbuilder": "~11.0.0" @@ -27713,6 +28982,7 @@ "version": "11.0.1", "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "license": "MIT", "engines": { "node": ">=4.0" } diff --git a/package.json b/package.json index dbfb7f422d1..e83cd00385e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "mattermost-mobile", - "version": "2.18.0", + "version": "2.20.0", "description": "Mattermost Mobile with React Native", "repository": "git@github.com:mattermost/mattermost-mobile.git", "author": "Mattermost, Inc.", @@ -17,113 +17,114 @@ "@formatjs/intl-locale": "4.0.0", "@formatjs/intl-numberformat": "8.10.3", "@formatjs/intl-pluralrules": "5.2.14", - "@gorhom/bottom-sheet": "4.6.3", - "@mattermost/calls": "github:mattermost/calls-common#cc5e65f6c103138fd013d6fa45eb33bfe7544f2a", + "@gorhom/bottom-sheet": "4.6.4", + "@mattermost/calls": "github:mattermost/calls-common#1ce6defb1ee0c1e0f106ddff8f46c37d10d60b76", "@mattermost/compass-icons": "0.1.45", "@mattermost/hardware-keyboard": "file:./libraries/@mattermost/hardware-keyboard", "@mattermost/keyboard-tracker": "file:./libraries/@mattermost/keyboard-tracker", "@mattermost/react-native-emm": "1.5.0", - "@mattermost/react-native-network-client": "1.6.0", + "@mattermost/react-native-network-client": "1.7.2", "@mattermost/react-native-paste-input": "0.8.0", "@mattermost/react-native-turbo-log": "0.4.0", "@mattermost/rnshare": "file:./libraries/@mattermost/rnshare", "@mattermost/rnutils": "file:./libraries/@mattermost/rnutils", "@msgpack/msgpack": "2.8.0", "@nozbe/watermelondb": "0.27.1", - "@react-native-camera-roll/camera-roll": "7.8.1", + "@react-native-camera-roll/camera-roll": "7.8.3", "@react-native-clipboard/clipboard": "1.14.1", - "@react-native-community/datetimepicker": "8.1.1", + "@react-native-community/datetimepicker": "8.2.0", "@react-native-community/netinfo": "11.3.2", "@react-native-cookies/cookies": "6.2.1", - "@react-navigation/bottom-tabs": "6.5.20", - "@react-navigation/native": "6.1.17", - "@react-navigation/stack": "6.3.29", + "@react-navigation/bottom-tabs": "6.6.1", + "@react-navigation/native": "6.1.18", + "@react-navigation/stack": "6.4.1", "@rneui/base": "4.0.0-rc.8", - "@sentry/react-native": "5.24.1", + "@sentry/react-native": "5.27.0", "@stream-io/flat-list-mvcp": "0.10.3", "@voximplant/react-native-foreground-service": "3.0.2", "base-64": "1.0.0", - "commonmark": "npm:@mattermost/commonmark@0.30.1-2", + "commonmark": "npm:@mattermost/commonmark@0.30.1-3", "commonmark-react-renderer": "github:mattermost/commonmark-react-renderer#81b5d27509652bae50b4b510ede777dd3bd923cf", "deep-equal": "2.2.3", "deepmerge": "4.3.1", "emoji-regex": "10.3.0", - "expo": "51.0.14", + "expo": "51.0.24", "expo-application": "5.9.1", "expo-crypto": "13.0.2", "expo-device": "6.0.2", - "expo-image": "1.12.12", + "expo-image": "1.12.13", "expo-linear-gradient": "13.0.2", "expo-store-review": "7.0.2", "expo-video-thumbnails": "8.0.0", "expo-web-browser": "13.0.3", "fuse.js": "7.0.0", "html-entities": "2.5.2", - "mime-db": "1.52.0", + "mime-db": "1.53.0", "moment-timezone": "0.5.45", + "node-html-parser": "6.1.13", "pako": "2.1.0", - "path-to-regexp": "6.2.2", + "path-to-regexp": "7.1.0", "react": "18.2.0", "react-freeze": "1.0.4", "react-intl": "6.6.8", - "react-native": "0.74.2", + "react-native": "0.74.5", "react-native-background-timer": "2.4.1", "react-native-document-picker": "9.3.0", "react-native-dotenv": "3.4.11", "react-native-exception-handler": "2.10.10", "react-native-file-viewer": "2.1.5", - "react-native-gesture-handler": "2.16.2", + "react-native-gesture-handler": "2.18.1", "react-native-haptic-feedback": "2.2.0", "react-native-image-picker": "7.1.2", "react-native-incall-manager": "4.2.0", "react-native-keyboard-aware-scroll-view": "0.9.5", "react-native-keychain": "8.2.0", - "react-native-localize": "3.2.0", + "react-native-localize": "3.2.1", "react-native-math-view": "3.9.5", - "react-native-navigation": "7.40.0", + "react-native-navigation": "7.40.1", "react-native-notifications": "5.1.0", "react-native-performance": "5.1.2", "react-native-permissions": "4.1.5", - "react-native-reanimated": "3.12.0", - "react-native-safe-area-context": "4.10.5", - "react-native-screens": "3.32.0", + "react-native-reanimated": "3.14.0", + "react-native-safe-area-context": "4.10.8", + "react-native-screens": "3.34.0", "react-native-section-list-get-item-layout": "2.2.3", "react-native-shadow-2": "7.1.0", "react-native-share": "10.2.1", - "react-native-svg": "15.3.0", + "react-native-svg": "15.4.0", "react-native-vector-icons": "10.1.0", - "react-native-video": "6.2.0", + "react-native-video": "6.4.3", "react-native-walkthrough-tooltip": "1.6.0", - "react-native-webrtc": "124.0.1", + "react-native-webrtc": "124.0.3", "react-syntax-highlighter": "15.5.0", - "semver": "7.6.2", + "semver": "7.6.3", "tinycolor2": "1.6.0", "url-parse": "1.5.10" }, "devDependencies": { - "@babel/cli": "7.24.7", - "@babel/core": "7.24.7", - "@babel/eslint-parser": "7.24.7", + "@babel/cli": "7.24.8", + "@babel/core": "7.25.2", + "@babel/eslint-parser": "7.25.1", "@babel/plugin-proposal-class-properties": "7.18.6", "@babel/plugin-proposal-decorators": "7.24.7", - "@babel/plugin-transform-flow-strip-types": "7.24.7", + "@babel/plugin-transform-flow-strip-types": "7.25.2", "@babel/plugin-transform-runtime": "7.24.7", - "@babel/preset-env": "7.24.7", + "@babel/preset-env": "7.25.3", "@babel/preset-typescript": "7.24.7", "@babel/register": "7.24.6", - "@babel/runtime": "7.24.7", - "@react-native/babel-preset": "0.74.84", - "@react-native/eslint-config": "0.74.84", - "@react-native/metro-config": "0.74.84", - "@react-native/typescript-config": "0.74.84", + "@babel/runtime": "7.25.0", + "@react-native/babel-preset": "0.74.87", + "@react-native/eslint-config": "0.74.87", + "@react-native/metro-config": "0.74.87", + "@react-native/typescript-config": "0.74.87", "@testing-library/react-hooks": "8.0.1", - "@testing-library/react-native": "12.5.1", + "@testing-library/react-native": "12.5.2", "@types/base-64": "1.0.2", "@types/commonmark": "0.27.9", "@types/commonmark-react-renderer": "4.3.4", "@types/deep-equal": "1.0.4", "@types/jest": "29.5.12", - "@types/lodash": "4.17.5", + "@types/lodash": "4.17.7", "@types/mime-db": "1.43.5", "@types/pako": "2.0.3", "@types/querystringify": "2.0.2", @@ -136,34 +137,34 @@ "@types/tinycolor2": "1.4.6", "@types/tough-cookie": "4.0.5", "@types/url-parse": "1.4.11", - "@types/uuid": "9.0.8", + "@types/uuid": "10.0.0", "@typescript-eslint/eslint-plugin": "7.13.1", "@typescript-eslint/parser": "7.13.1", - "axios": "1.7.2", + "axios": "1.7.5", "axios-cookiejar-support": "5.0.2", "babel-jest": "29.7.0", "babel-loader": "9.1.3", "babel-plugin-module-resolver": "5.0.2", - "detox": "20.20.1", + "detox": "20.25.2", "eslint": "8.57.0", "eslint-plugin-header": "3.1.1", "eslint-plugin-import": "2.29.1", - "eslint-plugin-jest": "28.6.0", - "eslint-plugin-react": "7.34.3", + "eslint-plugin-jest": "28.7.0", + "eslint-plugin-react": "7.35.0", "eslint-plugin-react-hooks": "4.6.2", - "husky": "9.0.11", + "husky": "9.1.4", "isomorphic-fetch": "3.0.0", "jest": "29.7.0", "jest-cli": "29.7.0", - "jest-expo": "51.0.2", + "jest-expo": "51.0.3", "jetifier": "2.0.0", "mmjstool": "github:mattermost/mattermost-utilities#83b1b311972b8f5e750aae4019457a40abb5aa44", "nock": "13.5.4", "patch-package": "8.0.0", - "react-devtools-core": "5.2.0", + "react-devtools-core": "5.3.1", "tough-cookie": "4.1.4", - "ts-jest": "29.1.5", - "typescript": "5.4.5", + "ts-jest": "29.2.4", + "typescript": "5.5.4", "uuid": "10.0.0" }, "scripts": { diff --git a/patches/expo-image+1.12.12.patch b/patches/expo-image+1.12.12.patch deleted file mode 100644 index c1ae6c2e6c1..00000000000 --- a/patches/expo-image+1.12.12.patch +++ /dev/null @@ -1,22 +0,0 @@ -diff --git a/node_modules/expo-image/android/src/main/java/expo/modules/image/okhttp/ExpoImageOkHttpClientGlideModule.kt b/node_modules/expo-image/android/src/main/java/expo/modules/image/okhttp/ExpoImageOkHttpClientGlideModule.kt -index 071907c..e5c39f5 100644 ---- a/node_modules/expo-image/android/src/main/java/expo/modules/image/okhttp/ExpoImageOkHttpClientGlideModule.kt -+++ b/node_modules/expo-image/android/src/main/java/expo/modules/image/okhttp/ExpoImageOkHttpClientGlideModule.kt -@@ -70,8 +70,16 @@ data class GlideUrlWrapper(val glideUrl: GlideUrl) { - - @GlideModule - class ExpoImageOkHttpClientGlideModule : LibraryGlideModule() { -+ companion object { -+ var okHttpClient: OkHttpClient? = null -+ } -+ - override fun registerComponents(context: Context, glide: Glide, registry: Registry) { -- val client = OkHttpClient() -+ val client = if (okHttpClient != null) { -+ okHttpClient!! -+ } else { -+ OkHttpClient() -+ } - // We don't use the `GlideUrl` directly but we want to replace the default okhttp loader anyway - // to make sure that the app will use only one client. - registry.replace(GlideUrl::class.java, InputStream::class.java, OkHttpUrlLoader.Factory(client)) \ No newline at end of file diff --git a/patches/expo-image+1.12.13.patch b/patches/expo-image+1.12.13.patch new file mode 100644 index 00000000000..96a62a70a8e --- /dev/null +++ b/patches/expo-image+1.12.13.patch @@ -0,0 +1,57 @@ +diff --git a/node_modules/expo-image/android/src/main/java/expo/modules/image/okhttp/ExpoImageOkHttpClientGlideModule.kt b/node_modules/expo-image/android/src/main/java/expo/modules/image/okhttp/ExpoImageOkHttpClientGlideModule.kt +index 071907c..e5c39f5 100644 +--- a/node_modules/expo-image/android/src/main/java/expo/modules/image/okhttp/ExpoImageOkHttpClientGlideModule.kt ++++ b/node_modules/expo-image/android/src/main/java/expo/modules/image/okhttp/ExpoImageOkHttpClientGlideModule.kt +@@ -70,8 +70,16 @@ data class GlideUrlWrapper(val glideUrl: GlideUrl) { + + @GlideModule + class ExpoImageOkHttpClientGlideModule : LibraryGlideModule() { ++ companion object { ++ var okHttpClient: OkHttpClient? = null ++ } ++ + override fun registerComponents(context: Context, glide: Glide, registry: Registry) { +- val client = OkHttpClient() ++ val client = if (okHttpClient != null) { ++ okHttpClient!! ++ } else { ++ OkHttpClient() ++ } + // We don't use the `GlideUrl` directly but we want to replace the default okhttp loader anyway + // to make sure that the app will use only one client. + registry.replace(GlideUrl::class.java, InputStream::class.java, OkHttpUrlLoader.Factory(client)) +diff --git a/node_modules/expo-image/ios/ExpoImage.podspec b/node_modules/expo-image/ios/ExpoImage.podspec +index de8bc65..564397d 100644 +--- a/node_modules/expo-image/ios/ExpoImage.podspec ++++ b/node_modules/expo-image/ios/ExpoImage.podspec +@@ -18,9 +18,7 @@ Pod::Spec.new do |s| + s.dependency 'ExpoModulesCore' + s.dependency 'SDWebImage', '~> 5.19.1' + s.dependency 'SDWebImageWebPCoder', '~> 0.14.6' +- s.dependency 'SDWebImageAVIFCoder', '~> 0.11.0' + s.dependency 'SDWebImageSVGCoder', '~> 1.7.0' +- s.dependency 'libavif/libdav1d' + + # Swift/Objective-C compatibility + s.pod_target_xcconfig = { +diff --git a/node_modules/expo-image/ios/ImageModule.swift b/node_modules/expo-image/ios/ImageModule.swift +index e4b7fca..5057b0a 100644 +--- a/node_modules/expo-image/ios/ImageModule.swift ++++ b/node_modules/expo-image/ios/ImageModule.swift +@@ -3,7 +3,6 @@ + import ExpoModulesCore + import SDWebImage + import SDWebImageWebPCoder +-import SDWebImageAVIFCoder + import SDWebImageSVGCoder + + public final class ImageModule: Module { +@@ -193,7 +192,7 @@ public final class ImageModule: Module { + // This coder is much slower, but it's the only one that works in iOS 13 + SDImageCodersManager.shared.addCoder(SDImageWebPCoder.shared) + } +- SDImageCodersManager.shared.addCoder(SDImageAVIFCoder.shared) ++ + SDImageCodersManager.shared.addCoder(SDImageSVGCoder.shared) + SDImageCodersManager.shared.addCoder(SDImageHEICCoder.shared) + } diff --git a/patches/expo-modules-core+1.12.15.patch b/patches/expo-modules-core+1.12.20.patch similarity index 100% rename from patches/expo-modules-core+1.12.15.patch rename to patches/expo-modules-core+1.12.20.patch diff --git a/patches/jest-expo+51.0.2.patch b/patches/jest-expo+51.0.3.patch similarity index 100% rename from patches/jest-expo+51.0.2.patch rename to patches/jest-expo+51.0.3.patch diff --git a/patches/react-native-navigation+7.40.0.patch b/patches/react-native-navigation+7.40.1.patch similarity index 100% rename from patches/react-native-navigation+7.40.0.patch rename to patches/react-native-navigation+7.40.1.patch diff --git a/patches/react-native-safe-area-context+4.10.5.patch b/patches/react-native-safe-area-context+4.10.8.patch similarity index 100% rename from patches/react-native-safe-area-context+4.10.5.patch rename to patches/react-native-safe-area-context+4.10.8.patch diff --git a/patches/react-native-svg+15.3.0.patch b/patches/react-native-svg+15.4.0.patch similarity index 100% rename from patches/react-native-svg+15.3.0.patch rename to patches/react-native-svg+15.4.0.patch diff --git a/patches/react-native-video+6.2.0.patch b/patches/react-native-video+6.4.3.patch similarity index 100% rename from patches/react-native-video+6.2.0.patch rename to patches/react-native-video+6.4.3.patch diff --git a/patches/react-native-webrtc+124.0.1.patch b/patches/react-native-webrtc+124.0.3.patch similarity index 100% rename from patches/react-native-webrtc+124.0.1.patch rename to patches/react-native-webrtc+124.0.3.patch diff --git a/share_extension/components/content_view/link_preview/index.tsx b/share_extension/components/content_view/link_preview/index.tsx index cd32aa2c0d7..030d60898ca 100644 --- a/share_extension/components/content_view/link_preview/index.tsx +++ b/share_extension/components/content_view/link_preview/index.tsx @@ -6,7 +6,7 @@ import React, {useCallback, useEffect, useState} from 'react'; import {ActivityIndicator, type StyleProp, Text, View, type ViewStyle} from 'react-native'; import CompassIcon from '@components/compass_icon'; -import fetchOpenGraph, {type OpenGraph} from '@share/open_graph'; +import {fetchOpenGraph, type OpenGraph} from '@utils/opengraph'; import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme'; import {typography} from '@utils/typography'; diff --git a/share_extension/index.tsx b/share_extension/index.tsx index 47c3ed710a9..f541d9cb07e 100644 --- a/share_extension/index.tsx +++ b/share_extension/index.tsx @@ -11,6 +11,7 @@ import {Appearance, BackHandler} from 'react-native'; import {getDefaultThemeByAppearance} from '@context/theme'; import {DEFAULT_LOCALE, getTranslations} from '@i18n'; import {initialize} from '@init/app'; +import PerformanceMetricsManager from '@managers/performance_metrics_manager'; import {extractStartLink, isValidUrl} from '@utils/url'; import ChannelsScreen from './screens/channels'; @@ -63,6 +64,13 @@ const ShareExtension = () => { return data?.filter((i) => !i.isString) || []; }, [data]); + useEffect(() => { + // Since the share functionality inits the app, the init mark gets set + // at this point. Therefore, any check on load times after this is done + // over the wrong value. + PerformanceMetricsManager.skipLoadMetric(); + }, []); + useEffect(() => { initialize().finally(async () => { const items = await MattermostShare.getSharedData(); diff --git a/share_extension/open_graph/index.ts b/share_extension/open_graph/index.ts deleted file mode 100644 index 465286df9bd..00000000000 --- a/share_extension/open_graph/index.ts +++ /dev/null @@ -1,123 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -import GenericClient from '@mattermost/react-native-network-client'; -import {decode} from 'html-entities'; - -export type OpenGraph = { - link: string; - title?: string; - imageURL?: string; - error?: any; -} - -const metaTags: Record = { - title: 'title', - description: 'description', - ogUrl: 'og:url', - ogType: 'og:type', - ogTitle: 'og:title', - ogDescription: 'og:description', - ogImage: 'og:image', - ogVideo: 'og:video', - ogVideoType: 'og:video:type', - ogVideoWidth: 'og:video:width', - ogVideoHeight: 'og:video:height', - ogVideoUrl: 'og:video:url', - twitterPlayer: 'twitter:player', - twitterPlayerWidth: 'twitter:player:width', - twitterPlayerHeight: 'twitter:player:height', - twitterPlayerStream: 'twitter:player:stream', - twitterCard: 'twitter:card', - twitterDomain: 'twitter:domain', - twitterUrl: 'twitter:url', - twitterTitle: 'twitter:title', - twitterDescription: 'twitter:description', - twitterImage: 'twitter:image', -}; - -const fetchRaw = async (url: string) => { - try { - const res = await GenericClient.get(url, { - headers: { - 'User-Agent': 'OpenGraph', - 'Cache-Control': 'no-cache', - Accept: '*/*', - Connection: 'keep-alive', - }, - }); - - if (!res.ok) { - return res; - } - - return res.data as any; - } catch (error: any) { - return {message: error.message}; - } -}; - -const fetchOpenGraph = async (url: string): Promise => { - const { - ogTitle, - ogImage, - } = metaTags; - - try { - const html = await fetchRaw(url); - let siteTitle = ''; - - const tagTitle = html.match( - /]*>[\r\n\t\s]*([^<]+)[\r\n\t\s]*<\/title>/gim, - ); - siteTitle = tagTitle[0].replace( - /]*>[\r\n\t\s]*([^<]+)[\r\n\t\s]*<\/title>/gim, - '$1', - ); - - const og = []; - const metas: any = html.match(/]+>/gim); - - // There is no else statement - /* istanbul ignore else */ - if (metas) { - for (let meta of metas) { - meta = meta.replace(/\s*\/?>$/, ' />'); - const zname = meta.replace(/[\s\S]*(property|name)\s*=\s*([\s\S]+)/, '$2'); - const name = (/^["']/).test(zname) ? zname.substr(1, zname.slice(1).indexOf(zname[0])) : zname.substr(0, zname.search(/[\s\t]/g)); - const valid = Boolean(Object.keys(metaTags).filter((m: any) => metaTags[m].toLowerCase() === name.toLowerCase()).length); - - // There is no else statement - /* istanbul ignore else */ - if (valid) { - const zcontent = meta.replace(/[\s\S]*(content)\s*=\s*([\s\S]+)/, '$2'); - const content = (/^["']/).test(zcontent) ? zcontent.substr(1, zcontent.slice(1).indexOf(zcontent[0])) : zcontent.substr(0, zcontent.search(/[\s\t]/g)); - og.push({name, value: content === 'undefined' ? null : content}); - } - } - } - - const result: OpenGraph = {link: url}; - const data = og.reduce( - (chain: any, meta: any) => ({...chain, [meta.name]: decode(meta.value)}), - {url}, - ); - - // Image - result.imageURL = data[ogImage] ? data[ogImage] : null; - - // Title - data[ogTitle] = data[ogTitle] ? data[ogTitle] : siteTitle; - - result.title = data[ogTitle]; - - return result; - } catch (error: any) { - return { - link: url, - error, - }; - } -}; - -export default fetchOpenGraph; diff --git a/test/setup.ts b/test/setup.ts index 55f86f577b4..2243502e752 100644 --- a/test/setup.ts +++ b/test/setup.ts @@ -150,6 +150,8 @@ jest.doMock('react-native', () => { removeChannelNotifications: jest.fn().mockImplementation(), removeThreadNotifications: jest.fn().mockImplementation(), removeServerNotifications: jest.fn().mockImplementation(), + + unlockOrientation: jest.fn(), }, APIClient: { getConstants: () => ({ @@ -289,6 +291,7 @@ jest.mock('react-native-navigation', () => { mergeOptions: jest.fn(), showOverlay: jest.fn(), dismissOverlay: jest.fn(), + updateProps: jest.fn(), }, }; }); @@ -320,6 +323,7 @@ jest.mock('react-native-notifications', () => { }), setBadgeCount: jest.fn(), }, + postLocalNotification: jest.fn((notification) => notification), }, }; }); diff --git a/types/api/channels.d.ts b/types/api/channels.d.ts index b176b15f0a8..1376f748d63 100644 --- a/types/api/channels.d.ts +++ b/types/api/channels.d.ts @@ -134,3 +134,32 @@ type ChannelMemberCountByGroup = { }; type ChannelMemberCountsByGroup = Record; + +type ChannelBookmarkType = 'link' | 'file'; + +type ChannelBookmark = { + id: string; + create_at: number; + update_at: number; + delete_at: number; + channel_id: string; + owner_id: string; + file_id?: string; + display_name: string; + sort_order: number; + link_url?: string; + image_url?: string; + emoji?: string; + type: ChannelBookmarkType; + original_id?: string; + parent_id?: string; +} + +type ChannelBookmarkWithFileInfo = ChannelBookmark & { + file?: FileInfo; +} + +type UpdateChannelBookmarkResponse = { + updated: ChannelBookmarkWithFileInfo; + deleted?: ChannelBookmarkWithFileInfo; +} diff --git a/types/api/config.d.ts b/types/api/config.d.ts index 195415e52e0..3354f41e51a 100644 --- a/types/api/config.d.ts +++ b/types/api/config.d.ts @@ -120,6 +120,7 @@ interface ClientConfig { FeatureFlagAppsEnabled?: string; FeatureFlagCollapsedThreads?: string; FeatureFlagPostPriority?: string; + FeatureFlagChannelBookmarks?: string; ForgotPasswordLink?: string; GfycatApiKey: string; GfycatApiSecret: string; diff --git a/types/api/files.d.ts b/types/api/files.d.ts index 6bf3dac054c..3e5f4d48bc6 100644 --- a/types/api/files.d.ts +++ b/types/api/files.d.ts @@ -16,7 +16,7 @@ type FileInfo = { mime_type: string; mini_preview?: string; name: string; - post_id: string; + post_id?: string; size: number; update_at?: number; uri?: string; diff --git a/types/database/database.ts b/types/database/database.ts index cc26b7fe977..3771a87e739 100644 --- a/types/database/database.ts +++ b/types/database/database.ts @@ -226,6 +226,10 @@ export type HandleChannelArgs = PrepareOnly & { channels?: Channel[]; }; +export type HandleChannelBookmarkArgs = PrepareOnly & { + bookmarks?: ChannelBookmarkWithFileInfo[]; +}; + export type HandleCategoryArgs = PrepareOnly & { categories?: Category[]; }; diff --git a/types/database/models/servers/channel.ts b/types/database/models/servers/channel.ts index 039a018e676..291e5fc1a7a 100644 --- a/types/database/models/servers/channel.ts +++ b/types/database/models/servers/channel.ts @@ -2,6 +2,7 @@ // See LICENSE.txt for license information. import type CategoryChannelModel from './category_channel'; +import type ChannelBookmarkModel from './channel_bookmark'; import type ChannelInfoModel from './channel_info'; import type ChannelMembershipModel from './channel_membership'; import type DraftModel from './draft'; @@ -59,6 +60,9 @@ declare class ChannelModel extends Model { /** drafts : All drafts for this channel */ drafts: Query; + /** bookmarks : All bookmaks for this channel */ + bookmarks: Query; + /** posts : All posts made in the channel */ posts: Query; diff --git a/types/database/models/servers/channel_bookmark.ts b/types/database/models/servers/channel_bookmark.ts new file mode 100644 index 00000000000..413f45b8264 --- /dev/null +++ b/types/database/models/servers/channel_bookmark.ts @@ -0,0 +1,74 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import type ChannelModel from './channel'; +import type FileModel from './file'; +import type UserModel from './user'; +import type {Model, Relation} from '@nozbe/watermelondb'; +import type {Associations} from '@nozbe/watermelondb/Model'; + +/** + * ChannelBookmark the bookmarks for a specific channel. + */ +declare class ChannelBookmarkModel extends Model { + /** table (name) : ChannelBookmark */ + static table: string; + + /** associations : Describes every relationship to this table. */ + static associations: Associations; + + /** create_at : The creation date for this channel bookmark */ + createAt: number; + + /** update_at : The timestamp to when this channel bookmark was last updated on the server */ + updateAt: number; + + /** delete_at : The deletion/archived date of this channel bookmark */ + deleteAt: number; + + /** channel_id : The channel to which this channel bookmark belongs. */ + channelId: string; + + /** owner_id : The user who created this channel bookmark */ + ownerId: string; + + /** file_id : The file that was bookmarked if of type file */ + fileId?: string; + + /** display_name : The channel bookmark display name (e.g. Important Document ) */ + displayName: string; + + /** sort_order : The channel bookmark sort order */ + sortOrder: number; + + /** link_url : The channel bookmark url if of type link */ + linkUrl?: string; + + /** image_url : The channel bookmark image url if of type link (optional) */ + imageUrl?: string; + + /** emoji : The channel bookmark emoji (optional) */ + emoji?: string; + + /** type : The channel bookmark type it can be link or file */ + type: ChannelBookmarkType; + + /** original_id : The channel bookmark original identifier before it was edited */ + originalId?: string; + + /** parent_id : The channel bookmark parent in case is nested */ + parentId?: string; + + /** channel : The CHANNEL to which this CHANNEL BOOKMARK belongs */ + channel: Relation; + + /** owner : The USER that created this CHANNEL BOOKMARK */ + owner: Relation; + + /** file : The FILE attached to this CHANNEL BOOKMARK */ + file: Relation; + + toApi(): ChannelBookmark; +} + +export default ChannelBookmarkModel; diff --git a/types/database/raw_values.d.ts b/types/database/raw_values.d.ts index 5fd63f61a40..ba8531854f4 100644 --- a/types/database/raw_values.d.ts +++ b/types/database/raw_values.d.ts @@ -106,6 +106,7 @@ type RawValue = | Category | CategoryChannel | Channel + | ChannelBookmark | ChannelInfo | ChannelMember | ChannelMembership diff --git a/types/launch/index.ts b/types/launch/index.ts index 4dffb4d7194..b813e4369a8 100644 --- a/types/launch/index.ts +++ b/types/launch/index.ts @@ -8,6 +8,10 @@ export interface DeepLink { teamName: string; } +export interface DeepLinkServer { + serverUrl: string; +} + export interface DeepLinkChannel extends DeepLink { channelName: string; } @@ -34,7 +38,7 @@ export type DeepLinkType = typeof DeepLink[keyof typeof DeepLink]; export interface DeepLinkWithData { type: DeepLinkType; url: string; - data?: DeepLinkChannel | DeepLinkDM | DeepLinkGM | DeepLinkPermalink | DeepLinkPlugin; + data?: DeepLinkChannel | DeepLinkDM | DeepLinkGM | DeepLinkPermalink | DeepLinkPlugin | DeepLinkServer; } export type LaunchType = typeof Launch[keyof typeof Launch]; diff --git a/types/screens/emoji_selector.d.ts b/types/screens/emoji_selector.d.ts index 2914fc0aec2..7ff886cca01 100644 --- a/types/screens/emoji_selector.d.ts +++ b/types/screens/emoji_selector.d.ts @@ -14,6 +14,7 @@ type EmojiSection = { icon: string; id: string; key: string; + renderItem?: ({item}: ListRenderItemInfo) => JSX.Element; } type CategoryTranslation = { diff --git a/types/screens/gallery.ts b/types/screens/gallery.ts index 7aeb9b1c173..44cec0860d9 100644 --- a/types/screens/gallery.ts +++ b/types/screens/gallery.ts @@ -60,8 +60,10 @@ export type OnGestureEvent = ( event: T, ) => void; +export type GalleryFileType = 'image' | 'video' | 'file' | 'avatar'; + export type GalleryItemType = { - type: 'image' | 'video' | 'file' | 'avatar'; + type: GalleryFileType; id: string; width: number; height: number;