From d44f614d0c6e5341536bb5078d6498fda1211455 Mon Sep 17 00:00:00 2001 From: Yash Rajpal <58601732+yash-rajpal@users.noreply.github.com> Date: Fri, 18 Oct 2024 08:18:00 +0530 Subject: [PATCH 01/25] fix!: Login services button colors (#33333) --- .changeset/fifty-mails-admire.md | 8 ++ .../server/lib/settings.ts | 2 + .../server/lib/oauth/addOAuthService.ts | 2 + apps/meteor/server/settings/cas.ts | 4 +- apps/meteor/server/settings/oauth.ts | 4 + .../meteor/server/startup/migrations/index.ts | 1 + apps/meteor/server/startup/migrations/v317.ts | 119 ++++++++++++++++++ apps/meteor/tests/e2e/page-objects/auth.ts | 2 +- apps/meteor/tests/e2e/saml.spec.ts | 7 ++ .../meteor/tests/e2e/utils/convertHexToRGB.ts | 9 ++ packages/i18n/src/locales/en.i18n.json | 1 + .../src/LoginServicesButton.tsx | 4 + 12 files changed, 160 insertions(+), 3 deletions(-) create mode 100644 .changeset/fifty-mails-admire.md create mode 100644 apps/meteor/server/startup/migrations/v317.ts create mode 100644 apps/meteor/tests/e2e/utils/convertHexToRGB.ts diff --git a/.changeset/fifty-mails-admire.md b/.changeset/fifty-mails-admire.md new file mode 100644 index 000000000000..c5e7ab247259 --- /dev/null +++ b/.changeset/fifty-mails-admire.md @@ -0,0 +1,8 @@ +--- +'@rocket.chat/web-ui-registration': patch +"@rocket.chat/meteor": major +--- + +Login services button was not respecting the button color and text color settings. Implemented a fix to respect these settings and change the button colors accordingly. + +Added a warning on all settings which allow admins to change OAuth button colors, so that they can be alerted about WCAG (Web Content Accessibility Guidelines) compliance. diff --git a/apps/meteor/app/meteor-accounts-saml/server/lib/settings.ts b/apps/meteor/app/meteor-accounts-saml/server/lib/settings.ts index bb9567260337..5c16716720b0 100644 --- a/apps/meteor/app/meteor-accounts-saml/server/lib/settings.ts +++ b/apps/meteor/app/meteor-accounts-saml/server/lib/settings.ts @@ -230,10 +230,12 @@ export const addSettings = async function (name: string): Promise { await this.add(`SAML_Custom_${name}_button_label_color`, '#FFFFFF', { type: 'string', i18nLabel: 'Accounts_OAuth_Custom_Button_Label_Color', + alert: 'OAuth_button_colors_alert', }); await this.add(`SAML_Custom_${name}_button_color`, '#1d74f5', { type: 'string', i18nLabel: 'Accounts_OAuth_Custom_Button_Color', + alert: 'OAuth_button_colors_alert', }); }); diff --git a/apps/meteor/server/lib/oauth/addOAuthService.ts b/apps/meteor/server/lib/oauth/addOAuthService.ts index 2a49a23a1f4e..db84cb467ffc 100644 --- a/apps/meteor/server/lib/oauth/addOAuthService.ts +++ b/apps/meteor/server/lib/oauth/addOAuthService.ts @@ -118,6 +118,7 @@ export async function addOAuthService(name: string, values: { [k: string]: strin section: `Custom OAuth: ${name}`, i18nLabel: 'Accounts_OAuth_Custom_Button_Label_Color', persistent: true, + alert: 'OAuth_button_colors_alert', }); await settingsRegistry.add(`Accounts_OAuth_Custom-${name}-button_color`, values.buttonColor || '#1d74f5', { type: 'string', @@ -125,6 +126,7 @@ export async function addOAuthService(name: string, values: { [k: string]: strin section: `Custom OAuth: ${name}`, i18nLabel: 'Accounts_OAuth_Custom_Button_Color', persistent: true, + alert: 'OAuth_button_colors_alert', }); await settingsRegistry.add(`Accounts_OAuth_Custom-${name}-key_field`, values.keyField || 'username', { type: 'select', diff --git a/apps/meteor/server/settings/cas.ts b/apps/meteor/server/settings/cas.ts index 48a13d8cbd9c..f8ae4e6ca65a 100644 --- a/apps/meteor/server/settings/cas.ts +++ b/apps/meteor/server/settings/cas.ts @@ -33,8 +33,8 @@ export const createCasSettings = () => await this.add('CAS_popup_width', 810, { type: 'int', group: 'CAS', public: true }); await this.add('CAS_popup_height', 610, { type: 'int', group: 'CAS', public: true }); await this.add('CAS_button_label_text', 'CAS', { type: 'string', group: 'CAS' }); - await this.add('CAS_button_label_color', '#FFFFFF', { type: 'color', group: 'CAS' }); - await this.add('CAS_button_color', '#1d74f5', { type: 'color', group: 'CAS' }); + await this.add('CAS_button_label_color', '#FFFFFF', { type: 'color', group: 'CAS', alert: 'OAuth_button_colors_alert' }); + await this.add('CAS_button_color', '#1d74f5', { type: 'color', group: 'CAS', alert: 'OAuth_button_colors_alert' }); await this.add('CAS_autoclose', true, { type: 'boolean', group: 'CAS' }); }); }); diff --git a/apps/meteor/server/settings/oauth.ts b/apps/meteor/server/settings/oauth.ts index c67286771a0a..acc397faa37d 100644 --- a/apps/meteor/server/settings/oauth.ts +++ b/apps/meteor/server/settings/oauth.ts @@ -115,12 +115,14 @@ export const createOauthSettings = () => public: true, i18nLabel: 'Accounts_OAuth_Custom_Button_Label_Color', persistent: true, + alert: 'OAuth_button_colors_alert', }); await this.add('Accounts_OAuth_Nextcloud_button_color', '#0082c9', { type: 'string', public: true, i18nLabel: 'Accounts_OAuth_Custom_Button_Color', persistent: true, + alert: 'OAuth_button_colors_alert', }); }); @@ -273,11 +275,13 @@ export const createOauthSettings = () => type: 'string', i18nLabel: 'Accounts_OAuth_Custom_Button_Label_Color', persistent: true, + alert: 'OAuth_button_colors_alert', }); await this.add('Accounts_OAuth_Dolphin_button_color', '#1d74f5', { type: 'string', i18nLabel: 'Accounts_OAuth_Custom_Button_Color', persistent: true, + alert: 'OAuth_button_colors_alert', }); }); await this.section('Facebook', async function () { diff --git a/apps/meteor/server/startup/migrations/index.ts b/apps/meteor/server/startup/migrations/index.ts index 5b3759941a2d..384c77cbc14e 100644 --- a/apps/meteor/server/startup/migrations/index.ts +++ b/apps/meteor/server/startup/migrations/index.ts @@ -22,5 +22,6 @@ import './v313'; import './v314'; import './v315'; import './v316'; +import './v317'; export * from './xrun'; diff --git a/apps/meteor/server/startup/migrations/v317.ts b/apps/meteor/server/startup/migrations/v317.ts new file mode 100644 index 000000000000..0a7905bf32fd --- /dev/null +++ b/apps/meteor/server/startup/migrations/v317.ts @@ -0,0 +1,119 @@ +import type { ILoginServiceConfiguration, OAuthConfiguration } from '@rocket.chat/core-typings'; +import { Settings, LoginServiceConfiguration } from '@rocket.chat/models'; + +import { isTruthy } from '../../../lib/isTruthy'; +import { SystemLogger } from '../../lib/logger/system'; +import { addMigration } from '../../lib/migrations'; + +const newDefaultButtonColor = '#e4e7ea'; +const newDefaultButtonLabelColor = '#1f2329'; + +const settingsToUpdate = [ + // button background colors + { key: 'SAML_Custom_Default_button_color', defaultValue: '#1d74f5', newValue: newDefaultButtonColor }, + { key: 'CAS_button_color', defaultValue: '#1d74f5', newValue: newDefaultButtonColor }, + { key: 'Accounts_OAuth_Nextcloud_button_color', defaultValue: '#0082c9', newValue: newDefaultButtonColor }, + { key: 'Accounts_OAuth_Dolphin_button_color', defaultValue: '#1d74f5', newValue: newDefaultButtonColor }, + // button label colors + { key: 'SAML_Custom_Default_button_label_color', defaultValue: '#1d74f5', newValue: newDefaultButtonLabelColor }, + { key: 'CAS_button_label_color', defaultValue: '#1d74f5', newValue: newDefaultButtonLabelColor }, + { key: 'Accounts_OAuth_Nextcloud_button_label_color', defaultValue: '#1d74f5', newValue: newDefaultButtonLabelColor }, + { key: 'Accounts_OAuth_Dolphin_button_label_color', defaultValue: '#1d74f5', newValue: newDefaultButtonLabelColor }, +]; + +const getSettingValue = async (key: string) => Settings.getValueById(key); + +async function updateOAuthServices(): Promise { + const services = await Settings.find({ _id: { $regex: /^(Accounts_OAuth_|Accounts_OAuth_Custom-)[a-z0-9_]+$/i } }).toArray(); + const filteredServices = services.filter(({ value }) => typeof value === 'boolean'); + for await (const { _id: key, value } of filteredServices) { + if (value !== true) { + continue; + } + + let serviceName = key.replace('Accounts_OAuth_', ''); + if (serviceName === 'Meteor') { + serviceName = 'meteor-developer'; + } + if (/Accounts_OAuth_Custom-/.test(key)) { + serviceName = key.replace('Accounts_OAuth_Custom-', ''); + } + + const serviceKey = serviceName.toLowerCase(); + + const data: Partial> = {}; + + if (/Accounts_OAuth_Custom-/.test(key)) { + data.buttonLabelColor = (await getSettingValue(`${key}-button_label_color`)) as string; + data.buttonColor = (await getSettingValue(`${key}-button_color`)) as string; + } + + if (serviceName === 'Nextcloud') { + data.buttonLabelColor = (await getSettingValue('Accounts_OAuth_Nextcloud_button_label_color')) as string; + data.buttonColor = (await getSettingValue('Accounts_OAuth_Nextcloud_button_color')) as string; + } + + await LoginServiceConfiguration.createOrUpdateService(serviceKey, data); + } +} + +addMigration({ + version: 317, + name: 'Change default color of OAuth login services buttons', + async up() { + const promises = settingsToUpdate + .map(async ({ key, defaultValue, newValue }) => { + const oldSettingValue = await getSettingValue(key); + + if (!oldSettingValue || oldSettingValue !== defaultValue) { + return; + } + + SystemLogger.warn(`The default value of the setting ${key} has changed to ${newValue}. Please review your settings.`); + + return Settings.updateOne({ _id: key }, { $set: { value: newValue } }); + }) + .filter(isTruthy); + + await Promise.all(promises); + + const customOAuthButtonColors = await Settings.find({ + _id: { $regex: /^Accounts_OAuth_Custom-[a-zA-Z0-9_-]+-button_color$/ }, + }).toArray(); + const customOAuthButtonLabelColors = await Settings.find({ + _id: { $regex: /^Accounts_OAuth_Custom-[a-zA-Z0-9_-]+-button_label_color$/ }, + }).toArray(); + + const buttonColorPromises = customOAuthButtonColors + .map(({ _id, value, packageValue }) => { + if (packageValue !== value) { + return; + } + + SystemLogger.warn( + `The default value of the custom setting ${_id} has changed to ${newDefaultButtonColor}. Please review your settings.`, + ); + + return Settings.updateOne({ _id }, { $set: { value: newDefaultButtonColor } }); + }) + .filter(isTruthy); + + const buttonLabelColorPromises = customOAuthButtonLabelColors + .map(({ _id, value, packageValue }) => { + if (packageValue !== value) { + return; + } + + SystemLogger.warn( + `The default value of the custom setting ${_id} has changed to ${newDefaultButtonLabelColor}. Please review your settings.`, + ); + + return Settings.updateOne({ _id }, { $set: { value: newDefaultButtonLabelColor } }); + }) + .filter(isTruthy); + + await Promise.all([...buttonColorPromises, ...buttonLabelColorPromises]); + // update login service configurations + await updateOAuthServices(); + }, +}); diff --git a/apps/meteor/tests/e2e/page-objects/auth.ts b/apps/meteor/tests/e2e/page-objects/auth.ts index 8d5fe1edad20..46ec7e1f38dc 100644 --- a/apps/meteor/tests/e2e/page-objects/auth.ts +++ b/apps/meteor/tests/e2e/page-objects/auth.ts @@ -20,7 +20,7 @@ export class Registration { } get btnLoginWithSaml(): Locator { - return this.page.locator('role=button[name="SAML"]'); + return this.page.locator('role=button[name="SAML test login button"]'); } get btnLoginWithGoogle(): Locator { diff --git a/apps/meteor/tests/e2e/saml.spec.ts b/apps/meteor/tests/e2e/saml.spec.ts index 3d5935107bb1..fe1295ca0b4b 100644 --- a/apps/meteor/tests/e2e/saml.spec.ts +++ b/apps/meteor/tests/e2e/saml.spec.ts @@ -10,6 +10,7 @@ import * as constants from './config/constants'; import { createUserFixture } from './fixtures/collections/users'; import { Users } from './fixtures/userStates'; import { Registration } from './page-objects'; +import { convertHexToRGB } from './utils/convertHexToRGB'; import { createCustomRole, deleteCustomRole } from './utils/custom-role'; import { getUserInfo } from './utils/getUserInfo'; import { parseMeteorResponse } from './utils/parseMeteorResponse'; @@ -59,6 +60,8 @@ const resetTestData = async ({ api, cleanupOnly = false }: { api?: any; cleanupO { _id: 'SAML_Custom_Default_issuer', value: 'http://localhost:3000/_saml/metadata/test-sp' }, { _id: 'SAML_Custom_Default_entry_point', value: 'http://localhost:8080/simplesaml/saml2/idp/SSOService.php' }, { _id: 'SAML_Custom_Default_idp_slo_redirect_url', value: 'http://localhost:8080/simplesaml/saml2/idp/SingleLogoutService.php' }, + { _id: 'SAML_Custom_Default_button_label_text', value: 'SAML test login button' }, + { _id: 'SAML_Custom_Default_button_color', value: '#185925' }, ]; await Promise.all(settings.map(({ _id, value }) => setSettingValueById(api, _id, value))); @@ -152,6 +155,10 @@ test.describe('SAML', () => { await expect(poRegistration.btnLoginWithSaml).toBeVisible({ timeout: 10000 }); }); + await test.step('expect to have SAML login button to have the required background color', async () => { + await expect(poRegistration.btnLoginWithSaml).toHaveCSS('background-color', convertHexToRGB('#185925')); + }); + await test.step('expect to be redirected to the IdP for login', async () => { await poRegistration.btnLoginWithSaml.click(); diff --git a/apps/meteor/tests/e2e/utils/convertHexToRGB.ts b/apps/meteor/tests/e2e/utils/convertHexToRGB.ts new file mode 100644 index 000000000000..9b20671cfcaa --- /dev/null +++ b/apps/meteor/tests/e2e/utils/convertHexToRGB.ts @@ -0,0 +1,9 @@ +export const convertHexToRGB = (hex: string) => { + hex = hex.replace(/^#/, ''); + + const red = parseInt(hex.substring(0, 2), 16); + const green = parseInt(hex.substring(2, 4), 16); + const blue = parseInt(hex.substring(4, 6), 16); + + return `rgb(${red}, ${green}, ${blue})`; +}; diff --git a/packages/i18n/src/locales/en.i18n.json b/packages/i18n/src/locales/en.i18n.json index 966ced7d5508..d8e29b5bff96 100644 --- a/packages/i18n/src/locales/en.i18n.json +++ b/packages/i18n/src/locales/en.i18n.json @@ -4030,6 +4030,7 @@ "OAuth": "OAuth", "OAuth_Description": "Configure authentication methods beyond just username and password.", "OAuth_Application": "OAuth Application", + "OAuth_button_colors_alert": "Changing the color may result in non-compliance with WCAG (Web Content Accessibility Guidelines) requirements. Please ensure that the new colors meet the recommended contrast and readability standards to maintain accessibility for all users.", "Objects": "Objects", "Off": "Off", "Off_the_record_conversation": "Off-the-Record Conversation", diff --git a/packages/web-ui-registration/src/LoginServicesButton.tsx b/packages/web-ui-registration/src/LoginServicesButton.tsx index d9f43b0e484c..ba16d360d9c1 100644 --- a/packages/web-ui-registration/src/LoginServicesButton.tsx +++ b/packages/web-ui-registration/src/LoginServicesButton.tsx @@ -15,6 +15,8 @@ const LoginServicesButton = ({ className, disabled, setError, + buttonColor, + buttonLabelColor, ...props }: T & { className?: string; @@ -43,6 +45,8 @@ const LoginServicesButton = ({ alignItems='center' display='flex' justifyContent='center' + color={buttonLabelColor} + backgroundColor={buttonColor} > {buttonLabelText || t('Sign_in_with__provider__', { provider: title })} From d879c8b5485821bbdeee6b694f8f8da37e073d07 Mon Sep 17 00:00:00 2001 From: Hugo Costa Date: Thu, 17 Oct 2024 23:51:11 -0300 Subject: [PATCH 02/25] fix: E2EE not rendering new navigation feature preview (#33631) --- .changeset/real-plants-mix.md | 5 +++++ .../client/views/room/E2EESetup/RoomE2EESetup.tsx | 13 ++++++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 .changeset/real-plants-mix.md diff --git a/.changeset/real-plants-mix.md b/.changeset/real-plants-mix.md new file mode 100644 index 000000000000..a0b6977d536c --- /dev/null +++ b/.changeset/real-plants-mix.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +Fixes E2EE not rendering new navigation feature preview diff --git a/apps/meteor/client/views/room/E2EESetup/RoomE2EESetup.tsx b/apps/meteor/client/views/room/E2EESetup/RoomE2EESetup.tsx index d89321346256..c3319ac32c4b 100644 --- a/apps/meteor/client/views/room/E2EESetup/RoomE2EESetup.tsx +++ b/apps/meteor/client/views/room/E2EESetup/RoomE2EESetup.tsx @@ -1,3 +1,4 @@ +import { FeaturePreview, FeaturePreviewOff, FeaturePreviewOn } from '@rocket.chat/ui-client'; import React, { useCallback } from 'react'; import { useTranslation } from 'react-i18next'; @@ -5,6 +6,7 @@ import { e2e } from '../../../../app/e2e/client'; import { E2EEState } from '../../../../app/e2e/client/E2EEState'; import { E2ERoomState } from '../../../../app/e2e/client/E2ERoomState'; import RoomBody from '../body/RoomBody'; +import RoomBodyV2 from '../body/RoomBodyV2'; import { useRoom } from '../contexts/RoomContext'; import { useE2EERoomState } from '../hooks/useE2EERoomState'; import { useE2EEState } from '../hooks/useE2EEState'; @@ -63,7 +65,16 @@ const RoomE2EESetup = () => { ); } - return ; + return ( + + + + + + + + + ); }; export default RoomE2EESetup; From f835e359053dfc0c31df7adce44ce9defa08eb64 Mon Sep 17 00:00:00 2001 From: Ricardo Garim Date: Fri, 18 Oct 2024 09:35:57 -0300 Subject: [PATCH 03/25] chore!: remove query field on online channels listing (#33646) --- .../app/api/server/helpers/parseJsonQuery.ts | 1 + apps/meteor/app/api/server/v1/channels.ts | 15 +++++++++++---- apps/meteor/tests/end-to-end/api/channels.ts | 4 ++-- .../src/v1/channels/ChannelsOnlineProps.ts | 6 +++++- packages/rest-typings/src/v1/channels/channels.ts | 1 + 5 files changed, 20 insertions(+), 7 deletions(-) diff --git a/apps/meteor/app/api/server/helpers/parseJsonQuery.ts b/apps/meteor/app/api/server/helpers/parseJsonQuery.ts index 631506e625a0..bbd58e118556 100644 --- a/apps/meteor/app/api/server/helpers/parseJsonQuery.ts +++ b/apps/meteor/app/api/server/helpers/parseJsonQuery.ts @@ -61,6 +61,7 @@ export async function parseJsonQuery(api: PartialThis): Promise<{ '/api/v1/custom-user-status.list', '/api/v1/custom-sounds.list', '/api/v1/channels.list', + '/api/v1/channels.online', ].includes(route); const isUnsafeQueryParamsAllowed = process.env.ALLOW_UNSAFE_QUERY_AND_FIELDS_API_PARAMS?.toUpperCase() === 'TRUE'; diff --git a/apps/meteor/app/api/server/v1/channels.ts b/apps/meteor/app/api/server/v1/channels.ts index eacaeb27c202..09c4bee3da69 100644 --- a/apps/meteor/app/api/server/v1/channels.ts +++ b/apps/meteor/app/api/server/v1/channels.ts @@ -20,6 +20,7 @@ import { isChannelsDeleteProps, isChannelsListProps, isChannelsFilesListProps, + isChannelsOnlineProps, } from '@rocket.chat/rest-typings'; import { Meteor } from 'meteor/meteor'; @@ -1083,17 +1084,23 @@ API.v1.addRoute( API.v1.addRoute( 'channels.online', - { authRequired: true }, + { authRequired: true, validateParams: isChannelsOnlineProps }, { async get() { const { query } = await this.parseJsonQuery(); - if (!query || Object.keys(query).length === 0) { + const { _id } = this.queryParams; + + if ((!query || Object.keys(query).length === 0) && !_id) { return API.v1.failure('Invalid query'); } - const ourQuery = Object.assign({}, query, { t: 'c' }); + const filter = { + ...query, + ...(_id ? { _id } : {}), + t: 'c', + }; - const room = await Rooms.findOne(ourQuery as Record); + const room = await Rooms.findOne(filter as Record); if (!room) { return API.v1.failure('Channel does not exists'); } diff --git a/apps/meteor/tests/end-to-end/api/channels.ts b/apps/meteor/tests/end-to-end/api/channels.ts index 6fa055697e1f..61a1c294afde 100644 --- a/apps/meteor/tests/end-to-end/api/channels.ts +++ b/apps/meteor/tests/end-to-end/api/channels.ts @@ -1001,7 +1001,7 @@ describe('[Channels]', () => { return request .get(api('channels.online')) .set(testUserCredentials) - .query(`query={"_id": "${room._id}"}`) + .query({ _id: room._id }) .expect('Content-Type', 'application/json') .expect(200) .expect((res) => { @@ -1025,7 +1025,7 @@ describe('[Channels]', () => { return request .get(api('channels.online')) .set(outsiderCredentials) - .query(`query={"_id": "${room._id}"}`) + .query({ _id: room._id }) .expect('Content-Type', 'application/json') .expect(200) .expect((res) => { diff --git a/packages/rest-typings/src/v1/channels/ChannelsOnlineProps.ts b/packages/rest-typings/src/v1/channels/ChannelsOnlineProps.ts index 80bba717904f..0a2b6a622e18 100644 --- a/packages/rest-typings/src/v1/channels/ChannelsOnlineProps.ts +++ b/packages/rest-typings/src/v1/channels/ChannelsOnlineProps.ts @@ -4,10 +4,14 @@ const ajv = new Ajv({ coerceTypes: true, }); -export type ChannelsOnlineProps = { query?: Record }; +export type ChannelsOnlineProps = { _id?: string; query?: Record }; const channelsOnlyPropsSchema = { type: 'object', properties: { + _id: { + type: 'string', + nullable: true, + }, query: { type: 'string', nullable: true, diff --git a/packages/rest-typings/src/v1/channels/channels.ts b/packages/rest-typings/src/v1/channels/channels.ts index 5ee5480ee6a8..c3138db17436 100644 --- a/packages/rest-typings/src/v1/channels/channels.ts +++ b/packages/rest-typings/src/v1/channels/channels.ts @@ -35,6 +35,7 @@ import type { ChannelsUnarchiveProps } from './ChannelsUnarchiveProps'; export * from './ChannelsFilesListProps'; export * from './ChannelsListProps'; +export * from './ChannelsOnlineProps'; export type ChannelsEndpoints = { '/v1/channels.files': { From 509143d6dd3d645e1da7e492203a917cb4bc7c52 Mon Sep 17 00:00:00 2001 From: Pierre Lehnen <55164754+pierre-lehnen-rc@users.noreply.github.com> Date: Fri, 18 Oct 2024 10:26:21 -0300 Subject: [PATCH 04/25] feat!: shrink payload of the startImport endpoint (#33630) --- .changeset/two-emus-wash.md | 7 ++ .../server/PendingAvatarImporter.ts | 21 +++--- .../server/PendingFileImporter.ts | 22 +++--- .../app/importer/server/classes/Importer.ts | 61 +++++---------- .../importer/server/methods/startImport.ts | 18 ++--- .../views/admin/import/PrepareImportPage.tsx | 13 +++- apps/meteor/lib/callbacks.ts | 1 - apps/meteor/server/services/import/service.ts | 4 +- .../tests/e2e/fixtures/files/csv_import.zip | Bin 991 -> 958 bytes .../e2e/fixtures/files/csv_import_users.csv | 1 + apps/meteor/tests/e2e/imports.spec.ts | 10 ++- apps/meteor/tests/e2e/page-objects/admin.ts | 8 ++ .../src/import/IImporterShortSelection.ts | 9 +++ packages/core-typings/src/import/index.ts | 1 + .../src/v1/import/StartImportParamsPOST.ts | 70 +++++------------- 15 files changed, 114 insertions(+), 132 deletions(-) create mode 100644 .changeset/two-emus-wash.md create mode 100644 packages/core-typings/src/import/IImporterShortSelection.ts diff --git a/.changeset/two-emus-wash.md b/.changeset/two-emus-wash.md new file mode 100644 index 000000000000..0a5307c58f25 --- /dev/null +++ b/.changeset/two-emus-wash.md @@ -0,0 +1,7 @@ +--- +'@rocket.chat/core-typings': major +'@rocket.chat/rest-typings': major +'@rocket.chat/meteor': major +--- + +Changes the payload of the startImport endpoint to decrease the amount of data it requires diff --git a/apps/meteor/app/importer-pending-avatars/server/PendingAvatarImporter.ts b/apps/meteor/app/importer-pending-avatars/server/PendingAvatarImporter.ts index 0f6c8c7d41df..de37ba200289 100644 --- a/apps/meteor/app/importer-pending-avatars/server/PendingAvatarImporter.ts +++ b/apps/meteor/app/importer-pending-avatars/server/PendingAvatarImporter.ts @@ -1,6 +1,7 @@ +import type { IImporterShortSelection } from '@rocket.chat/core-typings'; import { Users } from '@rocket.chat/models'; -import { Importer, ProgressStep, Selection } from '../../importer/server'; +import { Importer, ProgressStep } from '../../importer/server'; import type { ImporterProgress } from '../../importer/server/classes/ImporterProgress'; import { setAvatarFromServiceWithValidation } from '../../lib/server/functions/setUserAvatar'; @@ -17,21 +18,23 @@ export class PendingAvatarImporter extends Importer { return 0; } - await this.updateRecord({ 'count.messages': fileCount, 'messagesstatus': null }); - await this.addCountToTotal(fileCount); - - const fileData = new Selection(this.info.name, [], [], fileCount); - await this.updateRecord({ fileData }); + this.progress.count.total += fileCount; + await this.updateRecord({ + 'count.messages': fileCount, + 'count.total': fileCount, + 'messagesstatus': null, + 'status': ProgressStep.IMPORTING_FILES, + }); + this.reportProgress(); - await super.updateProgress(ProgressStep.IMPORTING_FILES); setImmediate(() => { - void this.startImport(fileData); + void this.startImport({}); }); return fileCount; } - async startImport(importSelection: Selection): Promise { + async startImport(importSelection: IImporterShortSelection): Promise { const pendingFileUserList = Users.findAllUsersWithPendingAvatar(); try { for await (const user of pendingFileUserList) { diff --git a/apps/meteor/app/importer-pending-files/server/PendingFileImporter.ts b/apps/meteor/app/importer-pending-files/server/PendingFileImporter.ts index 400a9856c4e7..a6d147cf8df3 100644 --- a/apps/meteor/app/importer-pending-files/server/PendingFileImporter.ts +++ b/apps/meteor/app/importer-pending-files/server/PendingFileImporter.ts @@ -2,12 +2,12 @@ import http from 'http'; import https from 'https'; import { api } from '@rocket.chat/core-services'; -import type { IImport, MessageAttachment, IUpload } from '@rocket.chat/core-typings'; +import type { IImport, MessageAttachment, IUpload, IImporterShortSelection } from '@rocket.chat/core-typings'; import { Messages } from '@rocket.chat/models'; import { Random } from '@rocket.chat/random'; import { FileUpload } from '../../file-upload/server'; -import { Importer, ProgressStep, Selection } from '../../importer/server'; +import { Importer, ProgressStep } from '../../importer/server'; import type { ConverterOptions } from '../../importer/server/classes/ImportDataConverter'; import type { ImporterProgress } from '../../importer/server/classes/ImporterProgress'; import type { ImporterInfo } from '../../importer/server/definitions/ImporterInfo'; @@ -27,21 +27,23 @@ export class PendingFileImporter extends Importer { return 0; } - await this.updateRecord({ 'count.messages': fileCount, 'messagesstatus': null }); - await this.addCountToTotal(fileCount); - - const fileData = new Selection(this.info.name, [], [], fileCount); - await this.updateRecord({ fileData }); + this.progress.count.total += fileCount; + await this.updateRecord({ + 'count.messages': fileCount, + 'count.total': fileCount, + 'messagesstatus': null, + 'status': ProgressStep.IMPORTING_FILES, + }); + this.reportProgress(); - await super.updateProgress(ProgressStep.IMPORTING_FILES); setImmediate(() => { - void this.startImport(fileData); + void this.startImport({}); }); return fileCount; } - async startImport(importSelection: Selection): Promise { + async startImport(importSelection: IImporterShortSelection): Promise { const downloadedFileIds: string[] = []; const maxFileCount = 10; const maxFileSize = 1024 * 1024 * 500; diff --git a/apps/meteor/app/importer/server/classes/Importer.ts b/apps/meteor/app/importer/server/classes/Importer.ts index d89cb5f979f3..5f40ead0e1ea 100644 --- a/apps/meteor/app/importer/server/classes/Importer.ts +++ b/apps/meteor/app/importer/server/classes/Importer.ts @@ -1,12 +1,18 @@ import { api } from '@rocket.chat/core-services'; -import type { IImport, IImportRecord, IImportChannel, IImportUser, IImportProgress } from '@rocket.chat/core-typings'; +import type { + IImport, + IImportRecord, + IImportChannel, + IImportUser, + IImportProgress, + IImporterShortSelection, +} from '@rocket.chat/core-typings'; import { Logger } from '@rocket.chat/logger'; import { Settings, ImportData, Imports } from '@rocket.chat/models'; import AdmZip from 'adm-zip'; import type { MatchKeysAndValues, MongoServerError } from 'mongodb'; import { Selection, SelectionChannel, SelectionUser } from '..'; -import { callbacks } from '../../../../lib/callbacks'; import { notifyOnSettingChangedById } from '../../../lib/server/lib/notifyListener'; import { t } from '../../../utils/lib/i18n'; import { ProgressStep, ImportPreparingStartedStates } from '../../lib/ImporterProgressStep'; @@ -91,27 +97,10 @@ export class Importer { * doesn't end up with a "locked" UI while Meteor waits for a response. * The returned object should be the progress. * - * @param {Selection} importSelection The selection data. + * @param {IImporterShortSelection} importSelection The selection data. * @returns {ImporterProgress} The progress record of the import. */ - async startImport(importSelection: Selection, startedByUserId: string): Promise { - if (!(importSelection instanceof Selection)) { - throw new Error(`Invalid Selection data provided to the ${this.info.name} importer.`); - } else if (importSelection.users === undefined) { - throw new Error(`Users in the selected data wasn't found, it must but at least an empty array for the ${this.info.name} importer.`); - } else if (importSelection.channels === undefined) { - throw new Error( - `Channels in the selected data wasn't found, it must but at least an empty array for the ${this.info.name} importer.`, - ); - } - if (!startedByUserId) { - throw new Error('You must be logged in to do this.'); - } - - if (!startedByUserId) { - throw new Error('You must be logged in to do this.'); - } - + async startImport(importSelection: IImporterShortSelection, startedByUserId: string): Promise { await this.updateProgress(ProgressStep.IMPORTING_STARTED); this.reloadCount(); const started = Date.now(); @@ -124,37 +113,29 @@ export class Importer { switch (type) { case 'channel': { - if (!importSelection.channels) { + if (importSelection.channels?.all) { return true; } + if (!importSelection.channels?.list?.length) { + return false; + } const channelData = data as IImportChannel; const id = channelData.t === 'd' ? '__directMessages__' : channelData.importIds[0]; - for (const channel of importSelection.channels) { - if (channel.channel_id === id) { - return channel.do_import; - } - } - - return false; + return importSelection.channels.list?.includes(id); } case 'user': { - // #TODO: Replace this workaround - if (importSelection.users.length === 0 && this.info.key === 'api') { + if (importSelection.users?.all) { return true; } + if (!importSelection.users?.list?.length) { + return false; + } const userData = data as IImportUser; - const id = userData.importIds[0]; - for (const user of importSelection.users) { - if (user.user_id === id) { - return user.do_import; - } - } - - return false; + return importSelection.users.list.includes(id); } } @@ -198,8 +179,6 @@ export class Importer { await this.applySettingValues({}); await this.updateProgress(ProgressStep.IMPORTING_USERS); - const usersToImport = importSelection.users.filter((user) => user.do_import); - await callbacks.run('beforeUserImport', { userCount: usersToImport.length }); await this.converter.convertUsers({ beforeImportFn, afterImportFn, onErrorFn, afterBatchFn }); await this.updateProgress(ProgressStep.IMPORTING_CHANNELS); diff --git a/apps/meteor/app/importer/server/methods/startImport.ts b/apps/meteor/app/importer/server/methods/startImport.ts index bbb5ce76ad1c..d86638662f6f 100644 --- a/apps/meteor/app/importer/server/methods/startImport.ts +++ b/apps/meteor/app/importer/server/methods/startImport.ts @@ -1,10 +1,10 @@ import type { IUser } from '@rocket.chat/core-typings'; import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Imports } from '@rocket.chat/models'; -import type { StartImportParamsPOST } from '@rocket.chat/rest-typings'; +import { isStartImportParamsPOST, type StartImportParamsPOST } from '@rocket.chat/rest-typings'; import { Meteor } from 'meteor/meteor'; -import { Importers, Selection, SelectionChannel, SelectionUser } from '..'; +import { Importers } from '..'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; export const executeStartImport = async ({ input }: StartImportParamsPOST, startedByUserId: IUser['_id']) => { @@ -21,15 +21,7 @@ export const executeStartImport = async ({ input }: StartImportParamsPOST, start const instance = new importer.importer(importer, operation); // eslint-disable-line new-cap - const usersSelection = input.users.map( - (user) => new SelectionUser(user.user_id, user.username, user.email, user.is_deleted, user.is_bot, user.do_import), - ); - const channelsSelection = input.channels.map( - (channel) => - new SelectionChannel(channel.channel_id, channel.name, channel.is_archived, channel.do_import, channel.is_private, channel.is_direct), - ); - const selection = new Selection(importer.name, usersSelection, channelsSelection, 0); - await instance.startImport(selection, startedByUserId); + await instance.startImport(input, startedByUserId); }; declare module '@rocket.chat/ddp-client' { @@ -41,6 +33,10 @@ declare module '@rocket.chat/ddp-client' { Meteor.methods({ async startImport({ input }: StartImportParamsPOST) { + if (!input || typeof input !== 'object' || !isStartImportParamsPOST({ input })) { + throw new Meteor.Error(`Invalid Selection data provided to the importer.`); + } + const userId = Meteor.userId(); // Takes name and object with users / channels selected to import if (!userId) { diff --git a/apps/meteor/client/views/admin/import/PrepareImportPage.tsx b/apps/meteor/client/views/admin/import/PrepareImportPage.tsx index 39002b3c3085..5af842c4bebe 100644 --- a/apps/meteor/client/views/admin/import/PrepareImportPage.tsx +++ b/apps/meteor/client/views/admin/import/PrepareImportPage.tsx @@ -151,10 +151,19 @@ function PrepareImportPage() { setImporting(true); try { + const usersToImport = users.filter(({ do_import }) => do_import).map(({ user_id }) => user_id); + const channelsToImport = channels.filter(({ do_import }) => do_import).map(({ channel_id }) => channel_id); + await startImport({ input: { - users: users.map((user) => ({ is_bot: false, is_email_taken: false, ...user })), - channels: channels.map((channel) => ({ is_private: false, is_direct: false, ...channel })), + users: { + all: users.length > 0 && usersToImport.length === users.length, + list: (usersToImport.length !== users.length && usersToImport) || undefined, + }, + channels: { + all: channels.length > 0 && channelsToImport.length === channels.length, + list: (channelsToImport.length !== channels.length && channelsToImport) || undefined, + }, }, }); router.navigate('/admin/import/progress'); diff --git a/apps/meteor/lib/callbacks.ts b/apps/meteor/lib/callbacks.ts index dcfd7a021c5e..f8f5a324a638 100644 --- a/apps/meteor/lib/callbacks.ts +++ b/apps/meteor/lib/callbacks.ts @@ -99,7 +99,6 @@ interface EventLikeCallbackSignatures { 'beforeSaveUser': ({ user, oldUser }: { user: IUser; oldUser?: IUser }) => void; 'afterSaveUser': ({ user, oldUser }: { user: IUser; oldUser?: IUser | null }) => void; 'livechat.afterTagRemoved': (tag: ILivechatTagRecord) => void; - 'beforeUserImport': (data: { userCount: number }) => void; 'afterUserImport': (data: { inserted: IUser['_id'][]; updated: IUser['_id']; skipped: number; failed: number }) => void; } diff --git a/apps/meteor/server/services/import/service.ts b/apps/meteor/server/services/import/service.ts index cb95b2d1aa8f..3b6986f7ef50 100644 --- a/apps/meteor/server/services/import/service.ts +++ b/apps/meteor/server/services/import/service.ts @@ -5,7 +5,6 @@ import { Imports, ImportData } from '@rocket.chat/models'; import { ObjectId } from 'mongodb'; import { Importers } from '../../../app/importer/server'; -import { ImporterSelection } from '../../../app/importer/server/classes/ImporterSelection'; import { settings } from '../../../app/settings/server'; import { validateRoleList } from '../../lib/roles/validateRoleList'; import { getNewUserRoles } from '../user/lib/getNewUserRoles'; @@ -175,7 +174,6 @@ export class ImportService extends ServiceClassInternal implements IImportServic skipExistingUsers: true, }); - const selection = new ImporterSelection(importer.name, [], [], 0); - await instance.startImport(selection, userId); + await instance.startImport({ users: { all: true } }, userId); } } diff --git a/apps/meteor/tests/e2e/fixtures/files/csv_import.zip b/apps/meteor/tests/e2e/fixtures/files/csv_import.zip index 19415a7cd142c71c4b8dcbda71b72d8c3ffd7ecd..63c502abd85816fa34cf4c8edaa9db34e20b0c39 100644 GIT binary patch delta 198 zcmcc5zK?ywF-Co61_lm>sl|bjQ$wEJbzxv&mdeWNSG-U8 zUkYq6o-D>BFK)$Epl0?p?&nI6R|i#m*k>GB#O~Bo!niqrNsTero>>HknJPe2K|rBl zN#j9ABpbLG6u=UU3=#|sjYpVvDcojkNS^U;Cy<8m1H4(;K#Ev^um-4EhY7?305r`v AjQ{`u delta 197 zcmdnTexH5AF-Bbm4j|kX7aG9;1qnc!lR<%@v^ce>STDJ_EHs3dfxUw7T51K~gNzKgtQ8kwWdo^V0m2HPvOp#f4*;*RFY^EZ diff --git a/apps/meteor/tests/e2e/fixtures/files/csv_import_users.csv b/apps/meteor/tests/e2e/fixtures/files/csv_import_users.csv index c796f6e3fada..433133e6361a 100644 --- a/apps/meteor/tests/e2e/fixtures/files/csv_import_users.csv +++ b/apps/meteor/tests/e2e/fixtures/files/csv_import_users.csv @@ -1,2 +1,3 @@ billy.bob, billy.bob@example.com, Billy Bob Jr. billy.joe, billy.joe@example.com, Billy Joe Jr. +billy.billy, billy.billy@example.com, Billy Billy Jr. \ No newline at end of file diff --git a/apps/meteor/tests/e2e/imports.spec.ts b/apps/meteor/tests/e2e/imports.spec.ts index 6da86cee86f9..ed76d866c728 100644 --- a/apps/meteor/tests/e2e/imports.spec.ts +++ b/apps/meteor/tests/e2e/imports.spec.ts @@ -115,6 +115,8 @@ test.describe.serial('imports', () => { await poAdmin.inputFile.setInputFiles(zipCsvImportDir); await poAdmin.btnImport.click(); + await poAdmin.findFileCheckboxByUsername('billy.billy').click(); + await poAdmin.btnStartImport.click(); await expect(poAdmin.importStatusTableFirstRowCell).toBeVisible({ @@ -125,8 +127,12 @@ test.describe.serial('imports', () => { test('expect all imported users to be actually listed as users', async ({ page }) => { await page.goto('/admin/users'); - for (const user of rowUserName) { - expect(page.locator(`tbody tr td:first-child >> text="${user}"`)); + for await (const user of rowUserName) { + if (user === 'billy.billy') { + await expect(page.locator(`tbody tr td:first-child >> text="${user}"`)).not.toBeVisible(); + } else { + expect(page.locator(`tbody tr td:first-child >> text="${user}"`)); + } } }); diff --git a/apps/meteor/tests/e2e/page-objects/admin.ts b/apps/meteor/tests/e2e/page-objects/admin.ts index 2086704ee4ed..49f86a59b6f9 100644 --- a/apps/meteor/tests/e2e/page-objects/admin.ts +++ b/apps/meteor/tests/e2e/page-objects/admin.ts @@ -298,4 +298,12 @@ export class Admin { async adminSectionButton(href: AdminSectionsHref): Promise { return this.page.locator(`a[href="${href}"]`); } + + findFileRowByUsername(username: string) { + return this.page.locator('tr', { has: this.page.getByRole('cell', { name: username }) }); + } + + findFileCheckboxByUsername(username: string) { + return this.findFileRowByUsername(username).locator('label', { has: this.page.getByRole('checkbox') }); + } } diff --git a/packages/core-typings/src/import/IImporterShortSelection.ts b/packages/core-typings/src/import/IImporterShortSelection.ts new file mode 100644 index 000000000000..008f54252f91 --- /dev/null +++ b/packages/core-typings/src/import/IImporterShortSelection.ts @@ -0,0 +1,9 @@ +export interface IImporterShortSelectionItem { + all?: boolean; + list?: string[]; +} + +export interface IImporterShortSelection { + users?: IImporterShortSelectionItem; + channels?: IImporterShortSelectionItem; +} diff --git a/packages/core-typings/src/import/index.ts b/packages/core-typings/src/import/index.ts index 00df59ff93b6..a2d5bf188b1f 100644 --- a/packages/core-typings/src/import/index.ts +++ b/packages/core-typings/src/import/index.ts @@ -9,4 +9,5 @@ export * from './IImportProgress'; export * from './IImporterSelection'; export * from './IImporterSelectionUser'; export * from './IImporterSelectionChannel'; +export * from './IImporterShortSelection'; export * from './ImportState'; diff --git a/packages/rest-typings/src/v1/import/StartImportParamsPOST.ts b/packages/rest-typings/src/v1/import/StartImportParamsPOST.ts index 310ea8c1d61a..fd2a456a615d 100644 --- a/packages/rest-typings/src/v1/import/StartImportParamsPOST.ts +++ b/packages/rest-typings/src/v1/import/StartImportParamsPOST.ts @@ -1,3 +1,4 @@ +import type { IImporterShortSelection } from '@rocket.chat/core-typings'; import Ajv from 'ajv'; const ajv = new Ajv({ @@ -5,26 +6,19 @@ const ajv = new Ajv({ }); export type StartImportParamsPOST = { - input: { - users: { - user_id: string; - username: string; - email: string; - is_deleted: boolean; - is_bot: boolean; - do_import: boolean; - is_email_taken: boolean; - }[]; - channels: { - channel_id: string; - name: string; - creator?: string; - is_archived: boolean; - do_import: boolean; - is_private: boolean; - is_direct: boolean; - }[]; - }; + input: IImporterShortSelection; +}; + +const RecordListSchema = { + type: 'object', + properties: { + all: { type: 'boolean' }, + list: { + type: 'array', + items: { type: 'string' }, + }, + }, + required: [], }; const StartImportParamsPostSchema = { @@ -33,40 +27,10 @@ const StartImportParamsPostSchema = { input: { type: 'object', properties: { - users: { - type: 'array', - items: { - type: 'object', - properties: { - user_id: { type: 'string' }, - username: { type: 'string' }, - email: { type: 'string', nullable: true }, - is_deleted: { type: 'boolean' }, - is_bot: { type: 'boolean' }, - do_import: { type: 'boolean' }, - is_email_taken: { type: 'boolean' }, - }, - required: ['user_id', 'username', 'is_deleted', 'is_bot', 'do_import', 'is_email_taken'], - }, - }, - channels: { - type: 'array', - items: { - type: 'object', - properties: { - channel_id: { type: 'string' }, - name: { type: 'string' }, - creator: { type: 'string' }, - is_archived: { type: 'boolean' }, - do_import: { type: 'boolean' }, - is_private: { type: 'boolean' }, - is_direct: { type: 'boolean' }, - }, - required: ['channel_id', 'name', 'is_archived', 'do_import', 'is_private', 'is_direct'], - }, - }, + users: RecordListSchema, + channels: RecordListSchema, }, - required: ['users', 'channels'], + required: [], }, }, additionalProperties: false, From 0e00975ce50f74e6b798fe9f44592734addc8550 Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo Date: Fri, 18 Oct 2024 10:44:19 -0300 Subject: [PATCH 05/25] chore: patches mongodb to make ts task less expensive (#33639) --- .../mongodb-npm-4.17.2-40d1286d70.patch | 13 +++++ apps/meteor/ee/server/services/package.json | 2 +- apps/meteor/package.json | 2 +- ee/apps/account-service/package.json | 2 +- ee/apps/authorization-service/package.json | 2 +- ee/apps/ddp-streamer/package.json | 2 +- ee/apps/omnichannel-transcript/package.json | 2 +- ee/apps/presence-service/package.json | 2 +- ee/apps/queue-worker/package.json | 2 +- ee/apps/stream-hub-service/package.json | 2 +- ee/packages/omnichannel-services/package.json | 2 +- ee/packages/presence/package.json | 2 +- package.json | 3 +- packages/agenda/package.json | 2 +- packages/core-services/package.json | 2 +- packages/core-typings/package.json | 2 +- packages/cron/package.json | 2 +- packages/instance-status/package.json | 2 +- packages/model-typings/package.json | 2 +- packages/rest-typings/package.json | 2 +- packages/ui-contexts/package.json | 2 +- yarn.lock | 58 ++++++++++++------- 22 files changed, 72 insertions(+), 40 deletions(-) create mode 100644 .yarn/patches/mongodb-npm-4.17.2-40d1286d70.patch diff --git a/.yarn/patches/mongodb-npm-4.17.2-40d1286d70.patch b/.yarn/patches/mongodb-npm-4.17.2-40d1286d70.patch new file mode 100644 index 000000000000..b34fbe1aa665 --- /dev/null +++ b/.yarn/patches/mongodb-npm-4.17.2-40d1286d70.patch @@ -0,0 +1,13 @@ +diff --git a/mongodb.d.ts b/mongodb.d.ts +index 9696a0d0104095e8e4dfa4a4fe05fe81fd8b50c7..f5aa6e76fe6f2e6a55e6e2a720be15b7a6f98107 100644 +--- a/mongodb.d.ts ++++ b/mongodb.d.ts +@@ -5535,7 +5535,7 @@ export declare interface MonitorOptions extends Omit = Depth['length'] extends 8 ? [] : Type extends string | number | boolean | Date | RegExp | Buffer | Uint8Array | ((...args: any[]) => any) | { ++export declare type NestedPaths = Depth['length'] extends 3 ? [] : Type extends string | number | boolean | Date | RegExp | Buffer | Uint8Array | ((...args: any[]) => any) | { + _bsontype: string; + } ? [] : Type extends ReadonlyArray ? [] | [number, ...NestedPaths] : Type extends Map ? [string] : Type extends object ? { + [Key in Extract]: Type[Key] extends Type ? [Key] : Type extends Type[Key] ? [Key] : Type[Key] extends ReadonlyArray ? Type extends ArrayType ? [Key] : ArrayType extends Type ? [Key] : [ diff --git a/apps/meteor/ee/server/services/package.json b/apps/meteor/ee/server/services/package.json index f16819ffac26..6c8b8e1f9efe 100644 --- a/apps/meteor/ee/server/services/package.json +++ b/apps/meteor/ee/server/services/package.json @@ -39,7 +39,7 @@ "jaeger-client": "^3.19.0", "mem": "^8.1.1", "moleculer": "^0.14.34", - "mongodb": "^4.17.2", + "mongodb": "patch:mongodb@npm%3A4.17.2#~/.yarn/patches/mongodb-npm-4.17.2-40d1286d70.patch", "nats": "^2.6.1", "pino": "^8.15.0", "sodium-native": "^3.3.0", diff --git a/apps/meteor/package.json b/apps/meteor/package.json index 2b65d6ccd100..f41c3057d94d 100644 --- a/apps/meteor/package.json +++ b/apps/meteor/package.json @@ -379,7 +379,7 @@ "moment": "^2.29.4", "moment-timezone": "^0.5.46", "mongo-message-queue": "^1.0.0", - "mongodb": "^4.17.2", + "mongodb": "patch:mongodb@npm%3A4.17.2#~/.yarn/patches/mongodb-npm-4.17.2-40d1286d70.patch", "nats": "^2.6.1", "node-dogstatsd": "^0.0.7", "node-fetch": "2.7.0", diff --git a/ee/apps/account-service/package.json b/ee/apps/account-service/package.json index b7fd8b3bc1e3..78326b6065f7 100644 --- a/ee/apps/account-service/package.json +++ b/ee/apps/account-service/package.json @@ -32,7 +32,7 @@ "gc-stats": "^1.4.1", "mem": "^8.1.1", "moleculer": "^0.14.34", - "mongodb": "^4.17.2", + "mongodb": "patch:mongodb@npm%3A4.17.2#~/.yarn/patches/mongodb-npm-4.17.2-40d1286d70.patch", "nats": "^2.4.0", "pino": "^8.15.0", "polka": "^0.5.2", diff --git a/ee/apps/authorization-service/package.json b/ee/apps/authorization-service/package.json index e17a7aca2843..3ce8f9734916 100644 --- a/ee/apps/authorization-service/package.json +++ b/ee/apps/authorization-service/package.json @@ -30,7 +30,7 @@ "gc-stats": "^1.4.1", "mem": "^8.1.1", "moleculer": "^0.14.34", - "mongodb": "^4.17.2", + "mongodb": "patch:mongodb@npm%3A4.17.2#~/.yarn/patches/mongodb-npm-4.17.2-40d1286d70.patch", "nats": "^2.4.0", "pino": "^8.15.0", "polka": "^0.5.2" diff --git a/ee/apps/ddp-streamer/package.json b/ee/apps/ddp-streamer/package.json index 0220828561d2..edfbb38e5cbf 100644 --- a/ee/apps/ddp-streamer/package.json +++ b/ee/apps/ddp-streamer/package.json @@ -33,7 +33,7 @@ "jaeger-client": "^3.19.0", "mem": "^8.1.1", "moleculer": "^0.14.34", - "mongodb": "^4.17.2", + "mongodb": "patch:mongodb@npm%3A4.17.2#~/.yarn/patches/mongodb-npm-4.17.2-40d1286d70.patch", "nats": "^2.4.0", "pino": "^8.15.0", "polka": "^0.5.2", diff --git a/ee/apps/omnichannel-transcript/package.json b/ee/apps/omnichannel-transcript/package.json index b6d6db13ca7b..b88cf06956a4 100644 --- a/ee/apps/omnichannel-transcript/package.json +++ b/ee/apps/omnichannel-transcript/package.json @@ -36,7 +36,7 @@ "moleculer": "^0.14.34", "moment-timezone": "^0.5.46", "mongo-message-queue": "^1.0.0", - "mongodb": "^4.17.2", + "mongodb": "patch:mongodb@npm%3A4.17.2#~/.yarn/patches/mongodb-npm-4.17.2-40d1286d70.patch", "nats": "^2.4.0", "pino": "^8.15.0", "polka": "^0.5.2" diff --git a/ee/apps/presence-service/package.json b/ee/apps/presence-service/package.json index 7480963d87ac..d4d8883596db 100644 --- a/ee/apps/presence-service/package.json +++ b/ee/apps/presence-service/package.json @@ -30,7 +30,7 @@ "gc-stats": "^1.4.1", "mem": "^8.1.1", "moleculer": "^0.14.34", - "mongodb": "^4.17.2", + "mongodb": "patch:mongodb@npm%3A4.17.2#~/.yarn/patches/mongodb-npm-4.17.2-40d1286d70.patch", "nats": "^2.4.0", "pino": "^8.15.0", "polka": "^0.5.2" diff --git a/ee/apps/queue-worker/package.json b/ee/apps/queue-worker/package.json index 7fb0a7185c4c..f1853bc41218 100644 --- a/ee/apps/queue-worker/package.json +++ b/ee/apps/queue-worker/package.json @@ -33,7 +33,7 @@ "moleculer": "^0.14.34", "moment-timezone": "^0.5.46", "mongo-message-queue": "^1.0.0", - "mongodb": "^4.17.2", + "mongodb": "patch:mongodb@npm%3A4.17.2#~/.yarn/patches/mongodb-npm-4.17.2-40d1286d70.patch", "nats": "^2.4.0", "pino": "^8.15.0", "polka": "^0.5.2" diff --git a/ee/apps/stream-hub-service/package.json b/ee/apps/stream-hub-service/package.json index 855fbf51346b..382b252a3bf9 100644 --- a/ee/apps/stream-hub-service/package.json +++ b/ee/apps/stream-hub-service/package.json @@ -30,7 +30,7 @@ "gc-stats": "^1.4.1", "mem": "^8.1.1", "moleculer": "^0.14.34", - "mongodb": "^4.17.2", + "mongodb": "patch:mongodb@npm%3A4.17.2#~/.yarn/patches/mongodb-npm-4.17.2-40d1286d70.patch", "nats": "^2.4.0", "pino": "^8.15.0", "polka": "^0.5.2" diff --git a/ee/packages/omnichannel-services/package.json b/ee/packages/omnichannel-services/package.json index 96e7462b0dbd..33f7657fcde5 100644 --- a/ee/packages/omnichannel-services/package.json +++ b/ee/packages/omnichannel-services/package.json @@ -29,7 +29,7 @@ "mem": "^8.1.1", "moment-timezone": "^0.5.46", "mongo-message-queue": "^1.0.0", - "mongodb": "^4.17.2", + "mongodb": "patch:mongodb@npm%3A4.17.2#~/.yarn/patches/mongodb-npm-4.17.2-40d1286d70.patch", "pino": "^8.15.0" }, "scripts": { diff --git a/ee/packages/presence/package.json b/ee/packages/presence/package.json index 5a185735068c..ceea768076ef 100644 --- a/ee/packages/presence/package.json +++ b/ee/packages/presence/package.json @@ -35,6 +35,6 @@ "@rocket.chat/core-services": "workspace:^", "@rocket.chat/core-typings": "workspace:^", "@rocket.chat/models": "workspace:^", - "mongodb": "^4.17.2" + "mongodb": "patch:mongodb@npm%3A4.17.2#~/.yarn/patches/mongodb-npm-4.17.2-40d1286d70.patch" } } diff --git a/package.json b/package.json index 6101f0e8c3d3..f7f22c694fa9 100644 --- a/package.json +++ b/package.json @@ -65,7 +65,8 @@ "mongodb@^4.17.1": "patch:mongodb@npm:4.17.1#.yarn/patches/mongodb-npm-4.17.1-a2fe811ff1.patch", "@rocket.chat/forked-matrix-sdk-crypto-nodejs": "0.1.0-beta.13", "typia@~6.9.0": "patch:typia@npm%3A6.9.0#./.yarn/patches/typia-npm-6.9.0-2fd4d85f25.patch", - "moleculer@^0.14.34": "patch:moleculer@npm%3A0.14.34#./.yarn/patches/moleculer-npm-0.14.34-440e26767d.patch" + "moleculer@^0.14.34": "patch:moleculer@npm%3A0.14.34#./.yarn/patches/moleculer-npm-0.14.34-440e26767d.patch", + "mongodb@npm:^4.3.1": "patch:mongodb@npm%3A4.17.2#~/.yarn/patches/mongodb-npm-4.17.2-40d1286d70.patch" }, "dependencies": { "node-gyp": "^9.4.1" diff --git a/packages/agenda/package.json b/packages/agenda/package.json index 1373e1cda88c..da3b9ea31670 100644 --- a/packages/agenda/package.json +++ b/packages/agenda/package.json @@ -9,7 +9,7 @@ "debug": "~4.1.1", "human-interval": "^2.0.1", "moment-timezone": "~0.5.46", - "mongodb": "^4.17.2" + "mongodb": "patch:mongodb@npm%3A4.17.2#~/.yarn/patches/mongodb-npm-4.17.2-40d1286d70.patch" }, "devDependencies": { "@types/debug": "^4.1.12", diff --git a/packages/core-services/package.json b/packages/core-services/package.json index 8fd497e8c98d..f38acd8f5307 100644 --- a/packages/core-services/package.json +++ b/packages/core-services/package.json @@ -13,7 +13,7 @@ "babel-jest": "^29.5.0", "eslint": "~8.45.0", "jest": "~29.7.0", - "mongodb": "^4.17.2", + "mongodb": "patch:mongodb@npm%3A4.17.2#~/.yarn/patches/mongodb-npm-4.17.2-40d1286d70.patch", "prettier": "~2.8.8", "typescript": "~5.5.4" }, diff --git a/packages/core-typings/package.json b/packages/core-typings/package.json index 933a632b2e1e..5efbc53e2602 100644 --- a/packages/core-typings/package.json +++ b/packages/core-typings/package.json @@ -7,7 +7,7 @@ "@rocket.chat/apps-engine": "workspace:^", "@rocket.chat/eslint-config": "workspace:^", "eslint": "~8.45.0", - "mongodb": "^4.17.2", + "mongodb": "patch:mongodb@npm%3A4.17.2#~/.yarn/patches/mongodb-npm-4.17.2-40d1286d70.patch", "prettier": "~2.8.8", "typescript": "~5.5.4" }, diff --git a/packages/cron/package.json b/packages/cron/package.json index 133a0e9c72fc..473506a13533 100644 --- a/packages/cron/package.json +++ b/packages/cron/package.json @@ -22,6 +22,6 @@ "@rocket.chat/core-typings": "workspace:^", "@rocket.chat/models": "workspace:^", "@rocket.chat/random": "workspace:^", - "mongodb": "^4.17.2" + "mongodb": "patch:mongodb@npm%3A4.17.2#~/.yarn/patches/mongodb-npm-4.17.2-40d1286d70.patch" } } diff --git a/packages/instance-status/package.json b/packages/instance-status/package.json index f369ebfa0cfc..be23f8a44498 100644 --- a/packages/instance-status/package.json +++ b/packages/instance-status/package.json @@ -5,7 +5,7 @@ "devDependencies": { "@rocket.chat/eslint-config": "workspace:^", "eslint": "~8.45.0", - "mongodb": "^4.17.2", + "mongodb": "patch:mongodb@npm%3A4.17.2#~/.yarn/patches/mongodb-npm-4.17.2-40d1286d70.patch", "prettier": "~2.8.8", "typescript": "~5.5.4" }, diff --git a/packages/model-typings/package.json b/packages/model-typings/package.json index 5864b7363a0d..936abcb632fb 100644 --- a/packages/model-typings/package.json +++ b/packages/model-typings/package.json @@ -5,7 +5,7 @@ "devDependencies": { "@types/node-rsa": "^1.1.4", "eslint": "~8.45.0", - "mongodb": "^4.17.2", + "mongodb": "patch:mongodb@npm%3A4.17.2#~/.yarn/patches/mongodb-npm-4.17.2-40d1286d70.patch", "typescript": "~5.5.4" }, "scripts": { diff --git a/packages/rest-typings/package.json b/packages/rest-typings/package.json index 255c524c97b0..83f4632d1433 100644 --- a/packages/rest-typings/package.json +++ b/packages/rest-typings/package.json @@ -7,7 +7,7 @@ "@types/jest": "~29.5.13", "eslint": "~8.45.0", "jest": "~29.7.0", - "mongodb": "^4.17.2", + "mongodb": "patch:mongodb@npm%3A4.17.2#~/.yarn/patches/mongodb-npm-4.17.2-40d1286d70.patch", "typescript": "~5.5.4" }, "scripts": { diff --git a/packages/ui-contexts/package.json b/packages/ui-contexts/package.json index 6836d4f0f570..0e29ba67d05e 100644 --- a/packages/ui-contexts/package.json +++ b/packages/ui-contexts/package.json @@ -14,7 +14,7 @@ "@types/use-sync-external-store": "^0.0.6", "eslint": "~8.45.0", "eslint-plugin-react-hooks": "^4.6.2", - "mongodb": "^4.17.2", + "mongodb": "patch:mongodb@npm%3A4.17.2#~/.yarn/patches/mongodb-npm-4.17.2-40d1286d70.patch", "react": "~17.0.2", "typescript": "~5.5.4", "use-sync-external-store": "^1.2.2" diff --git a/yarn.lock b/yarn.lock index 232055a40661..07792f63edfa 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7911,7 +7911,7 @@ __metadata: gc-stats: "npm:^1.4.1" mem: "npm:^8.1.1" moleculer: "npm:^0.14.34" - mongodb: "npm:^4.17.2" + mongodb: "patch:mongodb@npm%3A4.17.2#~/.yarn/patches/mongodb-npm-4.17.2-40d1286d70.patch" nats: "npm:^2.4.0" pino: "npm:^8.15.0" polka: "npm:^0.5.2" @@ -7941,7 +7941,7 @@ __metadata: eslint: "npm:~8.45.0" human-interval: "npm:^2.0.1" moment-timezone: "npm:~0.5.46" - mongodb: "npm:^4.17.2" + mongodb: "patch:mongodb@npm%3A4.17.2#~/.yarn/patches/mongodb-npm-4.17.2-40d1286d70.patch" typescript: "npm:~5.5.4" languageName: unknown linkType: soft @@ -8044,7 +8044,7 @@ __metadata: gc-stats: "npm:^1.4.1" mem: "npm:^8.1.1" moleculer: "npm:^0.14.34" - mongodb: "npm:^4.17.2" + mongodb: "patch:mongodb@npm%3A4.17.2#~/.yarn/patches/mongodb-npm-4.17.2-40d1286d70.patch" nats: "npm:^2.4.0" pino: "npm:^8.15.0" polka: "npm:^0.5.2" @@ -8099,7 +8099,7 @@ __metadata: babel-jest: "npm:^29.5.0" eslint: "npm:~8.45.0" jest: "npm:~29.7.0" - mongodb: "npm:^4.17.2" + mongodb: "patch:mongodb@npm%3A4.17.2#~/.yarn/patches/mongodb-npm-4.17.2-40d1286d70.patch" prettier: "npm:~2.8.8" typescript: "npm:~5.5.4" languageName: unknown @@ -8116,7 +8116,7 @@ __metadata: "@rocket.chat/ui-kit": "workspace:~" "@types/express": "npm:^4.17.21" eslint: "npm:~8.45.0" - mongodb: "npm:^4.17.2" + mongodb: "patch:mongodb@npm%3A4.17.2#~/.yarn/patches/mongodb-npm-4.17.2-40d1286d70.patch" prettier: "npm:~2.8.8" typescript: "npm:~5.5.4" languageName: unknown @@ -8131,7 +8131,7 @@ __metadata: "@rocket.chat/models": "workspace:^" "@rocket.chat/random": "workspace:^" eslint: "npm:~8.45.0" - mongodb: "npm:^4.17.2" + mongodb: "patch:mongodb@npm%3A4.17.2#~/.yarn/patches/mongodb-npm-4.17.2-40d1286d70.patch" typescript: "npm:~5.5.4" languageName: unknown linkType: soft @@ -8212,7 +8212,7 @@ __metadata: jaeger-client: "npm:^3.19.0" mem: "npm:^8.1.1" moleculer: "npm:^0.14.34" - mongodb: "npm:^4.17.2" + mongodb: "patch:mongodb@npm%3A4.17.2#~/.yarn/patches/mongodb-npm-4.17.2-40d1286d70.patch" nats: "npm:^2.4.0" pino: "npm:^8.15.0" pino-pretty: "npm:^7.6.1" @@ -8570,7 +8570,7 @@ __metadata: "@rocket.chat/eslint-config": "workspace:^" "@rocket.chat/models": "workspace:^" eslint: "npm:~8.45.0" - mongodb: "npm:^4.17.2" + mongodb: "patch:mongodb@npm%3A4.17.2#~/.yarn/patches/mongodb-npm-4.17.2-40d1286d70.patch" prettier: "npm:~2.8.8" typescript: "npm:~5.5.4" languageName: unknown @@ -9111,7 +9111,7 @@ __metadata: moment: "npm:^2.29.4" moment-timezone: "npm:^0.5.46" mongo-message-queue: "npm:^1.0.0" - mongodb: "npm:^4.17.2" + mongodb: "patch:mongodb@npm%3A4.17.2#~/.yarn/patches/mongodb-npm-4.17.2-40d1286d70.patch" nats: "npm:^2.6.1" node-dogstatsd: "npm:^0.0.7" node-fetch: "npm:2.7.0" @@ -9228,7 +9228,7 @@ __metadata: "@rocket.chat/core-typings": "workspace:^" "@types/node-rsa": "npm:^1.1.4" eslint: "npm:~8.45.0" - mongodb: "npm:^4.17.2" + mongodb: "patch:mongodb@npm%3A4.17.2#~/.yarn/patches/mongodb-npm-4.17.2-40d1286d70.patch" typescript: "npm:~5.5.4" languageName: unknown linkType: soft @@ -9303,7 +9303,7 @@ __metadata: mem: "npm:^8.1.1" moment-timezone: "npm:^0.5.46" mongo-message-queue: "npm:^1.0.0" - mongodb: "npm:^4.17.2" + mongodb: "patch:mongodb@npm%3A4.17.2#~/.yarn/patches/mongodb-npm-4.17.2-40d1286d70.patch" pino: "npm:^8.15.0" typescript: "npm:~5.5.4" languageName: unknown @@ -9338,7 +9338,7 @@ __metadata: moleculer: "npm:^0.14.34" moment-timezone: "npm:^0.5.46" mongo-message-queue: "npm:^1.0.0" - mongodb: "npm:^4.17.2" + mongodb: "patch:mongodb@npm%3A4.17.2#~/.yarn/patches/mongodb-npm-4.17.2-40d1286d70.patch" nats: "npm:^2.4.0" pino: "npm:^8.15.0" polka: "npm:^0.5.2" @@ -9475,7 +9475,7 @@ __metadata: gc-stats: "npm:^1.4.1" mem: "npm:^8.1.1" moleculer: "npm:^0.14.34" - mongodb: "npm:^4.17.2" + mongodb: "patch:mongodb@npm%3A4.17.2#~/.yarn/patches/mongodb-npm-4.17.2-40d1286d70.patch" nats: "npm:^2.4.0" pino: "npm:^8.15.0" polka: "npm:^0.5.2" @@ -9501,7 +9501,7 @@ __metadata: babel-jest: "npm:^29.0.3" eslint: "npm:~8.45.0" jest: "npm:~29.7.0" - mongodb: "npm:^4.17.2" + mongodb: "patch:mongodb@npm%3A4.17.2#~/.yarn/patches/mongodb-npm-4.17.2-40d1286d70.patch" typescript: "npm:~5.5.4" languageName: unknown linkType: soft @@ -9541,7 +9541,7 @@ __metadata: moleculer: "npm:^0.14.34" moment-timezone: "npm:^0.5.46" mongo-message-queue: "npm:^1.0.0" - mongodb: "npm:^4.17.2" + mongodb: "patch:mongodb@npm%3A4.17.2#~/.yarn/patches/mongodb-npm-4.17.2-40d1286d70.patch" nats: "npm:^2.4.0" pino: "npm:^8.15.0" polka: "npm:^0.5.2" @@ -9614,7 +9614,7 @@ __metadata: ajv-formats: "npm:^2.1.1" eslint: "npm:~8.45.0" jest: "npm:~29.7.0" - mongodb: "npm:^4.17.2" + mongodb: "patch:mongodb@npm%3A4.17.2#~/.yarn/patches/mongodb-npm-4.17.2-40d1286d70.patch" typescript: "npm:~5.5.4" languageName: unknown linkType: soft @@ -9698,7 +9698,7 @@ __metadata: gc-stats: "npm:^1.4.1" mem: "npm:^8.1.1" moleculer: "npm:^0.14.34" - mongodb: "npm:^4.17.2" + mongodb: "patch:mongodb@npm%3A4.17.2#~/.yarn/patches/mongodb-npm-4.17.2-40d1286d70.patch" nats: "npm:^2.4.0" pino: "npm:^8.15.0" polka: "npm:^0.5.2" @@ -9873,7 +9873,7 @@ __metadata: "@types/use-sync-external-store": "npm:^0.0.6" eslint: "npm:~8.45.0" eslint-plugin-react-hooks: "npm:^4.6.2" - mongodb: "npm:^4.17.2" + mongodb: "patch:mongodb@npm%3A4.17.2#~/.yarn/patches/mongodb-npm-4.17.2-40d1286d70.patch" react: "npm:~17.0.2" typescript: "npm:~5.5.4" use-sync-external-store: "npm:^1.2.2" @@ -28327,7 +28327,7 @@ __metadata: languageName: node linkType: hard -"mongodb@npm:^4.17.2, mongodb@npm:^4.3.1": +"mongodb@npm:4.17.2": version: 4.17.2 resolution: "mongodb@npm:4.17.2" dependencies: @@ -28345,6 +28345,24 @@ __metadata: languageName: node linkType: hard +"mongodb@patch:mongodb@npm%3A4.17.2#~/.yarn/patches/mongodb-npm-4.17.2-40d1286d70.patch": + version: 4.17.2 + resolution: "mongodb@patch:mongodb@npm%3A4.17.2#~/.yarn/patches/mongodb-npm-4.17.2-40d1286d70.patch::version=4.17.2&hash=e26c00" + dependencies: + "@aws-sdk/credential-providers": "npm:^3.186.0" + "@mongodb-js/saslprep": "npm:^1.1.0" + bson: "npm:^4.7.2" + mongodb-connection-string-url: "npm:^2.6.0" + socks: "npm:^2.7.1" + dependenciesMeta: + "@aws-sdk/credential-providers": + optional: true + "@mongodb-js/saslprep": + optional: true + checksum: 10/6fe983f51a6e7f2a621788d36630cf7a1e87d29ee72d3665186cebb661927b8d62471972d6a8182d40f226e54ebbcd895e09cb00bbbf65ae0050ae55b3009ce4 + languageName: node + linkType: hard + "moo@npm:^0.5.0, moo@npm:^0.5.1": version: 0.5.1 resolution: "moo@npm:0.5.1" @@ -33341,7 +33359,7 @@ __metadata: jaeger-client: "npm:^3.19.0" mem: "npm:^8.1.1" moleculer: "npm:^0.14.34" - mongodb: "npm:^4.17.2" + mongodb: "patch:mongodb@npm%3A4.17.2#~/.yarn/patches/mongodb-npm-4.17.2-40d1286d70.patch" nats: "npm:^2.6.1" npm-run-all: "npm:^4.1.5" pino: "npm:^8.15.0" From 916e205011273d973feae8e6db087ca1fe2500c0 Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo Date: Fri, 18 Oct 2024 11:10:52 -0300 Subject: [PATCH 06/25] chore: bump babel (#33535) --- apps/meteor/package.json | 12 +- ee/packages/presence/package.json | 6 +- packages/base64/package.json | 4 +- packages/core-services/package.json | 6 +- packages/eslint-config/package.json | 4 +- packages/fuselage-ui-kit/package.json | 8 +- packages/gazzodown/package.json | 2 +- packages/livechat/package.json | 6 +- packages/message-parser/package.json | 6 +- packages/random/package.json | 4 +- packages/sha256/package.json | 4 +- packages/ui-avatar/package.json | 2 +- packages/ui-client/package.json | 2 +- packages/ui-composer/package.json | 2 +- packages/ui-kit/package.json | 8 +- packages/ui-video-conf/package.json | 2 +- packages/ui-voip/package.json | 2 +- packages/web-ui-registration/package.json | 8 +- yarn.lock | 1747 +++++++++------------ 19 files changed, 770 insertions(+), 1065 deletions(-) diff --git a/apps/meteor/package.json b/apps/meteor/package.json index f41c3057d94d..c2f5ffbcc819 100644 --- a/apps/meteor/package.json +++ b/apps/meteor/package.json @@ -64,11 +64,11 @@ }, "devDependencies": { "@axe-core/playwright": "^4.7.3", - "@babel/core": "~7.22.20", - "@babel/eslint-parser": "~7.23.10", - "@babel/preset-env": "~7.22.20", - "@babel/preset-react": "~7.22.15", - "@babel/register": "~7.22.15", + "@babel/core": "~7.25.8", + "@babel/eslint-parser": "~7.25.8", + "@babel/preset-env": "~7.25.8", + "@babel/preset-react": "~7.25.7", + "@babel/register": "~7.25.7", "@faker-js/faker": "~8.0.2", "@playwright/test": "^1.40.1", "@rocket.chat/eslint-config": "workspace:^", @@ -215,7 +215,7 @@ "typescript": "~5.5.4" }, "dependencies": { - "@babel/runtime": "~7.22.15", + "@babel/runtime": "~7.25.7", "@bugsnag/js": "~7.20.2", "@bugsnag/plugin-react": "~7.19.0", "@google-cloud/storage": "^6.11.0", diff --git a/ee/packages/presence/package.json b/ee/packages/presence/package.json index ceea768076ef..cc20990d0873 100644 --- a/ee/packages/presence/package.json +++ b/ee/packages/presence/package.json @@ -3,9 +3,9 @@ "version": "0.2.8", "private": true, "devDependencies": { - "@babel/core": "~7.22.20", - "@babel/preset-env": "~7.22.20", - "@babel/preset-typescript": "~7.22.15", + "@babel/core": "~7.25.8", + "@babel/preset-env": "~7.25.8", + "@babel/preset-typescript": "~7.25.7", "@rocket.chat/apps-engine": "workspace:^", "@rocket.chat/eslint-config": "workspace:^", "@rocket.chat/rest-typings": "workspace:^", diff --git a/packages/base64/package.json b/packages/base64/package.json index 441fed0e06c1..4c135b864254 100644 --- a/packages/base64/package.json +++ b/packages/base64/package.json @@ -13,8 +13,8 @@ "test": "jest" }, "devDependencies": { - "@babel/core": "~7.22.20", - "@babel/preset-env": "~7.22.20", + "@babel/core": "~7.25.8", + "@babel/preset-env": "~7.25.8", "@rocket.chat/eslint-config": "workspace:^", "@rocket.chat/jest-presets": "workspace:~", "@typescript-eslint/eslint-plugin": "~5.60.1", diff --git a/packages/core-services/package.json b/packages/core-services/package.json index f38acd8f5307..4f81be0fac02 100644 --- a/packages/core-services/package.json +++ b/packages/core-services/package.json @@ -3,9 +3,9 @@ "version": "0.7.0", "private": true, "devDependencies": { - "@babel/core": "~7.22.20", - "@babel/preset-env": "~7.22.20", - "@babel/preset-typescript": "~7.22.15", + "@babel/core": "~7.25.8", + "@babel/preset-env": "~7.25.8", + "@babel/preset-typescript": "~7.25.7", "@rocket.chat/apps-engine": "workspace:^", "@rocket.chat/eslint-config": "workspace:^", "@rocket.chat/jest-presets": "workspace:~", diff --git a/packages/eslint-config/package.json b/packages/eslint-config/package.json index 4915221b71ca..240289379aac 100644 --- a/packages/eslint-config/package.json +++ b/packages/eslint-config/package.json @@ -3,8 +3,8 @@ "version": "0.7.0", "description": "Rocket.Chat's JS/TS ESLint config", "dependencies": { - "@babel/core": "^7.20.7", - "@babel/eslint-parser": "~7.23.10", + "@babel/core": "^7.25.8", + "@babel/eslint-parser": "~7.25.8", "@types/eslint": "~8.44.9", "@types/prettier": "^2.6.3", "@typescript-eslint/eslint-plugin": "~5.60.1", diff --git a/packages/fuselage-ui-kit/package.json b/packages/fuselage-ui-kit/package.json index 8ce92484eb3c..13aa830cd6f1 100644 --- a/packages/fuselage-ui-kit/package.json +++ b/packages/fuselage-ui-kit/package.json @@ -45,10 +45,10 @@ "@rocket.chat/ui-kit": "workspace:~" }, "devDependencies": { - "@babel/core": "~7.22.20", - "@babel/preset-env": "~7.22.20", - "@babel/preset-react": "~7.22.15", - "@babel/preset-typescript": "~7.22.15", + "@babel/core": "~7.25.8", + "@babel/preset-env": "~7.25.8", + "@babel/preset-react": "~7.25.7", + "@babel/preset-typescript": "~7.25.7", "@rocket.chat/apps-engine": "workspace:^", "@rocket.chat/core-typings": "workspace:^", "@rocket.chat/eslint-config": "workspace:^", diff --git a/packages/gazzodown/package.json b/packages/gazzodown/package.json index 3d92fa8b6bb5..792695fa890e 100644 --- a/packages/gazzodown/package.json +++ b/packages/gazzodown/package.json @@ -27,7 +27,7 @@ "react-error-boundary": "^3.1.4" }, "devDependencies": { - "@babel/core": "~7.22.20", + "@babel/core": "~7.25.8", "@rocket.chat/core-typings": "workspace:^", "@rocket.chat/css-in-js": "~0.31.25", "@rocket.chat/fuselage": "^0.59.1", diff --git a/packages/livechat/package.json b/packages/livechat/package.json index f81eb3596207..740760ef1723 100644 --- a/packages/livechat/package.json +++ b/packages/livechat/package.json @@ -25,9 +25,9 @@ "typecheck": "tsc -p tsconfig.typecheck.json" }, "devDependencies": { - "@babel/eslint-parser": "~7.23.10", - "@babel/preset-env": "~7.22.20", - "@babel/preset-typescript": "~7.22.15", + "@babel/eslint-parser": "~7.25.8", + "@babel/preset-env": "~7.25.8", + "@babel/preset-typescript": "~7.25.7", "@rocket.chat/core-typings": "workspace:^", "@rocket.chat/ddp-client": "workspace:^", "@rocket.chat/eslint-config": "workspace:^", diff --git a/packages/message-parser/package.json b/packages/message-parser/package.json index c0a83ca2f095..3cd15485356a 100644 --- a/packages/message-parser/package.json +++ b/packages/message-parser/package.json @@ -48,9 +48,9 @@ "docs": "typedoc" }, "devDependencies": { - "@babel/core": "~7.21.8", - "@babel/eslint-parser": "~7.21.8", - "@babel/preset-env": "~7.21.5", + "@babel/core": "~7.25.8", + "@babel/eslint-parser": "~7.25.8", + "@babel/preset-env": "~7.25.8", "@rocket.chat/eslint-config": "workspace:^", "@rocket.chat/jest-presets": "workspace:~", "@rocket.chat/peggy-loader": "workspace:~", diff --git a/packages/random/package.json b/packages/random/package.json index 498e218338d9..db0d71da86b9 100644 --- a/packages/random/package.json +++ b/packages/random/package.json @@ -15,8 +15,8 @@ "test": "jest" }, "devDependencies": { - "@babel/core": "~7.22.20", - "@babel/preset-env": "~7.22.20", + "@babel/core": "~7.25.8", + "@babel/preset-env": "~7.25.8", "@rocket.chat/eslint-config": "workspace:^", "@rocket.chat/jest-presets": "workspace:~", "@typescript-eslint/eslint-plugin": "~5.60.1", diff --git a/packages/sha256/package.json b/packages/sha256/package.json index fb9e0d0d7f26..1ce6712489fe 100644 --- a/packages/sha256/package.json +++ b/packages/sha256/package.json @@ -14,8 +14,8 @@ "test": "jest" }, "devDependencies": { - "@babel/core": "~7.22.20", - "@babel/preset-env": "~7.22.20", + "@babel/core": "~7.25.8", + "@babel/preset-env": "~7.25.8", "@rocket.chat/eslint-config": "workspace:^", "@rocket.chat/jest-presets": "workspace:~", "@typescript-eslint/eslint-plugin": "~5.60.1", diff --git a/packages/ui-avatar/package.json b/packages/ui-avatar/package.json index 5a85ece4217b..058cd096df7d 100644 --- a/packages/ui-avatar/package.json +++ b/packages/ui-avatar/package.json @@ -3,7 +3,7 @@ "version": "7.0.0", "private": true, "devDependencies": { - "@babel/core": "~7.22.20", + "@babel/core": "~7.25.8", "@rocket.chat/fuselage": "^0.59.1", "@rocket.chat/ui-contexts": "workspace:^", "@types/react": "~17.0.80", diff --git a/packages/ui-client/package.json b/packages/ui-client/package.json index 20e2f45aa445..c40e5e2cc29c 100644 --- a/packages/ui-client/package.json +++ b/packages/ui-client/package.json @@ -18,7 +18,7 @@ "typecheck": "tsc --noEmit" }, "devDependencies": { - "@babel/core": "~7.22.20", + "@babel/core": "~7.25.8", "@react-aria/toolbar": "^3.0.0-beta.1", "@rocket.chat/css-in-js": "~0.31.25", "@rocket.chat/fuselage": "^0.59.1", diff --git a/packages/ui-composer/package.json b/packages/ui-composer/package.json index 3206eacb596f..ed11fd3012cf 100644 --- a/packages/ui-composer/package.json +++ b/packages/ui-composer/package.json @@ -18,7 +18,7 @@ "typecheck": "tsc --noEmit" }, "devDependencies": { - "@babel/core": "~7.22.20", + "@babel/core": "~7.25.8", "@react-aria/toolbar": "^3.0.0-beta.1", "@rocket.chat/eslint-config": "workspace:^", "@rocket.chat/fuselage": "^0.59.1", diff --git a/packages/ui-kit/package.json b/packages/ui-kit/package.json index d1f46490bda9..8fb2e9a84c9e 100644 --- a/packages/ui-kit/package.json +++ b/packages/ui-kit/package.json @@ -35,10 +35,10 @@ "test": "jest" }, "devDependencies": { - "@babel/core": "~7.21.8", - "@babel/eslint-parser": "~7.23.10", - "@babel/plugin-transform-runtime": "~7.21.4", - "@babel/preset-env": "~7.21.5", + "@babel/core": "~7.25.8", + "@babel/eslint-parser": "~7.25.8", + "@babel/plugin-transform-runtime": "~7.25.7", + "@babel/preset-env": "~7.25.8", "@rocket.chat/eslint-config": "workspace:~", "@rocket.chat/icons": "~0.38.0", "@rocket.chat/jest-presets": "workspace:~", diff --git a/packages/ui-video-conf/package.json b/packages/ui-video-conf/package.json index 85bc6d75c043..6940eb991dec 100644 --- a/packages/ui-video-conf/package.json +++ b/packages/ui-video-conf/package.json @@ -21,7 +21,7 @@ "@rocket.chat/emitter": "~0.31.25" }, "devDependencies": { - "@babel/core": "~7.22.20", + "@babel/core": "~7.25.8", "@rocket.chat/css-in-js": "~0.31.25", "@rocket.chat/eslint-config": "workspace:^", "@rocket.chat/fuselage": "^0.59.1", diff --git a/packages/ui-voip/package.json b/packages/ui-voip/package.json index 14a862fe5d18..3654e21aa468 100644 --- a/packages/ui-voip/package.json +++ b/packages/ui-voip/package.json @@ -24,7 +24,7 @@ "sip.js": "^0.20.1" }, "devDependencies": { - "@babel/core": "~7.22.20", + "@babel/core": "~7.25.8", "@faker-js/faker": "~8.0.2", "@rocket.chat/css-in-js": "~0.31.25", "@rocket.chat/eslint-config": "workspace:^", diff --git a/packages/web-ui-registration/package.json b/packages/web-ui-registration/package.json index 9a58aff9981a..958f01cc7aab 100644 --- a/packages/web-ui-registration/package.json +++ b/packages/web-ui-registration/package.json @@ -17,10 +17,10 @@ "typecheck": "tsc --noEmit" }, "devDependencies": { - "@babel/core": "~7.22.20", - "@babel/preset-env": "~7.22.20", - "@babel/preset-react": "~7.22.15", - "@babel/preset-typescript": "~7.22.15", + "@babel/core": "~7.25.8", + "@babel/preset-env": "~7.25.8", + "@babel/preset-react": "~7.25.7", + "@babel/preset-typescript": "~7.25.7", "@rocket.chat/i18n": "workspace:~", "@rocket.chat/layout": "~0.31.27", "@rocket.chat/mock-providers": "workspace:~", diff --git a/yarn.lock b/yarn.lock index 07792f63edfa..c54c20875ca0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -966,7 +966,7 @@ __metadata: languageName: node linkType: hard -"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.10.4, @babel/code-frame@npm:^7.12.13, @babel/code-frame@npm:^7.21.4, @babel/code-frame@npm:^7.22.13, @babel/code-frame@npm:^7.23.5": +"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.10.4, @babel/code-frame@npm:^7.12.13, @babel/code-frame@npm:^7.22.13, @babel/code-frame@npm:^7.23.5": version: 7.23.5 resolution: "@babel/code-frame@npm:7.23.5" dependencies: @@ -996,14 +996,21 @@ __metadata: languageName: node linkType: hard -"@babel/compat-data@npm:^7.17.7, @babel/compat-data@npm:^7.20.5, @babel/compat-data@npm:^7.21.5, @babel/compat-data@npm:^7.22.20, @babel/compat-data@npm:^7.22.6, @babel/compat-data@npm:^7.22.9, @babel/compat-data@npm:^7.25.7": +"@babel/compat-data@npm:^7.22.6": + version: 7.23.5 + resolution: "@babel/compat-data@npm:7.23.5" + checksum: 10/088f14f646ecbddd5ef89f120a60a1b3389a50a9705d44603dca77662707d0175a5e0e0da3943c3298f1907a4ab871468656fbbf74bb7842cd8b0686b2c19736 + languageName: node + linkType: hard + +"@babel/compat-data@npm:^7.25.7, @babel/compat-data@npm:^7.25.8": version: 7.25.8 resolution: "@babel/compat-data@npm:7.25.8" checksum: 10/269fcb0d89e02e36c8a11e0c1b960a6b4204e88f59f20c374d28f8e318f4cd5ded42dfedc4b54162065e6a10f71c0de651f5ed3f9b45d3a4b52240196df85726 languageName: node linkType: hard -"@babel/core@npm:^7.11.6, @babel/core@npm:^7.12.3, @babel/core@npm:^7.20.7, @babel/core@npm:^7.21.4, @babel/core@npm:^7.7.5, @babel/core@npm:~7.22.20": +"@babel/core@npm:^7.11.6, @babel/core@npm:^7.12.3, @babel/core@npm:^7.21.4, @babel/core@npm:^7.7.5": version: 7.22.20 resolution: "@babel/core@npm:7.22.20" dependencies: @@ -1026,7 +1033,7 @@ __metadata: languageName: node linkType: hard -"@babel/core@npm:^7.18.9, @babel/core@npm:^7.23.7": +"@babel/core@npm:^7.18.9, @babel/core@npm:^7.23.7, @babel/core@npm:^7.25.8, @babel/core@npm:~7.25.8": version: 7.25.8 resolution: "@babel/core@npm:7.25.8" dependencies: @@ -1049,58 +1056,21 @@ __metadata: languageName: node linkType: hard -"@babel/core@npm:~7.21.8": - version: 7.21.8 - resolution: "@babel/core@npm:7.21.8" - dependencies: - "@ampproject/remapping": "npm:^2.2.0" - "@babel/code-frame": "npm:^7.21.4" - "@babel/generator": "npm:^7.21.5" - "@babel/helper-compilation-targets": "npm:^7.21.5" - "@babel/helper-module-transforms": "npm:^7.21.5" - "@babel/helpers": "npm:^7.21.5" - "@babel/parser": "npm:^7.21.8" - "@babel/template": "npm:^7.20.7" - "@babel/traverse": "npm:^7.21.5" - "@babel/types": "npm:^7.21.5" - convert-source-map: "npm:^1.7.0" - debug: "npm:^4.1.0" - gensync: "npm:^1.0.0-beta.2" - json5: "npm:^2.2.2" - semver: "npm:^6.3.0" - checksum: 10/a71076dc27964e0754ad99f139f82876d3ed35489c1182aae9052813d36c92f4bd9ddab0e490d28ce8b1f33eea87885081adaedd1305bfc5ce6595c030a7bb0b - languageName: node - linkType: hard - -"@babel/eslint-parser@npm:~7.21.8": - version: 7.21.8 - resolution: "@babel/eslint-parser@npm:7.21.8" - dependencies: - "@nicolo-ribaudo/eslint-scope-5-internals": "npm:5.1.1-v1" - eslint-visitor-keys: "npm:^2.1.0" - semver: "npm:^6.3.0" - peerDependencies: - "@babel/core": ">=7.11.0" - eslint: ^7.5.0 || ^8.0.0 - checksum: 10/f1b1bdcc6c6638384ea046c4cbda31aa31e1be3d1ccc41c953bc557caa1684f23c1155ce9c4a3673d3733d9805ac1f76a560ff09c4d1fea9b44bb9ec49933d37 - languageName: node - linkType: hard - -"@babel/eslint-parser@npm:~7.23.10": - version: 7.23.10 - resolution: "@babel/eslint-parser@npm:7.23.10" +"@babel/eslint-parser@npm:~7.25.8": + version: 7.25.8 + resolution: "@babel/eslint-parser@npm:7.25.8" dependencies: "@nicolo-ribaudo/eslint-scope-5-internals": "npm:5.1.1-v1" eslint-visitor-keys: "npm:^2.1.0" semver: "npm:^6.3.1" peerDependencies: "@babel/core": ^7.11.0 - eslint: ^7.5.0 || ^8.0.0 - checksum: 10/eb62ad6a1098836331317be978ebd5991a9257d58118062f252b002e995b4f35b76a5dc976b07d84d21e64c8395587a044c5e6e444b3b69ab53e50a18facf2af + eslint: ^7.5.0 || ^8.0.0 || ^9.0.0 + checksum: 10/4e9e5881f57a4680ccdec1ee28586dda35480a4b16ca8953b94ea31152aa8a2a2c9311849001424b54d243467dab13b961ec8a0af0ac2c4cc3bba5887946f3ea languageName: node linkType: hard -"@babel/generator@npm:^7.21.5, @babel/generator@npm:^7.22.15, @babel/generator@npm:^7.25.7, @babel/generator@npm:^7.7.2": +"@babel/generator@npm:^7.22.15, @babel/generator@npm:^7.25.7, @babel/generator@npm:^7.7.2": version: 7.25.7 resolution: "@babel/generator@npm:7.25.7" dependencies: @@ -1136,7 +1106,7 @@ __metadata: languageName: node linkType: hard -"@babel/helper-annotate-as-pure@npm:^7.18.6, @babel/helper-annotate-as-pure@npm:^7.22.5": +"@babel/helper-annotate-as-pure@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helper-annotate-as-pure@npm:7.22.5" dependencies: @@ -1145,16 +1115,26 @@ __metadata: languageName: node linkType: hard -"@babel/helper-builder-binary-assignment-operator-visitor@npm:^7.22.15": - version: 7.22.15 - resolution: "@babel/helper-builder-binary-assignment-operator-visitor@npm:7.22.15" +"@babel/helper-annotate-as-pure@npm:^7.25.7": + version: 7.25.7 + resolution: "@babel/helper-annotate-as-pure@npm:7.25.7" dependencies: - "@babel/types": "npm:^7.22.15" - checksum: 10/639c697a1c729f9fafa2dd4c9af2e18568190299b5907bd4c2d0bc818fcbd1e83ffeecc2af24327a7faa7ac4c34edd9d7940510a5e66296c19bad17001cf5c7a + "@babel/types": "npm:^7.25.7" + checksum: 10/38044806cab33032391c46861cd0a36adb960525b00bc03f2f3d4380c983bf17971cdabc431e58b93a328ef24bd0271f1dc3a8c1c1ea6cab49d04702961451d8 + languageName: node + linkType: hard + +"@babel/helper-builder-binary-assignment-operator-visitor@npm:^7.25.7": + version: 7.25.7 + resolution: "@babel/helper-builder-binary-assignment-operator-visitor@npm:7.25.7" + dependencies: + "@babel/traverse": "npm:^7.25.7" + "@babel/types": "npm:^7.25.7" + checksum: 10/e493c4b7ea1dcb1e406cf30265164b632e133ea9a039a5ddc8eadd5370ad498eddcd99871fdf500b9ac05d0b43f2a0987580ceb1f7adb3b7272e49b56589849a languageName: node linkType: hard -"@babel/helper-compilation-targets@npm:^7.17.7, @babel/helper-compilation-targets@npm:^7.20.7, @babel/helper-compilation-targets@npm:^7.21.5, @babel/helper-compilation-targets@npm:^7.22.15, @babel/helper-compilation-targets@npm:^7.22.6, @babel/helper-compilation-targets@npm:^7.25.7": +"@babel/helper-compilation-targets@npm:^7.22.15, @babel/helper-compilation-targets@npm:^7.22.6, @babel/helper-compilation-targets@npm:^7.25.7": version: 7.25.7 resolution: "@babel/helper-compilation-targets@npm:7.25.7" dependencies: @@ -1167,26 +1147,24 @@ __metadata: languageName: node linkType: hard -"@babel/helper-create-class-features-plugin@npm:^7.18.6, @babel/helper-create-class-features-plugin@npm:^7.21.0, @babel/helper-create-class-features-plugin@npm:^7.22.11, @babel/helper-create-class-features-plugin@npm:^7.22.15, @babel/helper-create-class-features-plugin@npm:^7.22.5": - version: 7.22.15 - resolution: "@babel/helper-create-class-features-plugin@npm:7.22.15" +"@babel/helper-create-class-features-plugin@npm:^7.25.7": + version: 7.25.7 + resolution: "@babel/helper-create-class-features-plugin@npm:7.25.7" dependencies: - "@babel/helper-annotate-as-pure": "npm:^7.22.5" - "@babel/helper-environment-visitor": "npm:^7.22.5" - "@babel/helper-function-name": "npm:^7.22.5" - "@babel/helper-member-expression-to-functions": "npm:^7.22.15" - "@babel/helper-optimise-call-expression": "npm:^7.22.5" - "@babel/helper-replace-supers": "npm:^7.22.9" - "@babel/helper-skip-transparent-expression-wrappers": "npm:^7.22.5" - "@babel/helper-split-export-declaration": "npm:^7.22.6" + "@babel/helper-annotate-as-pure": "npm:^7.25.7" + "@babel/helper-member-expression-to-functions": "npm:^7.25.7" + "@babel/helper-optimise-call-expression": "npm:^7.25.7" + "@babel/helper-replace-supers": "npm:^7.25.7" + "@babel/helper-skip-transparent-expression-wrappers": "npm:^7.25.7" + "@babel/traverse": "npm:^7.25.7" semver: "npm:^6.3.1" peerDependencies: "@babel/core": ^7.0.0 - checksum: 10/000d29f1df397b7fdcb97ad0e9a442781787e5cb0456a9b8da690d13e03549a716bf74348029d3bd3fa4837b35d143a535cad1006f9d552063799ecdd96df672 + checksum: 10/76e3bb727d7541d38acaa9b6ecff88f70e62370396dd22511837b90a556c6815a7efd6fd25b499bf1c8b02cdb18c575781a6aba0c442c38a2129a403b5bf9b1e languageName: node linkType: hard -"@babel/helper-create-regexp-features-plugin@npm:^7.18.6, @babel/helper-create-regexp-features-plugin@npm:^7.22.15, @babel/helper-create-regexp-features-plugin@npm:^7.22.5": +"@babel/helper-create-regexp-features-plugin@npm:^7.18.6": version: 7.22.15 resolution: "@babel/helper-create-regexp-features-plugin@npm:7.22.15" dependencies: @@ -1199,25 +1177,22 @@ __metadata: languageName: node linkType: hard -"@babel/helper-define-polyfill-provider@npm:^0.3.3": - version: 0.3.3 - resolution: "@babel/helper-define-polyfill-provider@npm:0.3.3" +"@babel/helper-create-regexp-features-plugin@npm:^7.25.7": + version: 7.25.7 + resolution: "@babel/helper-create-regexp-features-plugin@npm:7.25.7" dependencies: - "@babel/helper-compilation-targets": "npm:^7.17.7" - "@babel/helper-plugin-utils": "npm:^7.16.7" - debug: "npm:^4.1.1" - lodash.debounce: "npm:^4.0.8" - resolve: "npm:^1.14.2" - semver: "npm:^6.1.2" + "@babel/helper-annotate-as-pure": "npm:^7.25.7" + regexpu-core: "npm:^6.1.1" + semver: "npm:^6.3.1" peerDependencies: - "@babel/core": ^7.4.0-0 - checksum: 10/a32b09f9d3827145347fca5105a33bc1a52ff8eb3d63e8eb4acc515f9b54a371862cc6ae376c275cdfa97ff9828975dde88fd6105a8d01107364200b52dfc9ad + "@babel/core": ^7.0.0 + checksum: 10/fa083f83ae9ba3326e32762c9839fea171de34d66bffc65569a6a67222ec55cf4ef35b6b26f332d24485c0622a69a2e0b9eb2a7ca279595b174cfeeec68849ac languageName: node linkType: hard -"@babel/helper-define-polyfill-provider@npm:^0.4.2": - version: 0.4.2 - resolution: "@babel/helper-define-polyfill-provider@npm:0.4.2" +"@babel/helper-define-polyfill-provider@npm:^0.6.2": + version: 0.6.2 + resolution: "@babel/helper-define-polyfill-provider@npm:0.6.2" dependencies: "@babel/helper-compilation-targets": "npm:^7.22.6" "@babel/helper-plugin-utils": "npm:^7.22.5" @@ -1226,18 +1201,18 @@ __metadata: resolve: "npm:^1.14.2" peerDependencies: "@babel/core": ^7.4.0 || ^8.0.0-0 <8.0.0 - checksum: 10/6383a34af4048957e46366fa7e6228b61e140955a707f8af7b69c26b2b780880db164d08b6de9420f6ec5a0ee01eb23aa5d78a4b141f2b65b3670e71906471bf + checksum: 10/bb32ec12024d3f16e70641bc125d2534a97edbfdabbc9f69001ec9c4ce46f877c7a224c566aa6c8c510c3b0def2e43dc4433bf6a40896ba5ce0cef4ea5ccbcff languageName: node linkType: hard -"@babel/helper-environment-visitor@npm:^7.18.9, @babel/helper-environment-visitor@npm:^7.22.20, @babel/helper-environment-visitor@npm:^7.22.5": +"@babel/helper-environment-visitor@npm:^7.22.20": version: 7.22.20 resolution: "@babel/helper-environment-visitor@npm:7.22.20" checksum: 10/d80ee98ff66f41e233f36ca1921774c37e88a803b2f7dca3db7c057a5fea0473804db9fb6729e5dbfd07f4bed722d60f7852035c2c739382e84c335661590b69 languageName: node linkType: hard -"@babel/helper-function-name@npm:^7.22.5, @babel/helper-function-name@npm:^7.23.0": +"@babel/helper-function-name@npm:^7.23.0": version: 7.23.0 resolution: "@babel/helper-function-name@npm:7.23.0" dependencies: @@ -1256,16 +1231,17 @@ __metadata: languageName: node linkType: hard -"@babel/helper-member-expression-to-functions@npm:^7.22.15": - version: 7.23.0 - resolution: "@babel/helper-member-expression-to-functions@npm:7.23.0" +"@babel/helper-member-expression-to-functions@npm:^7.25.7": + version: 7.25.7 + resolution: "@babel/helper-member-expression-to-functions@npm:7.25.7" dependencies: - "@babel/types": "npm:^7.23.0" - checksum: 10/325feb6e200478c8cd6e10433fabe993a7d3315cc1a2a457e45514a5f95a73dff4c69bea04cc2daea0ffe72d8ed85d504b3f00b2e0767b7d4f5ae25fec9b35b2 + "@babel/traverse": "npm:^7.25.7" + "@babel/types": "npm:^7.25.7" + checksum: 10/f953a0ddbcfbaae835033b54fdbf42cc3aea08c554875fccfc02ed4b1e5fe3ee06abf1b7a8419314357841fabc9efdbcbb8afdf07c4f216a73164a45a147562b languageName: node linkType: hard -"@babel/helper-module-imports@npm:^7.21.4, @babel/helper-module-imports@npm:^7.22.15, @babel/helper-module-imports@npm:^7.25.7": +"@babel/helper-module-imports@npm:^7.25.7": version: 7.25.7 resolution: "@babel/helper-module-imports@npm:7.25.7" dependencies: @@ -1275,7 +1251,7 @@ __metadata: languageName: node linkType: hard -"@babel/helper-module-transforms@npm:^7.21.5, @babel/helper-module-transforms@npm:^7.22.20, @babel/helper-module-transforms@npm:^7.23.3, @babel/helper-module-transforms@npm:^7.25.7": +"@babel/helper-module-transforms@npm:^7.22.20, @babel/helper-module-transforms@npm:^7.25.7": version: 7.25.7 resolution: "@babel/helper-module-transforms@npm:7.25.7" dependencies: @@ -1289,49 +1265,56 @@ __metadata: languageName: node linkType: hard -"@babel/helper-optimise-call-expression@npm:^7.22.5": - version: 7.22.5 - resolution: "@babel/helper-optimise-call-expression@npm:7.22.5" +"@babel/helper-optimise-call-expression@npm:^7.25.7": + version: 7.25.7 + resolution: "@babel/helper-optimise-call-expression@npm:7.25.7" dependencies: - "@babel/types": "npm:^7.22.5" - checksum: 10/c70ef6cc6b6ed32eeeec4482127e8be5451d0e5282d5495d5d569d39eb04d7f1d66ec99b327f45d1d5842a9ad8c22d48567e93fc502003a47de78d122e355f7c + "@babel/types": "npm:^7.25.7" + checksum: 10/8da0d9f5aae15991678ad1bbe58e52cd62a0ed36871075756d9684c0a7a65988ed81bab53ad6436c39a470d3cd690694dd2b07147817217e3ca87178a129c509 languageName: node linkType: hard -"@babel/helper-plugin-utils@npm:^7.0.0, @babel/helper-plugin-utils@npm:^7.10.4, @babel/helper-plugin-utils@npm:^7.12.13, @babel/helper-plugin-utils@npm:^7.14.5, @babel/helper-plugin-utils@npm:^7.16.7, @babel/helper-plugin-utils@npm:^7.18.6, @babel/helper-plugin-utils@npm:^7.18.9, @babel/helper-plugin-utils@npm:^7.19.0, @babel/helper-plugin-utils@npm:^7.20.2, @babel/helper-plugin-utils@npm:^7.21.5, @babel/helper-plugin-utils@npm:^7.22.5, @babel/helper-plugin-utils@npm:^7.8.0, @babel/helper-plugin-utils@npm:^7.8.3": +"@babel/helper-plugin-utils@npm:^7.0.0, @babel/helper-plugin-utils@npm:^7.10.4, @babel/helper-plugin-utils@npm:^7.12.13, @babel/helper-plugin-utils@npm:^7.14.5, @babel/helper-plugin-utils@npm:^7.18.6, @babel/helper-plugin-utils@npm:^7.19.0, @babel/helper-plugin-utils@npm:^7.20.2, @babel/helper-plugin-utils@npm:^7.22.5, @babel/helper-plugin-utils@npm:^7.8.0": version: 7.22.5 resolution: "@babel/helper-plugin-utils@npm:7.22.5" checksum: 10/ab220db218089a2aadd0582f5833fd17fa300245999f5f8784b10f5a75267c4e808592284a29438a0da365e702f05acb369f99e1c915c02f9f9210ec60eab8ea languageName: node linkType: hard -"@babel/helper-remap-async-to-generator@npm:^7.18.9, @babel/helper-remap-async-to-generator@npm:^7.22.20, @babel/helper-remap-async-to-generator@npm:^7.22.9": - version: 7.22.20 - resolution: "@babel/helper-remap-async-to-generator@npm:7.22.20" +"@babel/helper-plugin-utils@npm:^7.25.7": + version: 7.25.7 + resolution: "@babel/helper-plugin-utils@npm:7.25.7" + checksum: 10/e1b0ea5e67b05378d6360e3fc370e99bfb247eed9f68145b5cce541da703424e1887fb6fc60ab2f7f743c72dcbfbed79d3032af43f2c251c229c734dc2572a5b + languageName: node + linkType: hard + +"@babel/helper-remap-async-to-generator@npm:^7.25.7": + version: 7.25.7 + resolution: "@babel/helper-remap-async-to-generator@npm:7.25.7" dependencies: - "@babel/helper-annotate-as-pure": "npm:^7.22.5" - "@babel/helper-environment-visitor": "npm:^7.22.20" - "@babel/helper-wrap-function": "npm:^7.22.20" + "@babel/helper-annotate-as-pure": "npm:^7.25.7" + "@babel/helper-wrap-function": "npm:^7.25.7" + "@babel/traverse": "npm:^7.25.7" peerDependencies: "@babel/core": ^7.0.0 - checksum: 10/2fe6300a6f1b58211dffa0aed1b45d4958506d096543663dba83bd9251fe8d670fa909143a65b45e72acb49e7e20fbdb73eae315d9ddaced467948c3329986e7 + checksum: 10/3d563ac35cb1306bf70e7353fc807e7b082a7510d955a36db089fa861c6a8b2c470184996f3177d5384e5290a1be9e7eed424efb9e2dd3bed3a8cf6c2d6a9723 languageName: node linkType: hard -"@babel/helper-replace-supers@npm:^7.22.20, @babel/helper-replace-supers@npm:^7.22.9": - version: 7.22.20 - resolution: "@babel/helper-replace-supers@npm:7.22.20" +"@babel/helper-replace-supers@npm:^7.25.7": + version: 7.25.7 + resolution: "@babel/helper-replace-supers@npm:7.25.7" dependencies: - "@babel/helper-environment-visitor": "npm:^7.22.20" - "@babel/helper-member-expression-to-functions": "npm:^7.22.15" - "@babel/helper-optimise-call-expression": "npm:^7.22.5" + "@babel/helper-member-expression-to-functions": "npm:^7.25.7" + "@babel/helper-optimise-call-expression": "npm:^7.25.7" + "@babel/traverse": "npm:^7.25.7" peerDependencies: "@babel/core": ^7.0.0 - checksum: 10/617666f57b0f94a2f430ee66b67c8f6fa94d4c22400f622947580d8f3638ea34b71280af59599ed4afbb54ae6e2bdd4f9083fe0e341184a4bb0bd26ef58d3017 + checksum: 10/87b65c7b278fabcb67458e592082a0b4532d5400acbb51e496ea47763077d0a64dc0531d32bafcb1d51f04d61d4715dadb1fd0301bc8449c26fcfd06913eb45e languageName: node linkType: hard -"@babel/helper-simple-access@npm:^7.22.5, @babel/helper-simple-access@npm:^7.25.7": +"@babel/helper-simple-access@npm:^7.25.7": version: 7.25.7 resolution: "@babel/helper-simple-access@npm:7.25.7" dependencies: @@ -1341,12 +1324,13 @@ __metadata: languageName: node linkType: hard -"@babel/helper-skip-transparent-expression-wrappers@npm:^7.20.0, @babel/helper-skip-transparent-expression-wrappers@npm:^7.22.5": - version: 7.22.5 - resolution: "@babel/helper-skip-transparent-expression-wrappers@npm:7.22.5" +"@babel/helper-skip-transparent-expression-wrappers@npm:^7.25.7": + version: 7.25.7 + resolution: "@babel/helper-skip-transparent-expression-wrappers@npm:7.25.7" dependencies: - "@babel/types": "npm:^7.22.5" - checksum: 10/1012ef2295eb12dc073f2b9edf3425661e9b8432a3387e62a8bc27c42963f1f216ab3124228015c748770b2257b4f1fda882ca8fa34c0bf485e929ae5bc45244 + "@babel/traverse": "npm:^7.25.7" + "@babel/types": "npm:^7.25.7" + checksum: 10/466c81d09981bfb9e10aa6697ecb621389ff92a86187daaca34a969ca990d7327ebe931e87f7d52a200e499542d398469478d83dfaaa244d2f49df4e078490b3 languageName: node linkType: hard @@ -1394,25 +1378,25 @@ __metadata: languageName: node linkType: hard -"@babel/helper-validator-option@npm:^7.21.0, @babel/helper-validator-option@npm:^7.22.15, @babel/helper-validator-option@npm:^7.25.7": +"@babel/helper-validator-option@npm:^7.25.7": version: 7.25.7 resolution: "@babel/helper-validator-option@npm:7.25.7" checksum: 10/3c46cbdd666d176f90a0b7e952a0c6e92184b66633336eca79aca243d1f86085ec339a6e45c3d44efa9e03f1829b470a350ddafa70926af6bbf1ac611284f8d3 languageName: node linkType: hard -"@babel/helper-wrap-function@npm:^7.22.20": - version: 7.22.20 - resolution: "@babel/helper-wrap-function@npm:7.22.20" +"@babel/helper-wrap-function@npm:^7.25.7": + version: 7.25.7 + resolution: "@babel/helper-wrap-function@npm:7.25.7" dependencies: - "@babel/helper-function-name": "npm:^7.22.5" - "@babel/template": "npm:^7.22.15" - "@babel/types": "npm:^7.22.19" - checksum: 10/b22e4666dec3d401bdf8ebd01d448bb3733617dae5aa6fbd1b684a22a35653cca832edd876529fd139577713b44fb89b4f5e52b7315ab218620f78b8a8ae23de + "@babel/template": "npm:^7.25.7" + "@babel/traverse": "npm:^7.25.7" + "@babel/types": "npm:^7.25.7" + checksum: 10/00e2291a2b67e060b98cae90b4cc9107cff29d7b26bd5eb61149c63fb99418d9bd00bb0708b8b3e733cae4b1ea3a2b41a709d85192accfa15903f8af5c821fe6 languageName: node linkType: hard -"@babel/helpers@npm:^7.21.5, @babel/helpers@npm:^7.22.15, @babel/helpers@npm:^7.25.7": +"@babel/helpers@npm:^7.22.15, @babel/helpers@npm:^7.25.7": version: 7.25.7 resolution: "@babel/helpers@npm:7.25.7" dependencies: @@ -1457,7 +1441,7 @@ __metadata: languageName: node linkType: hard -"@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.14.7, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.21.8, @babel/parser@npm:^7.22.16, @babel/parser@npm:^7.25.7, @babel/parser@npm:^7.25.8": +"@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.14.7, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.22.16, @babel/parser@npm:^7.25.7, @babel/parser@npm:^7.25.8": version: 7.25.8 resolution: "@babel/parser@npm:7.25.8" dependencies: @@ -1488,190 +1472,62 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:^7.18.6, @babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:^7.22.15": - version: 7.23.3 - resolution: "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:7.23.3" +"@babel/plugin-bugfix-firefox-class-in-computed-class-key@npm:^7.25.7": + version: 7.25.7 + resolution: "@babel/plugin-bugfix-firefox-class-in-computed-class-key@npm:7.25.7" dependencies: - "@babel/helper-plugin-utils": "npm:^7.22.5" + "@babel/helper-plugin-utils": "npm:^7.25.7" + "@babel/traverse": "npm:^7.25.7" peerDependencies: "@babel/core": ^7.0.0 - checksum: 10/ddbaf2c396b7780f15e80ee01d6dd790db076985f3dfeb6527d1a8d4cacf370e49250396a3aa005b2c40233cac214a106232f83703d5e8491848bde273938232 - languageName: node - linkType: hard - -"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@npm:^7.20.7, @babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@npm:^7.22.15": - version: 7.23.3 - resolution: "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@npm:7.23.3" - dependencies: - "@babel/helper-plugin-utils": "npm:^7.22.5" - "@babel/helper-skip-transparent-expression-wrappers": "npm:^7.22.5" - "@babel/plugin-transform-optional-chaining": "npm:^7.23.3" - peerDependencies: - "@babel/core": ^7.13.0 - checksum: 10/434b9d710ae856fa1a456678cc304fbc93915af86d581ee316e077af746a709a741ea39d7e1d4f5b98861b629cc7e87f002d3138f5e836775632466d4c74aef2 - languageName: node - linkType: hard - -"@babel/plugin-proposal-async-generator-functions@npm:^7.20.7": - version: 7.20.7 - resolution: "@babel/plugin-proposal-async-generator-functions@npm:7.20.7" - dependencies: - "@babel/helper-environment-visitor": "npm:^7.18.9" - "@babel/helper-plugin-utils": "npm:^7.20.2" - "@babel/helper-remap-async-to-generator": "npm:^7.18.9" - "@babel/plugin-syntax-async-generators": "npm:^7.8.4" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 10/111109ee118c9e69982f08d5e119eab04190b36a0f40e22e873802d941956eee66d2aa5a15f5321e51e3f9aa70a91136451b987fe15185ef8cc547ac88937723 - languageName: node - linkType: hard - -"@babel/plugin-proposal-class-properties@npm:^7.18.6": - version: 7.18.6 - resolution: "@babel/plugin-proposal-class-properties@npm:7.18.6" - dependencies: - "@babel/helper-create-class-features-plugin": "npm:^7.18.6" - "@babel/helper-plugin-utils": "npm:^7.18.6" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 10/49a78a2773ec0db56e915d9797e44fd079ab8a9b2e1716e0df07c92532f2c65d76aeda9543883916b8e0ff13606afeffa67c5b93d05b607bc87653ad18a91422 - languageName: node - linkType: hard - -"@babel/plugin-proposal-class-static-block@npm:^7.21.0": - version: 7.21.0 - resolution: "@babel/plugin-proposal-class-static-block@npm:7.21.0" - dependencies: - "@babel/helper-create-class-features-plugin": "npm:^7.21.0" - "@babel/helper-plugin-utils": "npm:^7.20.2" - "@babel/plugin-syntax-class-static-block": "npm:^7.14.5" - peerDependencies: - "@babel/core": ^7.12.0 - checksum: 10/236c0ad089e7a7acab776cc1d355330193314bfcd62e94e78f2df35817c6144d7e0e0368976778afd6b7c13e70b5068fa84d7abbf967d4f182e60d03f9ef802b - languageName: node - linkType: hard - -"@babel/plugin-proposal-dynamic-import@npm:^7.18.6": - version: 7.18.6 - resolution: "@babel/plugin-proposal-dynamic-import@npm:7.18.6" - dependencies: - "@babel/helper-plugin-utils": "npm:^7.18.6" - "@babel/plugin-syntax-dynamic-import": "npm:^7.8.3" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 10/96b1c8a8ad8171d39e9ab106be33bde37ae09b22fb2c449afee9a5edf3c537933d79d963dcdc2694d10677cb96da739cdf1b53454e6a5deab9801f28a818bb2f - languageName: node - linkType: hard - -"@babel/plugin-proposal-export-namespace-from@npm:^7.18.9": - version: 7.18.9 - resolution: "@babel/plugin-proposal-export-namespace-from@npm:7.18.9" - dependencies: - "@babel/helper-plugin-utils": "npm:^7.18.9" - "@babel/plugin-syntax-export-namespace-from": "npm:^7.8.3" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 10/84ff22bacc5d30918a849bfb7e0e90ae4c5b8d8b65f2ac881803d1cf9068dffbe53bd657b0e4bc4c20b4db301b1c85f1e74183cf29a0dd31e964bd4e97c363ef - languageName: node - linkType: hard - -"@babel/plugin-proposal-json-strings@npm:^7.18.6": - version: 7.18.6 - resolution: "@babel/plugin-proposal-json-strings@npm:7.18.6" - dependencies: - "@babel/helper-plugin-utils": "npm:^7.18.6" - "@babel/plugin-syntax-json-strings": "npm:^7.8.3" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 10/25ba0e6b9d6115174f51f7c6787e96214c90dd4026e266976b248a2ed417fe50fddae72843ffb3cbe324014a18632ce5648dfac77f089da858022b49fd608cb3 - languageName: node - linkType: hard - -"@babel/plugin-proposal-logical-assignment-operators@npm:^7.20.7": - version: 7.20.7 - resolution: "@babel/plugin-proposal-logical-assignment-operators@npm:7.20.7" - dependencies: - "@babel/helper-plugin-utils": "npm:^7.20.2" - "@babel/plugin-syntax-logical-assignment-operators": "npm:^7.10.4" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 10/cdd7b8136cc4db3f47714d5266f9e7b592a2ac5a94a5878787ce08890e97c8ab1ca8e94b27bfeba7b0f2b1549a026d9fc414ca2196de603df36fb32633bbdc19 - languageName: node - linkType: hard - -"@babel/plugin-proposal-nullish-coalescing-operator@npm:^7.18.6": - version: 7.18.6 - resolution: "@babel/plugin-proposal-nullish-coalescing-operator@npm:7.18.6" - dependencies: - "@babel/helper-plugin-utils": "npm:^7.18.6" - "@babel/plugin-syntax-nullish-coalescing-operator": "npm:^7.8.3" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 10/949c9ddcdecdaec766ee610ef98f965f928ccc0361dd87cf9f88cf4896a6ccd62fce063d4494778e50da99dea63d270a1be574a62d6ab81cbe9d85884bf55a7d - languageName: node - linkType: hard - -"@babel/plugin-proposal-numeric-separator@npm:^7.18.6": - version: 7.18.6 - resolution: "@babel/plugin-proposal-numeric-separator@npm:7.18.6" - dependencies: - "@babel/helper-plugin-utils": "npm:^7.18.6" - "@babel/plugin-syntax-numeric-separator": "npm:^7.10.4" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 10/f370ea584c55bf4040e1f78c80b4eeb1ce2e6aaa74f87d1a48266493c33931d0b6222d8cee3a082383d6bb648ab8d6b7147a06f974d3296ef3bc39c7851683ec + checksum: 10/25f1d0a2ec6f9e912d2513b3830b239acdf9c75f453c208f77074687393f380b1150684ca0acb78368391fa1035242ac66e7f3856834d8003f01d1af17747230 languageName: node linkType: hard -"@babel/plugin-proposal-object-rest-spread@npm:^7.20.7": - version: 7.20.7 - resolution: "@babel/plugin-proposal-object-rest-spread@npm:7.20.7" +"@babel/plugin-bugfix-safari-class-field-initializer-scope@npm:^7.25.7": + version: 7.25.7 + resolution: "@babel/plugin-bugfix-safari-class-field-initializer-scope@npm:7.25.7" dependencies: - "@babel/compat-data": "npm:^7.20.5" - "@babel/helper-compilation-targets": "npm:^7.20.7" - "@babel/helper-plugin-utils": "npm:^7.20.2" - "@babel/plugin-syntax-object-rest-spread": "npm:^7.8.3" - "@babel/plugin-transform-parameters": "npm:^7.20.7" + "@babel/helper-plugin-utils": "npm:^7.25.7" peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 10/cb0f8f2ff98d7bb64ee91c28b20e8ab15d9bc7043f0932cbb9e51e1bbfb623b12f206a1171e070299c9cf21948c320b710d6d72a42f68a5bfd2702354113a1c5 + "@babel/core": ^7.0.0 + checksum: 10/52551470b6164a787460c28019428e97d20097d5dffab74f8866512706a3b002e57fdb23fe8e5156149bc4c9cfea48d5a0347b7a9e7e2a05f681941037136ab3 languageName: node linkType: hard -"@babel/plugin-proposal-optional-catch-binding@npm:^7.18.6": - version: 7.18.6 - resolution: "@babel/plugin-proposal-optional-catch-binding@npm:7.18.6" +"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:^7.25.7": + version: 7.25.7 + resolution: "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:7.25.7" dependencies: - "@babel/helper-plugin-utils": "npm:^7.18.6" - "@babel/plugin-syntax-optional-catch-binding": "npm:^7.8.3" + "@babel/helper-plugin-utils": "npm:^7.25.7" peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 10/7b5b39fb5d8d6d14faad6cb68ece5eeb2fd550fb66b5af7d7582402f974f5bc3684641f7c192a5a57e0f59acfae4aada6786be1eba030881ddc590666eff4d1e + "@babel/core": ^7.0.0 + checksum: 10/c37204ec3c82a1babba13f0cc2a68220959224cbab1294b1d7d8501af4734de1664b43c67b97fcaa1b3f53c865b0a4ad6f887102c7d7b913aab43df29ac7da52 languageName: node linkType: hard -"@babel/plugin-proposal-optional-chaining@npm:^7.21.0": - version: 7.21.0 - resolution: "@babel/plugin-proposal-optional-chaining@npm:7.21.0" +"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@npm:^7.25.7": + version: 7.25.7 + resolution: "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@npm:7.25.7" dependencies: - "@babel/helper-plugin-utils": "npm:^7.20.2" - "@babel/helper-skip-transparent-expression-wrappers": "npm:^7.20.0" - "@babel/plugin-syntax-optional-chaining": "npm:^7.8.3" + "@babel/helper-plugin-utils": "npm:^7.25.7" + "@babel/helper-skip-transparent-expression-wrappers": "npm:^7.25.7" + "@babel/plugin-transform-optional-chaining": "npm:^7.25.7" peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 10/522cd133aff5c94c0ef36ff83c64f03deee183815da68b65b6950e81972ace3b514e032df07ea76d0f9ec8cc7a49578092907adfa17fccb4612117557c04a882 + "@babel/core": ^7.13.0 + checksum: 10/507c92bbcb3d7747c82290370336b50368fbb652af31fea718be8f1928142f1c5f7c6f2b9810d8b9b2905734f8f6b778f9e4b1cfb5a11073a2f1cfe9e5e5b354 languageName: node linkType: hard -"@babel/plugin-proposal-private-methods@npm:^7.18.6": - version: 7.18.6 - resolution: "@babel/plugin-proposal-private-methods@npm:7.18.6" +"@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@npm:^7.25.7": + version: 7.25.7 + resolution: "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@npm:7.25.7" dependencies: - "@babel/helper-create-class-features-plugin": "npm:^7.18.6" - "@babel/helper-plugin-utils": "npm:^7.18.6" + "@babel/helper-plugin-utils": "npm:^7.25.7" + "@babel/traverse": "npm:^7.25.7" peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 10/22d8502ee96bca99ad2c8393e8493e2b8d4507576dd054490fd8201a36824373440106f5b098b6d821b026c7e72b0424ff4aeca69ed5f42e48f029d3a156d5ad + "@babel/core": ^7.0.0 + checksum: 10/289c6da5840c5049adbeb9b4cfb166d422f0b7b3f3a54aff64e8a053a1249e8eb513eac0fa3d033869372ffc30eda1e8555fa789b9daa1064bfdaf7f4717daa8 languageName: node linkType: hard @@ -1684,32 +1540,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-proposal-private-property-in-object@npm:^7.21.0": - version: 7.21.11 - resolution: "@babel/plugin-proposal-private-property-in-object@npm:7.21.11" - dependencies: - "@babel/helper-annotate-as-pure": "npm:^7.18.6" - "@babel/helper-create-class-features-plugin": "npm:^7.21.0" - "@babel/helper-plugin-utils": "npm:^7.20.2" - "@babel/plugin-syntax-private-property-in-object": "npm:^7.14.5" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 10/f803b5e1de0cb7c53f0d7f70bfbf57f2b3a20d95c19f8f2710719c4938149b490ee14d2d0c2f8316080823f0943c6cb8668fa8c139420e7bc7f80a66bfd50fff - languageName: node - linkType: hard - -"@babel/plugin-proposal-unicode-property-regex@npm:^7.18.6, @babel/plugin-proposal-unicode-property-regex@npm:^7.4.4": - version: 7.18.6 - resolution: "@babel/plugin-proposal-unicode-property-regex@npm:7.18.6" - dependencies: - "@babel/helper-create-regexp-features-plugin": "npm:^7.18.6" - "@babel/helper-plugin-utils": "npm:^7.18.6" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 10/a8575ecb7ff24bf6c6e94808d5c84bb5a0c6dd7892b54f09f4646711ba0ee1e1668032b3c43e3e1dfec2c5716c302e851ac756c1645e15882d73df6ad21ae951 - languageName: node - linkType: hard - "@babel/plugin-syntax-async-generators@npm:^7.8.4": version: 7.8.4 resolution: "@babel/plugin-syntax-async-generators@npm:7.8.4" @@ -1732,7 +1562,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-syntax-class-properties@npm:^7.12.13, @babel/plugin-syntax-class-properties@npm:^7.8.3": +"@babel/plugin-syntax-class-properties@npm:^7.8.3": version: 7.12.13 resolution: "@babel/plugin-syntax-class-properties@npm:7.12.13" dependencies: @@ -1743,62 +1573,29 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-syntax-class-static-block@npm:^7.14.5": - version: 7.14.5 - resolution: "@babel/plugin-syntax-class-static-block@npm:7.14.5" - dependencies: - "@babel/helper-plugin-utils": "npm:^7.14.5" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 10/3e80814b5b6d4fe17826093918680a351c2d34398a914ce6e55d8083d72a9bdde4fbaf6a2dcea0e23a03de26dc2917ae3efd603d27099e2b98380345703bf948 - languageName: node - linkType: hard - -"@babel/plugin-syntax-dynamic-import@npm:^7.8.3": - version: 7.8.3 - resolution: "@babel/plugin-syntax-dynamic-import@npm:7.8.3" - dependencies: - "@babel/helper-plugin-utils": "npm:^7.8.0" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 10/ce307af83cf433d4ec42932329fad25fa73138ab39c7436882ea28742e1c0066626d224e0ad2988724c82644e41601cef607b36194f695cb78a1fcdc959637bd - languageName: node - linkType: hard - -"@babel/plugin-syntax-export-namespace-from@npm:^7.8.3": - version: 7.8.3 - resolution: "@babel/plugin-syntax-export-namespace-from@npm:7.8.3" - dependencies: - "@babel/helper-plugin-utils": "npm:^7.8.3" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 10/85740478be5b0de185228e7814451d74ab8ce0a26fcca7613955262a26e99e8e15e9da58f60c754b84515d4c679b590dbd3f2148f0f58025f4ae706f1c5a5d4a - languageName: node - linkType: hard - -"@babel/plugin-syntax-import-assertions@npm:^7.20.0, @babel/plugin-syntax-import-assertions@npm:^7.22.5": - version: 7.23.3 - resolution: "@babel/plugin-syntax-import-assertions@npm:7.23.3" +"@babel/plugin-syntax-import-assertions@npm:^7.25.7": + version: 7.25.7 + resolution: "@babel/plugin-syntax-import-assertions@npm:7.25.7" dependencies: - "@babel/helper-plugin-utils": "npm:^7.22.5" + "@babel/helper-plugin-utils": "npm:^7.25.7" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10/883e6b35b2da205138caab832d54505271a3fee3fc1e8dc0894502434fc2b5d517cbe93bbfbfef8068a0fb6ec48ebc9eef3f605200a489065ba43d8cddc1c9a7 + checksum: 10/d72615f8dcc5ffbcb456bcf7ce27bc22b30cc9ea8d809e461d80af486033d31bd0b6d83c9a7997c9cd36ff283a9c1207f806da4361bb620370659c256c5454e9 languageName: node linkType: hard -"@babel/plugin-syntax-import-attributes@npm:^7.22.5": - version: 7.22.5 - resolution: "@babel/plugin-syntax-import-attributes@npm:7.22.5" +"@babel/plugin-syntax-import-attributes@npm:^7.25.7": + version: 7.25.7 + resolution: "@babel/plugin-syntax-import-attributes@npm:7.25.7" dependencies: - "@babel/helper-plugin-utils": "npm:^7.22.5" + "@babel/helper-plugin-utils": "npm:^7.25.7" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10/197b3c5ea2a9649347f033342cb222ab47f4645633695205c0250c6bf2af29e643753b8bb24a2db39948bef08e7c540babfd365591eb57fc110cb30b425ffc47 + checksum: 10/7c5451e2d8351693acbc53b1e1f6951026e35899d22847a6d22424a1ee5c92c11ac6c6f209a9e18f85d7bb9267caaf2532653e892997cdcd51784106a5858b7e languageName: node linkType: hard -"@babel/plugin-syntax-import-meta@npm:^7.10.4, @babel/plugin-syntax-import-meta@npm:^7.8.3": +"@babel/plugin-syntax-import-meta@npm:^7.8.3": version: 7.10.4 resolution: "@babel/plugin-syntax-import-meta@npm:7.10.4" dependencies: @@ -1820,7 +1617,18 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-syntax-jsx@npm:^7.22.5, @babel/plugin-syntax-jsx@npm:^7.7.2": +"@babel/plugin-syntax-jsx@npm:^7.25.7": + version: 7.25.7 + resolution: "@babel/plugin-syntax-jsx@npm:7.25.7" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.25.7" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10/243476a943a84b6b86e99076301e66f48268e8799564053e8feccab90da7944a0b42c91360216dbfb0b2958bbd0ed100d2c7b2db688dab83d19ff2745d4892eb + languageName: node + linkType: hard + +"@babel/plugin-syntax-jsx@npm:^7.7.2": version: 7.22.5 resolution: "@babel/plugin-syntax-jsx@npm:7.22.5" dependencies: @@ -1831,7 +1639,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-syntax-logical-assignment-operators@npm:^7.10.4, @babel/plugin-syntax-logical-assignment-operators@npm:^7.8.3": +"@babel/plugin-syntax-logical-assignment-operators@npm:^7.8.3": version: 7.10.4 resolution: "@babel/plugin-syntax-logical-assignment-operators@npm:7.10.4" dependencies: @@ -1853,7 +1661,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-syntax-numeric-separator@npm:^7.10.4, @babel/plugin-syntax-numeric-separator@npm:^7.8.3": +"@babel/plugin-syntax-numeric-separator@npm:^7.8.3": version: 7.10.4 resolution: "@babel/plugin-syntax-numeric-separator@npm:7.10.4" dependencies: @@ -1897,29 +1705,29 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-syntax-private-property-in-object@npm:^7.14.5": +"@babel/plugin-syntax-top-level-await@npm:^7.8.3": version: 7.14.5 - resolution: "@babel/plugin-syntax-private-property-in-object@npm:7.14.5" + resolution: "@babel/plugin-syntax-top-level-await@npm:7.14.5" dependencies: "@babel/helper-plugin-utils": "npm:^7.14.5" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10/b317174783e6e96029b743ccff2a67d63d38756876e7e5d0ba53a322e38d9ca452c13354a57de1ad476b4c066dbae699e0ca157441da611117a47af88985ecda + checksum: 10/bbd1a56b095be7820029b209677b194db9b1d26691fe999856462e66b25b281f031f3dfd91b1619e9dcf95bebe336211833b854d0fb8780d618e35667c2d0d7e languageName: node linkType: hard -"@babel/plugin-syntax-top-level-await@npm:^7.14.5, @babel/plugin-syntax-top-level-await@npm:^7.8.3": - version: 7.14.5 - resolution: "@babel/plugin-syntax-top-level-await@npm:7.14.5" +"@babel/plugin-syntax-typescript@npm:^7.25.7": + version: 7.25.7 + resolution: "@babel/plugin-syntax-typescript@npm:7.25.7" dependencies: - "@babel/helper-plugin-utils": "npm:^7.14.5" + "@babel/helper-plugin-utils": "npm:^7.25.7" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10/bbd1a56b095be7820029b209677b194db9b1d26691fe999856462e66b25b281f031f3dfd91b1619e9dcf95bebe336211833b854d0fb8780d618e35667c2d0d7e + checksum: 10/f1492336230920cc4daa6e7aa3571253fb0c0fd05a1d0a7b5dc0a5b907f31945235ee8bf09c83f7738b89943a2320a61dda95e0db2b6310b07040aeda6be4f44 languageName: node linkType: hard -"@babel/plugin-syntax-typescript@npm:^7.22.5, @babel/plugin-syntax-typescript@npm:^7.7.2": +"@babel/plugin-syntax-typescript@npm:^7.7.2": version: 7.22.5 resolution: "@babel/plugin-syntax-typescript@npm:7.22.5" dependencies: @@ -1942,479 +1750,476 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-arrow-functions@npm:^7.21.5, @babel/plugin-transform-arrow-functions@npm:^7.22.5": - version: 7.23.3 - resolution: "@babel/plugin-transform-arrow-functions@npm:7.23.3" +"@babel/plugin-transform-arrow-functions@npm:^7.25.7": + version: 7.25.7 + resolution: "@babel/plugin-transform-arrow-functions@npm:7.25.7" dependencies: - "@babel/helper-plugin-utils": "npm:^7.22.5" + "@babel/helper-plugin-utils": "npm:^7.25.7" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10/1e99118176e5366c2636064d09477016ab5272b2a92e78b8edb571d20bc3eaa881789a905b20042942c3c2d04efc530726cf703f937226db5ebc495f5d067e66 + checksum: 10/45a6b05acd132bd399ab127d54d43f7117f650908092c15da7c41c61c5e49bfdb63c0e65bd59ad68c94bfc6aade602732a98a55b146b69dfae212516203d43f9 languageName: node linkType: hard -"@babel/plugin-transform-async-generator-functions@npm:^7.22.15": - version: 7.22.15 - resolution: "@babel/plugin-transform-async-generator-functions@npm:7.22.15" +"@babel/plugin-transform-async-generator-functions@npm:^7.25.8": + version: 7.25.8 + resolution: "@babel/plugin-transform-async-generator-functions@npm:7.25.8" dependencies: - "@babel/helper-environment-visitor": "npm:^7.22.5" - "@babel/helper-plugin-utils": "npm:^7.22.5" - "@babel/helper-remap-async-to-generator": "npm:^7.22.9" - "@babel/plugin-syntax-async-generators": "npm:^7.8.4" + "@babel/helper-plugin-utils": "npm:^7.25.7" + "@babel/helper-remap-async-to-generator": "npm:^7.25.7" + "@babel/traverse": "npm:^7.25.7" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10/fad98786b446ce63bde0d14a221e2617eef5a7bbca62b49d96f16ab5e1694521234cfba6145b830fbf9af16d60a8a3dbf148e8694830bd91796fe333b0599e73 + checksum: 10/ab3f74664fc03af357e8450711de60ec77149be668059dbc0c0d616d85253117aec0e5ffb2eccda3449d0099d5fba5ef32f0e6e12a52af5f72fbca437372ece5 languageName: node linkType: hard -"@babel/plugin-transform-async-to-generator@npm:^7.20.7, @babel/plugin-transform-async-to-generator@npm:^7.22.5": - version: 7.23.3 - resolution: "@babel/plugin-transform-async-to-generator@npm:7.23.3" +"@babel/plugin-transform-async-to-generator@npm:^7.25.7": + version: 7.25.7 + resolution: "@babel/plugin-transform-async-to-generator@npm:7.25.7" dependencies: - "@babel/helper-module-imports": "npm:^7.22.15" - "@babel/helper-plugin-utils": "npm:^7.22.5" - "@babel/helper-remap-async-to-generator": "npm:^7.22.20" + "@babel/helper-module-imports": "npm:^7.25.7" + "@babel/helper-plugin-utils": "npm:^7.25.7" + "@babel/helper-remap-async-to-generator": "npm:^7.25.7" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10/2e9d9795d4b3b3d8090332104e37061c677f29a1ce65bcbda4099a32d243e5d9520270a44bbabf0fb1fb40d463bd937685b1a1042e646979086c546d55319c3c + checksum: 10/fdc6161e9027bec8bbe523e40934a2cccf1a30cf241006c98a120b2cda6e4f75d4a4cb4831cf3ece43d9b752183117e4ca5ec43778750146d5fc9a74b22b1acf languageName: node linkType: hard -"@babel/plugin-transform-block-scoped-functions@npm:^7.18.6, @babel/plugin-transform-block-scoped-functions@npm:^7.22.5": - version: 7.23.3 - resolution: "@babel/plugin-transform-block-scoped-functions@npm:7.23.3" +"@babel/plugin-transform-block-scoped-functions@npm:^7.25.7": + version: 7.25.7 + resolution: "@babel/plugin-transform-block-scoped-functions@npm:7.25.7" dependencies: - "@babel/helper-plugin-utils": "npm:^7.22.5" + "@babel/helper-plugin-utils": "npm:^7.25.7" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10/e63b16d94ee5f4d917e669da3db5ea53d1e7e79141a2ec873c1e644678cdafe98daa556d0d359963c827863d6b3665d23d4938a94a4c5053a1619c4ebd01d020 + checksum: 10/334debb143d002295c6dd5559ebf24483557787621fd1d7283ac748eb401ed96b7d43c981f1d2b6795720979fe7872dd0719aed890d064244d52b1c4fe6f3347 languageName: node linkType: hard -"@babel/plugin-transform-block-scoping@npm:^7.21.0, @babel/plugin-transform-block-scoping@npm:^7.22.15": - version: 7.23.4 - resolution: "@babel/plugin-transform-block-scoping@npm:7.23.4" +"@babel/plugin-transform-block-scoping@npm:^7.25.7": + version: 7.25.7 + resolution: "@babel/plugin-transform-block-scoping@npm:7.25.7" dependencies: - "@babel/helper-plugin-utils": "npm:^7.22.5" + "@babel/helper-plugin-utils": "npm:^7.25.7" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10/bbb965a3acdfb03559806d149efbd194ac9c983b260581a60efcb15eb9fbe20e3054667970800146d867446db1c1398f8e4ee87f4454233e49b8f8ce947bd99b + checksum: 10/bbc5b815c6850eb798a294a5d31ed09bb0db367a31196e78c0d02ce3f845ddd2e0dcfd7ec70505dfa4e1bd67f13e46b315d290c58aa7531468feed380e267d97 languageName: node linkType: hard -"@babel/plugin-transform-class-properties@npm:^7.22.5": - version: 7.22.5 - resolution: "@babel/plugin-transform-class-properties@npm:7.22.5" +"@babel/plugin-transform-class-properties@npm:^7.25.7": + version: 7.25.7 + resolution: "@babel/plugin-transform-class-properties@npm:7.25.7" dependencies: - "@babel/helper-create-class-features-plugin": "npm:^7.22.5" - "@babel/helper-plugin-utils": "npm:^7.22.5" + "@babel/helper-create-class-features-plugin": "npm:^7.25.7" + "@babel/helper-plugin-utils": "npm:^7.25.7" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10/b830152dfc2ff2f647f0abe76e6251babdfbef54d18c4b2c73a6bf76b1a00050a5d998dac80dc901a48514e95604324943a9dd39317073fe0928b559e0e0c579 + checksum: 10/fe1dbbd77275ade96964fec0c85a1f14e2dac0c6565bccddf00680e43c2e906d289dd9d483aff6359420cef2a044b4aaaeb303f64a3a1a005601c018188368e7 languageName: node linkType: hard -"@babel/plugin-transform-class-static-block@npm:^7.22.11": - version: 7.22.11 - resolution: "@babel/plugin-transform-class-static-block@npm:7.22.11" +"@babel/plugin-transform-class-static-block@npm:^7.25.8": + version: 7.25.8 + resolution: "@babel/plugin-transform-class-static-block@npm:7.25.8" dependencies: - "@babel/helper-create-class-features-plugin": "npm:^7.22.11" - "@babel/helper-plugin-utils": "npm:^7.22.5" - "@babel/plugin-syntax-class-static-block": "npm:^7.14.5" + "@babel/helper-create-class-features-plugin": "npm:^7.25.7" + "@babel/helper-plugin-utils": "npm:^7.25.7" peerDependencies: "@babel/core": ^7.12.0 - checksum: 10/69f040506fad66f1c6918d288d0e0edbc5c8a07c8b4462c1184ad2f9f08995d68b057126c213871c0853ae0c72afc60ec87492049dfacb20902e32346a448bcb + checksum: 10/160d5f9d1dbe4dc12c2998227b51b1ccfe9f4d11b1031d0698f34403961d5b9bb995cc86acf1855102b9be365370c97d8cea243802b73c7ba7b2b18b2ac3aae9 languageName: node linkType: hard -"@babel/plugin-transform-classes@npm:^7.21.0, @babel/plugin-transform-classes@npm:^7.22.15": - version: 7.23.5 - resolution: "@babel/plugin-transform-classes@npm:7.23.5" +"@babel/plugin-transform-classes@npm:^7.25.7": + version: 7.25.7 + resolution: "@babel/plugin-transform-classes@npm:7.25.7" dependencies: - "@babel/helper-annotate-as-pure": "npm:^7.22.5" - "@babel/helper-compilation-targets": "npm:^7.22.15" - "@babel/helper-environment-visitor": "npm:^7.22.20" - "@babel/helper-function-name": "npm:^7.23.0" - "@babel/helper-optimise-call-expression": "npm:^7.22.5" - "@babel/helper-plugin-utils": "npm:^7.22.5" - "@babel/helper-replace-supers": "npm:^7.22.20" - "@babel/helper-split-export-declaration": "npm:^7.22.6" + "@babel/helper-annotate-as-pure": "npm:^7.25.7" + "@babel/helper-compilation-targets": "npm:^7.25.7" + "@babel/helper-plugin-utils": "npm:^7.25.7" + "@babel/helper-replace-supers": "npm:^7.25.7" + "@babel/traverse": "npm:^7.25.7" globals: "npm:^11.1.0" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10/f6c4fed2f48bdd46a4726b829ea2ddb5c9c97edd0e55dc53791d82927daad5725052b7e785a8b7e90a53b0606166b9c554469dc94f10fba59ca9642e997d97ee + checksum: 10/239926ceb7fa926054fe9aabb7a64dba090d8f83d075bcec804d602a5715501c56dc26367bb90e6780e1113cc04cf6ad32c131e2782ccf1768fd059ac7eba04b languageName: node linkType: hard -"@babel/plugin-transform-computed-properties@npm:^7.21.5, @babel/plugin-transform-computed-properties@npm:^7.22.5": - version: 7.23.3 - resolution: "@babel/plugin-transform-computed-properties@npm:7.23.3" +"@babel/plugin-transform-computed-properties@npm:^7.25.7": + version: 7.25.7 + resolution: "@babel/plugin-transform-computed-properties@npm:7.25.7" dependencies: - "@babel/helper-plugin-utils": "npm:^7.22.5" - "@babel/template": "npm:^7.22.15" + "@babel/helper-plugin-utils": "npm:^7.25.7" + "@babel/template": "npm:^7.25.7" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10/e75593e02c5ea473c17839e3c9d597ce3697bf039b66afe9a4d06d086a87fb3d95850b4174476897afc351dc1b46a9ec3165ee6e8fbad3732c0d65f676f855ad + checksum: 10/f25caeb3366847a1f67efe4b250a460f88a5ebb4c12c566d945bf211ef28977dd21f4dd6539f63743f3fabdbb174b4d34e22af2a451aba3bcfd702396442eb53 languageName: node linkType: hard -"@babel/plugin-transform-destructuring@npm:^7.21.3, @babel/plugin-transform-destructuring@npm:^7.22.15": - version: 7.23.3 - resolution: "@babel/plugin-transform-destructuring@npm:7.23.3" +"@babel/plugin-transform-destructuring@npm:^7.25.7": + version: 7.25.7 + resolution: "@babel/plugin-transform-destructuring@npm:7.25.7" dependencies: - "@babel/helper-plugin-utils": "npm:^7.22.5" + "@babel/helper-plugin-utils": "npm:^7.25.7" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10/5abd93718af5a61f8f6a97d2ccac9139499752dd5b2c533d7556fb02947ae01b2f51d4c4f5e64df569e8783d3743270018eb1fa979c43edec7dd1377acf107ed + checksum: 10/b58347dc1b807ef8e6aaf995d59c6f09aa9de2c590bb90a52bc9c4082836ef72f70f8fc062370138134220de40dad06af6122ffcce77fb97d5e77ca7cd71e5c7 languageName: node linkType: hard -"@babel/plugin-transform-dotall-regex@npm:^7.18.6, @babel/plugin-transform-dotall-regex@npm:^7.22.5, @babel/plugin-transform-dotall-regex@npm:^7.4.4": - version: 7.23.3 - resolution: "@babel/plugin-transform-dotall-regex@npm:7.23.3" +"@babel/plugin-transform-dotall-regex@npm:^7.25.7": + version: 7.25.7 + resolution: "@babel/plugin-transform-dotall-regex@npm:7.25.7" dependencies: - "@babel/helper-create-regexp-features-plugin": "npm:^7.22.15" - "@babel/helper-plugin-utils": "npm:^7.22.5" + "@babel/helper-create-regexp-features-plugin": "npm:^7.25.7" + "@babel/helper-plugin-utils": "npm:^7.25.7" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10/a2dbbf7f1ea16a97948c37df925cb364337668c41a3948b8d91453f140507bd8a3429030c7ce66d09c299987b27746c19a2dd18b6f17dcb474854b14fd9159a3 + checksum: 10/a4727ee33b95d1f7e33b277b0003e3e91ebb9c3c611512e1ca5f3f0d99efd552a6c42b09e5520ea686ef0389dd8159a77c7c59fb53d0d1a1ff7d385c362da71b languageName: node linkType: hard -"@babel/plugin-transform-duplicate-keys@npm:^7.18.9, @babel/plugin-transform-duplicate-keys@npm:^7.22.5": - version: 7.23.3 - resolution: "@babel/plugin-transform-duplicate-keys@npm:7.23.3" +"@babel/plugin-transform-duplicate-keys@npm:^7.25.7": + version: 7.25.7 + resolution: "@babel/plugin-transform-duplicate-keys@npm:7.25.7" dependencies: - "@babel/helper-plugin-utils": "npm:^7.22.5" + "@babel/helper-plugin-utils": "npm:^7.25.7" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10/c2a21c34dc0839590cd945192cbc46fde541a27e140c48fe1808315934664cdbf18db64889e23c4eeb6bad9d3e049482efdca91d29de5734ffc887c4fbabaa16 + checksum: 10/b132ce919643f1fa519c8597513fba77155fde2d7689154c73791847efd218ff7ce11694b539ca9dee65538c9e774adf4bd6a6e950800dd648f43d5906a38155 languageName: node linkType: hard -"@babel/plugin-transform-dynamic-import@npm:^7.22.11": - version: 7.22.11 - resolution: "@babel/plugin-transform-dynamic-import@npm:7.22.11" +"@babel/plugin-transform-duplicate-named-capturing-groups-regex@npm:^7.25.7": + version: 7.25.7 + resolution: "@babel/plugin-transform-duplicate-named-capturing-groups-regex@npm:7.25.7" dependencies: - "@babel/helper-plugin-utils": "npm:^7.22.5" - "@babel/plugin-syntax-dynamic-import": "npm:^7.8.3" + "@babel/helper-create-regexp-features-plugin": "npm:^7.25.7" + "@babel/helper-plugin-utils": "npm:^7.25.7" + peerDependencies: + "@babel/core": ^7.0.0 + checksum: 10/688ab66ed249a08d4b2e3ae8a2c10678fbe23f6466d5020d4cc3e031946dc335c028f5a1bee3221acb3875a1e901b0237020463157690fabc06edc4bdd6c6c88 + languageName: node + linkType: hard + +"@babel/plugin-transform-dynamic-import@npm:^7.25.8": + version: 7.25.8 + resolution: "@babel/plugin-transform-dynamic-import@npm:7.25.8" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.25.7" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10/78fc9c532210bf9e8f231747f542318568ac360ee6c27e80853962c984283c73da3f8f8aebe83c2096090a435b356b092ed85de617a156cbe0729d847632be45 + checksum: 10/cf2c105143461876f418d21893ac8f7f2b0a3c3cefb4374c3cd6338a19d3a0deed3565049f7436b94452c6471622958ef9248c7bdfeb34d2917710ac74431203 languageName: node linkType: hard -"@babel/plugin-transform-exponentiation-operator@npm:^7.18.6, @babel/plugin-transform-exponentiation-operator@npm:^7.22.5": - version: 7.23.3 - resolution: "@babel/plugin-transform-exponentiation-operator@npm:7.23.3" +"@babel/plugin-transform-exponentiation-operator@npm:^7.25.7": + version: 7.25.7 + resolution: "@babel/plugin-transform-exponentiation-operator@npm:7.25.7" dependencies: - "@babel/helper-builder-binary-assignment-operator-visitor": "npm:^7.22.15" - "@babel/helper-plugin-utils": "npm:^7.22.5" + "@babel/helper-builder-binary-assignment-operator-visitor": "npm:^7.25.7" + "@babel/helper-plugin-utils": "npm:^7.25.7" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10/00d05ab14ad0f299160fcf9d8f55a1cc1b740e012ab0b5ce30207d2365f091665115557af7d989cd6260d075a252d9e4283de5f2b247dfbbe0e42ae586e6bf66 + checksum: 10/3371fc79c052a3c63652785284a9f4b943a188ae5aa3e68a760c45afc43739d654ad6d8d24b93ed04fe736f6c0b4a7a11ace56bc954d3a6520d0b3c79e058c03 languageName: node linkType: hard -"@babel/plugin-transform-export-namespace-from@npm:^7.22.11": - version: 7.22.11 - resolution: "@babel/plugin-transform-export-namespace-from@npm:7.22.11" +"@babel/plugin-transform-export-namespace-from@npm:^7.25.8": + version: 7.25.8 + resolution: "@babel/plugin-transform-export-namespace-from@npm:7.25.8" dependencies: - "@babel/helper-plugin-utils": "npm:^7.22.5" - "@babel/plugin-syntax-export-namespace-from": "npm:^7.8.3" + "@babel/helper-plugin-utils": "npm:^7.25.7" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10/73af5883a321ed56a4bfd43c8a7de0164faebe619287706896fc6ee2f7a4e69042adaa1338c0b8b4bdb9f7e5fdceb016fb1d40694cb43ca3b8827429e8aac4bf + checksum: 10/439aac4ca1c7dbb63f021142e7abcd746049bf0d44cc5d2eb469ae3b75d90e076a43ff77190b74d8139402b53eea625b08c68651d3ce1d0a0915f5643450b3de languageName: node linkType: hard -"@babel/plugin-transform-for-of@npm:^7.21.5, @babel/plugin-transform-for-of@npm:^7.22.15": - version: 7.23.3 - resolution: "@babel/plugin-transform-for-of@npm:7.23.3" +"@babel/plugin-transform-for-of@npm:^7.25.7": + version: 7.25.7 + resolution: "@babel/plugin-transform-for-of@npm:7.25.7" dependencies: - "@babel/helper-plugin-utils": "npm:^7.22.5" + "@babel/helper-plugin-utils": "npm:^7.25.7" + "@babel/helper-skip-transparent-expression-wrappers": "npm:^7.25.7" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10/745054f125fba6dbaea3d863352c94266c97db87e3521bc6c436a8c05f384821907c0109ace437a90342e423a3365f4d8e592de06e4a241bbd7070e1f293604f + checksum: 10/6fdfc1747283f50ada9f08d4f801d2156658f183db731369ac2b17f5f885661114906b3645c6a38bb6a5e24b771e6bd43c0ea47580c4fcb9347cd1d179e57435 languageName: node linkType: hard -"@babel/plugin-transform-function-name@npm:^7.18.9, @babel/plugin-transform-function-name@npm:^7.22.5": - version: 7.23.3 - resolution: "@babel/plugin-transform-function-name@npm:7.23.3" +"@babel/plugin-transform-function-name@npm:^7.25.7": + version: 7.25.7 + resolution: "@babel/plugin-transform-function-name@npm:7.25.7" dependencies: - "@babel/helper-compilation-targets": "npm:^7.22.15" - "@babel/helper-function-name": "npm:^7.23.0" - "@babel/helper-plugin-utils": "npm:^7.22.5" + "@babel/helper-compilation-targets": "npm:^7.25.7" + "@babel/helper-plugin-utils": "npm:^7.25.7" + "@babel/traverse": "npm:^7.25.7" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10/355c6dbe07c919575ad42b2f7e020f320866d72f8b79181a16f8e0cd424a2c761d979f03f47d583d9471b55dcd68a8a9d829b58e1eebcd572145b934b48975a6 + checksum: 10/465d54942c03f77da3be5fb56404c6f8162f0e99034b8aceab6af2d386a77ecaf3df0c5f2dd67a00b66cd8ad970c0a08151026ed14aa44673a33f495e6849cc7 languageName: node linkType: hard -"@babel/plugin-transform-json-strings@npm:^7.22.11": - version: 7.22.11 - resolution: "@babel/plugin-transform-json-strings@npm:7.22.11" +"@babel/plugin-transform-json-strings@npm:^7.25.8": + version: 7.25.8 + resolution: "@babel/plugin-transform-json-strings@npm:7.25.8" dependencies: - "@babel/helper-plugin-utils": "npm:^7.22.5" - "@babel/plugin-syntax-json-strings": "npm:^7.8.3" + "@babel/helper-plugin-utils": "npm:^7.25.7" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10/50665e5979e66358c50e90a26db53c55917f78175127ac2fa05c7888d156d418ffb930ec0a109353db0a7c5f57c756ce01bfc9825d24cbfd2b3ec453f2ed8cba + checksum: 10/adbc6a5a77b96db0f7e168c5fd2e56941df649808ce960f12447c1ba5d3893e9d458e7e14e3a5bd725ac5f3432ac1b3cf62b7413bbf7168a7c656dce51db711a languageName: node linkType: hard -"@babel/plugin-transform-literals@npm:^7.18.9, @babel/plugin-transform-literals@npm:^7.22.5": - version: 7.23.3 - resolution: "@babel/plugin-transform-literals@npm:7.23.3" +"@babel/plugin-transform-literals@npm:^7.25.7": + version: 7.25.7 + resolution: "@babel/plugin-transform-literals@npm:7.25.7" dependencies: - "@babel/helper-plugin-utils": "npm:^7.22.5" + "@babel/helper-plugin-utils": "npm:^7.25.7" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10/519a544cd58586b9001c4c9b18da25a62f17d23c48600ff7a685d75ca9eb18d2c5e8f5476f067f0a8f1fea2a31107eff950b9864833061e6076dcc4bdc3e71ed + checksum: 10/435d9709204e4cae46f9e75973a1424b98bb71516d9cfb0619260cfb56d445b43fa34aa49dacb0e1fbc2a19fdd9373f4b4db4908007be8f9e9e3f0ccf6c73e71 languageName: node linkType: hard -"@babel/plugin-transform-logical-assignment-operators@npm:^7.22.11": - version: 7.22.11 - resolution: "@babel/plugin-transform-logical-assignment-operators@npm:7.22.11" +"@babel/plugin-transform-logical-assignment-operators@npm:^7.25.8": + version: 7.25.8 + resolution: "@babel/plugin-transform-logical-assignment-operators@npm:7.25.8" dependencies: - "@babel/helper-plugin-utils": "npm:^7.22.5" - "@babel/plugin-syntax-logical-assignment-operators": "npm:^7.10.4" + "@babel/helper-plugin-utils": "npm:^7.25.7" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10/c664e9798e85afa7f92f07b867682dee7392046181d82f5d21bae6f2ca26dfe9c8375cdc52b7483c3fc09a983c1989f60eff9fbc4f373b0c0a74090553d05739 + checksum: 10/7af0e4ad63c1a59f24894b64330040966204963b75287752a2d56703c7924d3a883a3c2497e1f03c4b1792f8664e0650cf6687010dc5483444c077de1daae9f5 languageName: node linkType: hard -"@babel/plugin-transform-member-expression-literals@npm:^7.18.6, @babel/plugin-transform-member-expression-literals@npm:^7.22.5": - version: 7.23.3 - resolution: "@babel/plugin-transform-member-expression-literals@npm:7.23.3" +"@babel/plugin-transform-member-expression-literals@npm:^7.25.7": + version: 7.25.7 + resolution: "@babel/plugin-transform-member-expression-literals@npm:7.25.7" dependencies: - "@babel/helper-plugin-utils": "npm:^7.22.5" + "@babel/helper-plugin-utils": "npm:^7.25.7" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10/95cec13c36d447c5aa6b8e4c778b897eeba66dcb675edef01e0d2afcec9e8cb9726baf4f81b4bbae7a782595aed72e6a0d44ffb773272c3ca180fada99bf92db + checksum: 10/fb2b985cfa0436bfbed6fbcdd430573272518cf3454c9b0de374cfb80ac6fe60b2ebbe0818a83035e436a9ff08b159bb87527dfd712560c52a0ebfabe6f65121 languageName: node linkType: hard -"@babel/plugin-transform-modules-amd@npm:^7.20.11, @babel/plugin-transform-modules-amd@npm:^7.22.5": - version: 7.23.3 - resolution: "@babel/plugin-transform-modules-amd@npm:7.23.3" +"@babel/plugin-transform-modules-amd@npm:^7.25.7": + version: 7.25.7 + resolution: "@babel/plugin-transform-modules-amd@npm:7.25.7" dependencies: - "@babel/helper-module-transforms": "npm:^7.23.3" - "@babel/helper-plugin-utils": "npm:^7.22.5" + "@babel/helper-module-transforms": "npm:^7.25.7" + "@babel/helper-plugin-utils": "npm:^7.25.7" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10/48c87dee2c7dae8ed40d16901f32c9e58be4ef87bf2c3985b51dd2e78e82081f3bad0a39ee5cf6e8909e13e954e2b4bedef0a8141922f281ed833ddb59ed9be2 + checksum: 10/0d061c91130433fccc723b4eb1359ced515a5dd7196c3ec75f2b2c24613154365ec1c89fe89bee648c1dc28a54c9625dd2b21b6196659a9f2b8ebff0b2352f6c languageName: node linkType: hard -"@babel/plugin-transform-modules-commonjs@npm:^7.21.5, @babel/plugin-transform-modules-commonjs@npm:^7.22.15": - version: 7.23.3 - resolution: "@babel/plugin-transform-modules-commonjs@npm:7.23.3" +"@babel/plugin-transform-modules-commonjs@npm:^7.25.7": + version: 7.25.7 + resolution: "@babel/plugin-transform-modules-commonjs@npm:7.25.7" dependencies: - "@babel/helper-module-transforms": "npm:^7.23.3" - "@babel/helper-plugin-utils": "npm:^7.22.5" - "@babel/helper-simple-access": "npm:^7.22.5" + "@babel/helper-module-transforms": "npm:^7.25.7" + "@babel/helper-plugin-utils": "npm:^7.25.7" + "@babel/helper-simple-access": "npm:^7.25.7" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10/a3bc082d0dfe8327a29263a6d721cea608d440bc8141ba3ec6ba80ad73d84e4f9bbe903c27e9291c29878feec9b5dee2bd0563822f93dc951f5d7fc36bdfe85b + checksum: 10/4b3d038b55bfe5553e9eea360cc1b3dd689068256a9bce1939061ab1dfa194fea0b7b54f10c53b0af0be44508fd0037022c32709a6d96ac1277fb9c7de0f510c languageName: node linkType: hard -"@babel/plugin-transform-modules-systemjs@npm:^7.20.11, @babel/plugin-transform-modules-systemjs@npm:^7.22.11": - version: 7.23.3 - resolution: "@babel/plugin-transform-modules-systemjs@npm:7.23.3" +"@babel/plugin-transform-modules-systemjs@npm:^7.25.7": + version: 7.25.7 + resolution: "@babel/plugin-transform-modules-systemjs@npm:7.25.7" dependencies: - "@babel/helper-hoist-variables": "npm:^7.22.5" - "@babel/helper-module-transforms": "npm:^7.23.3" - "@babel/helper-plugin-utils": "npm:^7.22.5" - "@babel/helper-validator-identifier": "npm:^7.22.20" + "@babel/helper-module-transforms": "npm:^7.25.7" + "@babel/helper-plugin-utils": "npm:^7.25.7" + "@babel/helper-validator-identifier": "npm:^7.25.7" + "@babel/traverse": "npm:^7.25.7" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10/051112de7585fff4ffd67865066401f01f90745d41f26b0edbeec0981342c10517ce1a6b4d7051b583a3e513088eece6a3f57b1663f1dd9418071cd05f14fef9 + checksum: 10/abd3522e60a9b639f8ad58b2ee237debe9e78a3a1462e3c5b17b4fbdc1b4bb2235edb1ed7d204b45701ec99dd3506d87164ece8ac9d9465a3e603cf13170b65b languageName: node linkType: hard -"@babel/plugin-transform-modules-umd@npm:^7.18.6, @babel/plugin-transform-modules-umd@npm:^7.22.5": - version: 7.23.3 - resolution: "@babel/plugin-transform-modules-umd@npm:7.23.3" +"@babel/plugin-transform-modules-umd@npm:^7.25.7": + version: 7.25.7 + resolution: "@babel/plugin-transform-modules-umd@npm:7.25.7" dependencies: - "@babel/helper-module-transforms": "npm:^7.23.3" - "@babel/helper-plugin-utils": "npm:^7.22.5" + "@babel/helper-module-transforms": "npm:^7.25.7" + "@babel/helper-plugin-utils": "npm:^7.25.7" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10/e3f3af83562d687899555c7826b3faf0ab93ee7976898995b1d20cbe7f4451c55e05b0e17bfb3e549937cbe7573daf5400b752912a241b0a8a64d2457c7626e5 + checksum: 10/06d6e95a9948aa91b218ada2940b8f568f78991265f2923f6e69c29e97ef1731c1b79adaf72a072a834a86f98fc0bd0117dfb14a37aaea6337fb4468f757471a languageName: node linkType: hard -"@babel/plugin-transform-named-capturing-groups-regex@npm:^7.20.5, @babel/plugin-transform-named-capturing-groups-regex@npm:^7.22.5": - version: 7.22.5 - resolution: "@babel/plugin-transform-named-capturing-groups-regex@npm:7.22.5" +"@babel/plugin-transform-named-capturing-groups-regex@npm:^7.25.7": + version: 7.25.7 + resolution: "@babel/plugin-transform-named-capturing-groups-regex@npm:7.25.7" dependencies: - "@babel/helper-create-regexp-features-plugin": "npm:^7.22.5" - "@babel/helper-plugin-utils": "npm:^7.22.5" + "@babel/helper-create-regexp-features-plugin": "npm:^7.25.7" + "@babel/helper-plugin-utils": "npm:^7.25.7" peerDependencies: "@babel/core": ^7.0.0 - checksum: 10/3ee564ddee620c035b928fdc942c5d17e9c4b98329b76f9cefac65c111135d925eb94ed324064cd7556d4f5123beec79abea1d4b97d1c8a2a5c748887a2eb623 + checksum: 10/4c8340cacb1d21794777abb68db2ea434a89274e9ca539e6f564488f5e8a7f517fdf0f9dc754a14cdb8702a3a488ba2bf0fad404a7da3ba4481f620fa6f234c9 languageName: node linkType: hard -"@babel/plugin-transform-new-target@npm:^7.18.6, @babel/plugin-transform-new-target@npm:^7.22.5": - version: 7.23.3 - resolution: "@babel/plugin-transform-new-target@npm:7.23.3" +"@babel/plugin-transform-new-target@npm:^7.25.7": + version: 7.25.7 + resolution: "@babel/plugin-transform-new-target@npm:7.25.7" dependencies: - "@babel/helper-plugin-utils": "npm:^7.22.5" + "@babel/helper-plugin-utils": "npm:^7.25.7" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10/e5053389316fce73ad5201b7777437164f333e24787fbcda4ae489cd2580dbbbdfb5694a7237bad91fabb46b591d771975d69beb1c740b82cb4761625379f00b + checksum: 10/c410edc9d8800590e34e648851a633534c3d153d0a76a34cc12854a4ecd578ce1b1c121e42e8c8f654757fcba13849a39fccde0d619de9ee3567a8f9fa2c8fc0 languageName: node linkType: hard -"@babel/plugin-transform-nullish-coalescing-operator@npm:^7.22.11": - version: 7.22.11 - resolution: "@babel/plugin-transform-nullish-coalescing-operator@npm:7.22.11" +"@babel/plugin-transform-nullish-coalescing-operator@npm:^7.25.8": + version: 7.25.8 + resolution: "@babel/plugin-transform-nullish-coalescing-operator@npm:7.25.8" dependencies: - "@babel/helper-plugin-utils": "npm:^7.22.5" - "@babel/plugin-syntax-nullish-coalescing-operator": "npm:^7.8.3" + "@babel/helper-plugin-utils": "npm:^7.25.7" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10/167babecc8b8fe70796a7b7d34af667ebbf43da166c21689502e5e8cc93180b7a85979c77c9f64b7cce431b36718bd0a6df9e5e0ffea4ae22afb22cfef886372 + checksum: 10/d742fedc1abf404d7f40065cdff9afc521236607f0d06c48d1e471f43d3a7471010d1651ba4758d80c73347a39dc278d86c43a9c814382ded4e9c7c519ace021 languageName: node linkType: hard -"@babel/plugin-transform-numeric-separator@npm:^7.22.11": - version: 7.22.11 - resolution: "@babel/plugin-transform-numeric-separator@npm:7.22.11" +"@babel/plugin-transform-numeric-separator@npm:^7.25.8": + version: 7.25.8 + resolution: "@babel/plugin-transform-numeric-separator@npm:7.25.8" dependencies: - "@babel/helper-plugin-utils": "npm:^7.22.5" - "@babel/plugin-syntax-numeric-separator": "npm:^7.10.4" + "@babel/helper-plugin-utils": "npm:^7.25.7" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10/af064d06a4a041767ec396a5f258103f64785df290e038bba9f0ef454e6c914f2ac45d862bbdad8fac2c7ad47fa4e95356f29053c60c100a0160b02a995fe2a3 + checksum: 10/e27779a309dbc5fdba71d7eae0eac5506547632b0cbf8f0add8215797bbda4f4e61595750236fee3292600cc2d13892f133beccc52b2998534e0b10c668db857 languageName: node linkType: hard -"@babel/plugin-transform-object-rest-spread@npm:^7.22.15": - version: 7.22.15 - resolution: "@babel/plugin-transform-object-rest-spread@npm:7.22.15" +"@babel/plugin-transform-object-rest-spread@npm:^7.25.8": + version: 7.25.8 + resolution: "@babel/plugin-transform-object-rest-spread@npm:7.25.8" dependencies: - "@babel/compat-data": "npm:^7.22.9" - "@babel/helper-compilation-targets": "npm:^7.22.15" - "@babel/helper-plugin-utils": "npm:^7.22.5" - "@babel/plugin-syntax-object-rest-spread": "npm:^7.8.3" - "@babel/plugin-transform-parameters": "npm:^7.22.15" + "@babel/helper-compilation-targets": "npm:^7.25.7" + "@babel/helper-plugin-utils": "npm:^7.25.7" + "@babel/plugin-transform-parameters": "npm:^7.25.7" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10/04b9f4bbabf4bbd019b47c60b294d873fe5d2f6063628a5b311d88da9e81b0a8622756dd42c7030359925479b7a3cd743dee46e73d84e03afd907d8cfd44ddea + checksum: 10/38f0fab8321a0b1e44784b7371f8bd5601eb885a7e9d88d7904dedda33a72f500d84792758c47e1541336c1b7592b6d956a85c2fd8e2e294f34c0303cc73442c languageName: node linkType: hard -"@babel/plugin-transform-object-super@npm:^7.18.6, @babel/plugin-transform-object-super@npm:^7.22.5": - version: 7.23.3 - resolution: "@babel/plugin-transform-object-super@npm:7.23.3" +"@babel/plugin-transform-object-super@npm:^7.25.7": + version: 7.25.7 + resolution: "@babel/plugin-transform-object-super@npm:7.25.7" dependencies: - "@babel/helper-plugin-utils": "npm:^7.22.5" - "@babel/helper-replace-supers": "npm:^7.22.20" + "@babel/helper-plugin-utils": "npm:^7.25.7" + "@babel/helper-replace-supers": "npm:^7.25.7" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10/e495497186f621fa79026e183b4f1fbb172fd9df812cbd2d7f02c05b08adbe58012b1a6eb6dd58d11a30343f6ec80d0f4074f9b501d70aa1c94df76d59164c53 + checksum: 10/c033337d27f98a255509c3ac152a54ce25d707b7969a64ba5262c7ddb54ba962da081fe756ce922caa57d782cacc6705e3d8e74364938391170f043eb9c5905e languageName: node linkType: hard -"@babel/plugin-transform-optional-catch-binding@npm:^7.22.11": - version: 7.22.11 - resolution: "@babel/plugin-transform-optional-catch-binding@npm:7.22.11" +"@babel/plugin-transform-optional-catch-binding@npm:^7.25.8": + version: 7.25.8 + resolution: "@babel/plugin-transform-optional-catch-binding@npm:7.25.8" dependencies: - "@babel/helper-plugin-utils": "npm:^7.22.5" - "@babel/plugin-syntax-optional-catch-binding": "npm:^7.8.3" + "@babel/helper-plugin-utils": "npm:^7.25.7" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10/f17abd90e1de67c84d63afea29c8021c74abb2794d3a6eeafb0bbe7372d3db32aefca386e392116ec63884537a4a2815d090d26264d259bacc08f6e3ed05294c + checksum: 10/9ecf32accc5b12b83ce2f6537c9eac87f2b0f89abfe91a8a8c87ea5ece05820988415271d0fdaf7f565e2c0c837afb24fc644779029b98b1401782d9c0d73642 languageName: node linkType: hard -"@babel/plugin-transform-optional-chaining@npm:^7.22.15, @babel/plugin-transform-optional-chaining@npm:^7.23.3": - version: 7.23.4 - resolution: "@babel/plugin-transform-optional-chaining@npm:7.23.4" +"@babel/plugin-transform-optional-chaining@npm:^7.25.7, @babel/plugin-transform-optional-chaining@npm:^7.25.8": + version: 7.25.8 + resolution: "@babel/plugin-transform-optional-chaining@npm:7.25.8" dependencies: - "@babel/helper-plugin-utils": "npm:^7.22.5" - "@babel/helper-skip-transparent-expression-wrappers": "npm:^7.22.5" - "@babel/plugin-syntax-optional-chaining": "npm:^7.8.3" + "@babel/helper-plugin-utils": "npm:^7.25.7" + "@babel/helper-skip-transparent-expression-wrappers": "npm:^7.25.7" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10/0ef24e889d6151428953fc443af5f71f4dae73f373dc1b7f5dd3f6a61d511296eb77e9b870e8c2c02a933e3455ae24c1fa91738c826b72a4ff87e0337db527e8 + checksum: 10/ffb5d81e6dbb28907d5346c8e12a1ed1ea0e30170fbe609d48d0466cdbc9d11b5774c8781682693f7cf7bd39da6111980e54813af96c6b3086dc769369c67d28 languageName: node linkType: hard -"@babel/plugin-transform-parameters@npm:^7.20.7, @babel/plugin-transform-parameters@npm:^7.21.3, @babel/plugin-transform-parameters@npm:^7.22.15": - version: 7.23.3 - resolution: "@babel/plugin-transform-parameters@npm:7.23.3" +"@babel/plugin-transform-parameters@npm:^7.25.7": + version: 7.25.7 + resolution: "@babel/plugin-transform-parameters@npm:7.25.7" dependencies: - "@babel/helper-plugin-utils": "npm:^7.22.5" + "@babel/helper-plugin-utils": "npm:^7.25.7" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10/a8c36c3fc25f9daa46c4f6db47ea809c395dc4abc7f01c4b1391f6e5b0cd62b83b6016728b02a6a8ac21aca56207c9ec66daefc0336e9340976978de7e6e28df + checksum: 10/c6a77fece85b3fd7323ec4ecc62329932b92c2c1ec20f1cc7617d3e49cc175f143988e756f5ccc45deca0fe582040afa67eeefd1704a8188cf2dc437efcfaf53 languageName: node linkType: hard -"@babel/plugin-transform-private-methods@npm:^7.22.5": - version: 7.22.5 - resolution: "@babel/plugin-transform-private-methods@npm:7.22.5" +"@babel/plugin-transform-private-methods@npm:^7.25.7": + version: 7.25.7 + resolution: "@babel/plugin-transform-private-methods@npm:7.25.7" dependencies: - "@babel/helper-create-class-features-plugin": "npm:^7.22.5" - "@babel/helper-plugin-utils": "npm:^7.22.5" + "@babel/helper-create-class-features-plugin": "npm:^7.25.7" + "@babel/helper-plugin-utils": "npm:^7.25.7" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10/321479b4fcb6d3b3ef622ab22fd24001e43d46e680e8e41324c033d5810c84646e470f81b44cbcbef5c22e99030784f7cac92f1829974da7a47a60a7139082c3 + checksum: 10/79506a74334dc77f6c53f44109f0a3fcf6c50410faa5dd5e5d17ac4b73194098de509f5515a7aed3724a4bfa5dd246517e22a1dff4c20fc052df7a189bf2160d languageName: node linkType: hard -"@babel/plugin-transform-private-property-in-object@npm:^7.22.11": - version: 7.22.11 - resolution: "@babel/plugin-transform-private-property-in-object@npm:7.22.11" +"@babel/plugin-transform-private-property-in-object@npm:^7.25.8": + version: 7.25.8 + resolution: "@babel/plugin-transform-private-property-in-object@npm:7.25.8" dependencies: - "@babel/helper-annotate-as-pure": "npm:^7.22.5" - "@babel/helper-create-class-features-plugin": "npm:^7.22.11" - "@babel/helper-plugin-utils": "npm:^7.22.5" - "@babel/plugin-syntax-private-property-in-object": "npm:^7.14.5" + "@babel/helper-annotate-as-pure": "npm:^7.25.7" + "@babel/helper-create-class-features-plugin": "npm:^7.25.7" + "@babel/helper-plugin-utils": "npm:^7.25.7" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10/b00623d107069c91a164d5cf7486c0929a4ee3023fcddbc8844e21b5e66f369271e1aa51921c7d87b80d9927bc75d63afcfe4d577872457ddb0443a5b86bacca + checksum: 10/c612023879930c951e3a993104bbc3b78169aef6c38233758ee3358a7ab76954b41880bca67635df218dc6893aabad138f3783d508dc715419e62c8d1fad9088 languageName: node linkType: hard -"@babel/plugin-transform-property-literals@npm:^7.18.6, @babel/plugin-transform-property-literals@npm:^7.22.5": - version: 7.23.3 - resolution: "@babel/plugin-transform-property-literals@npm:7.23.3" +"@babel/plugin-transform-property-literals@npm:^7.25.7": + version: 7.25.7 + resolution: "@babel/plugin-transform-property-literals@npm:7.25.7" dependencies: - "@babel/helper-plugin-utils": "npm:^7.22.5" + "@babel/helper-plugin-utils": "npm:^7.25.7" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10/16b048c8e87f25095f6d53634ab7912992f78e6997a6ff549edc3cf519db4fca01c7b4e0798530d7f6a05228ceee479251245cdd850a5531c6e6f404104d6cc9 + checksum: 10/f8be4090e9ffa9eebaca5eab4534de16acc5c84a476649cfed532de564817fc982a47d9349e6e447c510786897625153f60740fe9128b40d3a1eae3bbb5e1438 languageName: node linkType: hard -"@babel/plugin-transform-react-display-name@npm:^7.22.5": - version: 7.22.5 - resolution: "@babel/plugin-transform-react-display-name@npm:7.22.5" +"@babel/plugin-transform-react-display-name@npm:^7.25.7": + version: 7.25.7 + resolution: "@babel/plugin-transform-react-display-name@npm:7.25.7" dependencies: - "@babel/helper-plugin-utils": "npm:^7.22.5" + "@babel/helper-plugin-utils": "npm:^7.25.7" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10/a12bfd1e4e93055efca3ace3c34722571bda59d9740dca364d225d9c6e3ca874f134694d21715c42cc63d79efd46db9665bd4a022998767f9245f1e29d5d204d + checksum: 10/2785dda2f1b5379692f9095bffbd412dd1c49f41096d111c2fba1fba7202f4eed558c675df1bbfdcd44590013f8d2b7e6fc84443866e8a5c9bd51cf95f79cbdb languageName: node linkType: hard -"@babel/plugin-transform-react-jsx-development@npm:^7.22.5": - version: 7.22.5 - resolution: "@babel/plugin-transform-react-jsx-development@npm:7.22.5" +"@babel/plugin-transform-react-jsx-development@npm:^7.25.7": + version: 7.25.7 + resolution: "@babel/plugin-transform-react-jsx-development@npm:7.25.7" dependencies: - "@babel/plugin-transform-react-jsx": "npm:^7.22.5" + "@babel/plugin-transform-react-jsx": "npm:^7.25.7" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10/36bc3ff0b96bb0ef4723070a50cfdf2e72cfd903a59eba448f9fe92fea47574d6f22efd99364413719e1f3fb3c51b6c9b2990b87af088f8486a84b2a5f9e4560 + checksum: 10/6e6e8f9f9fc5393b932fb646188d6df9f270b37ab31560a5f3622b373ccb9fbf3d1976b3fb1b899541d25c1fa504d315fb4f8473d53bd57ad614e523f1ecf2c1 languageName: node linkType: hard @@ -2440,362 +2245,265 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-react-jsx@npm:^7.22.15, @babel/plugin-transform-react-jsx@npm:^7.22.5": - version: 7.22.15 - resolution: "@babel/plugin-transform-react-jsx@npm:7.22.15" +"@babel/plugin-transform-react-jsx@npm:^7.25.7": + version: 7.25.7 + resolution: "@babel/plugin-transform-react-jsx@npm:7.25.7" dependencies: - "@babel/helper-annotate-as-pure": "npm:^7.22.5" - "@babel/helper-module-imports": "npm:^7.22.15" - "@babel/helper-plugin-utils": "npm:^7.22.5" - "@babel/plugin-syntax-jsx": "npm:^7.22.5" - "@babel/types": "npm:^7.22.15" + "@babel/helper-annotate-as-pure": "npm:^7.25.7" + "@babel/helper-module-imports": "npm:^7.25.7" + "@babel/helper-plugin-utils": "npm:^7.25.7" + "@babel/plugin-syntax-jsx": "npm:^7.25.7" + "@babel/types": "npm:^7.25.7" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10/a436bfbffe723d162e5816d510dca7349a1fc572c501d73f1e17bbca7eb899d7a6a14d8fc2ae5993dd79fdd77bcc68d295e59a3549bed03b8579c767f6e3c9dc + checksum: 10/9f87990b39c68dc6441b55bf9b530c89e8cfc7a610e250dfd8002d94a6b806a585fe7cc9318540e4e635eb819fdaf15a42fd5e8a2ec3f8949bd7a5c759b558d3 languageName: node linkType: hard -"@babel/plugin-transform-react-pure-annotations@npm:^7.22.5": - version: 7.22.5 - resolution: "@babel/plugin-transform-react-pure-annotations@npm:7.22.5" +"@babel/plugin-transform-react-pure-annotations@npm:^7.25.7": + version: 7.25.7 + resolution: "@babel/plugin-transform-react-pure-annotations@npm:7.25.7" dependencies: - "@babel/helper-annotate-as-pure": "npm:^7.22.5" - "@babel/helper-plugin-utils": "npm:^7.22.5" + "@babel/helper-annotate-as-pure": "npm:^7.25.7" + "@babel/helper-plugin-utils": "npm:^7.25.7" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10/092021c4f404e267002099ec20b3f12dd730cb90b0d83c5feed3dc00dbe43b9c42c795a18e7c6c7d7bddea20c7dd56221b146aec81b37f2e7eb5137331c61120 + checksum: 10/a0bb666ef2c0209d5c7f637b17587f7a6782dbb135475f836bfe59b2f9eb193821653d6291866fc643b8ca0cef56989a9648c6127727d630808fc6de6fa180ca languageName: node linkType: hard -"@babel/plugin-transform-regenerator@npm:^7.21.5, @babel/plugin-transform-regenerator@npm:^7.22.10": - version: 7.23.3 - resolution: "@babel/plugin-transform-regenerator@npm:7.23.3" +"@babel/plugin-transform-regenerator@npm:^7.25.7": + version: 7.25.7 + resolution: "@babel/plugin-transform-regenerator@npm:7.25.7" dependencies: - "@babel/helper-plugin-utils": "npm:^7.22.5" + "@babel/helper-plugin-utils": "npm:^7.25.7" regenerator-transform: "npm:^0.15.2" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10/7fdacc7b40008883871b519c9e5cdea493f75495118ccc56ac104b874983569a24edd024f0f5894ba1875c54ee2b442f295d6241c3280e61c725d0dd3317c8e6 + checksum: 10/7a68e841b12b5f767d98ee650aa914ea246d99cc84de054bdb331185894c0fa554ec4296f32d65385e3012dcf083a098e06c14e518056d7e8a0804227a12d85d languageName: node linkType: hard -"@babel/plugin-transform-reserved-words@npm:^7.18.6, @babel/plugin-transform-reserved-words@npm:^7.22.5": - version: 7.23.3 - resolution: "@babel/plugin-transform-reserved-words@npm:7.23.3" +"@babel/plugin-transform-reserved-words@npm:^7.25.7": + version: 7.25.7 + resolution: "@babel/plugin-transform-reserved-words@npm:7.25.7" dependencies: - "@babel/helper-plugin-utils": "npm:^7.22.5" + "@babel/helper-plugin-utils": "npm:^7.25.7" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10/298c4440ddc136784ff920127cea137168e068404e635dc946ddb5d7b2a27b66f1dd4c4acb01f7184478ff7d5c3e7177a127279479926519042948fb7fa0fa48 + checksum: 10/484edb3f4aa52f49914bea5f832fe380d159fff44e007ac9063666cf773bc258ef5b741f5a323167087bfd6a6dd5c2f96556d1ce5b3765bdf3a54fc018f3670d languageName: node linkType: hard -"@babel/plugin-transform-runtime@npm:~7.21.4": - version: 7.21.4 - resolution: "@babel/plugin-transform-runtime@npm:7.21.4" +"@babel/plugin-transform-runtime@npm:~7.25.7": + version: 7.25.7 + resolution: "@babel/plugin-transform-runtime@npm:7.25.7" dependencies: - "@babel/helper-module-imports": "npm:^7.21.4" - "@babel/helper-plugin-utils": "npm:^7.20.2" - babel-plugin-polyfill-corejs2: "npm:^0.3.3" - babel-plugin-polyfill-corejs3: "npm:^0.6.0" - babel-plugin-polyfill-regenerator: "npm:^0.4.1" - semver: "npm:^6.3.0" + "@babel/helper-module-imports": "npm:^7.25.7" + "@babel/helper-plugin-utils": "npm:^7.25.7" + babel-plugin-polyfill-corejs2: "npm:^0.4.10" + babel-plugin-polyfill-corejs3: "npm:^0.10.6" + babel-plugin-polyfill-regenerator: "npm:^0.6.1" + semver: "npm:^6.3.1" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10/0748067b95b8f87af34d2de866bdbd6e427bb711cc0d22822084b2476b412a3464d35db0a0369add087af387eb0d8aeb16ba02e99d36cc82ad79d6e79863a82f + checksum: 10/45a829b6748758f311e483ead6e874a8cc885a4cdb258582fd01221d055649f3ccf08e0eee8d2d6611c05affdde3bbda354c08ad0a6b6258c2d45d87399b7660 languageName: node linkType: hard -"@babel/plugin-transform-shorthand-properties@npm:^7.18.6, @babel/plugin-transform-shorthand-properties@npm:^7.22.5": - version: 7.23.3 - resolution: "@babel/plugin-transform-shorthand-properties@npm:7.23.3" +"@babel/plugin-transform-shorthand-properties@npm:^7.25.7": + version: 7.25.7 + resolution: "@babel/plugin-transform-shorthand-properties@npm:7.25.7" dependencies: - "@babel/helper-plugin-utils": "npm:^7.22.5" + "@babel/helper-plugin-utils": "npm:^7.25.7" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10/5d677a03676f9fff969b0246c423d64d77502e90a832665dc872a5a5e05e5708161ce1effd56bb3c0f2c20a1112fca874be57c8a759d8b08152755519281f326 + checksum: 10/71c9c1d77887ffa452b2d7c9026ee8e40596e4b4208b077369a997e4e031b474ab08c2991b882a9883b78d7cd6d0d2a2b73345b17e195577b28538360b36f914 languageName: node linkType: hard -"@babel/plugin-transform-spread@npm:^7.20.7, @babel/plugin-transform-spread@npm:^7.22.5": - version: 7.23.3 - resolution: "@babel/plugin-transform-spread@npm:7.23.3" +"@babel/plugin-transform-spread@npm:^7.25.7": + version: 7.25.7 + resolution: "@babel/plugin-transform-spread@npm:7.25.7" dependencies: - "@babel/helper-plugin-utils": "npm:^7.22.5" - "@babel/helper-skip-transparent-expression-wrappers": "npm:^7.22.5" + "@babel/helper-plugin-utils": "npm:^7.25.7" + "@babel/helper-skip-transparent-expression-wrappers": "npm:^7.25.7" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10/c6372d2f788fd71d85aba12fbe08ee509e053ed27457e6674a4f9cae41ff885e2eb88aafea8fadd0ccf990601fc69ec596fa00959e05af68a15461a8d97a548d + checksum: 10/5dd9e269241fccfdb8c9ac9cb21c53fa776113c3cee0ea92bb029940c6231b3bc7c0c70e13e5df220b80cfafe8683264cadff5b182bed9fd1b1317557f1a6c2d languageName: node linkType: hard -"@babel/plugin-transform-sticky-regex@npm:^7.18.6, @babel/plugin-transform-sticky-regex@npm:^7.22.5": - version: 7.23.3 - resolution: "@babel/plugin-transform-sticky-regex@npm:7.23.3" +"@babel/plugin-transform-sticky-regex@npm:^7.25.7": + version: 7.25.7 + resolution: "@babel/plugin-transform-sticky-regex@npm:7.25.7" dependencies: - "@babel/helper-plugin-utils": "npm:^7.22.5" + "@babel/helper-plugin-utils": "npm:^7.25.7" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10/53e55eb2575b7abfdb4af7e503a2bf7ef5faf8bf6b92d2cd2de0700bdd19e934e5517b23e6dfed94ba50ae516b62f3f916773ef7d9bc81f01503f585051e2949 + checksum: 10/9f918281fdf2661a095d53ce8b981acaec0fef2a133af4a4fb66132c7a8ad509c49f444ee140bfa5821db24f987d278b3886d3f04e6ba94a6a55c7b2ed024443 languageName: node linkType: hard -"@babel/plugin-transform-template-literals@npm:^7.18.9, @babel/plugin-transform-template-literals@npm:^7.22.5": - version: 7.23.3 - resolution: "@babel/plugin-transform-template-literals@npm:7.23.3" +"@babel/plugin-transform-template-literals@npm:^7.25.7": + version: 7.25.7 + resolution: "@babel/plugin-transform-template-literals@npm:7.25.7" dependencies: - "@babel/helper-plugin-utils": "npm:^7.22.5" + "@babel/helper-plugin-utils": "npm:^7.25.7" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10/b16c5cb0b8796be0118e9c144d15bdc0d20a7f3f59009c6303a6e9a8b74c146eceb3f05186f5b97afcba7cfa87e34c1585a22186e3d5b22f2fd3d27d959d92b2 + checksum: 10/bdb541c31d4890a0aea4cf73a897975b69372cc524302ee9b375424d1706c38d1344b891c14ad2cbc3926e9553ffc2596772e8dab5982e09a9da0d959e4a1e92 languageName: node linkType: hard -"@babel/plugin-transform-typeof-symbol@npm:^7.18.9, @babel/plugin-transform-typeof-symbol@npm:^7.22.5": - version: 7.23.3 - resolution: "@babel/plugin-transform-typeof-symbol@npm:7.23.3" +"@babel/plugin-transform-typeof-symbol@npm:^7.25.7": + version: 7.25.7 + resolution: "@babel/plugin-transform-typeof-symbol@npm:7.25.7" dependencies: - "@babel/helper-plugin-utils": "npm:^7.22.5" + "@babel/helper-plugin-utils": "npm:^7.25.7" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10/0af7184379d43afac7614fc89b1bdecce4e174d52f4efaeee8ec1a4f2c764356c6dba3525c0685231f1cbf435b6dd4ee9e738d7417f3b10ce8bbe869c32f4384 + checksum: 10/1145d65dbf720837b0a1bdcdb2b8b0a761587f3602703ba42209e06b6b8d81801a2041671a881ed0cff6866acec4f7c17031f8540017f2d53913584e152453db languageName: node linkType: hard -"@babel/plugin-transform-typescript@npm:^7.22.15": - version: 7.22.15 - resolution: "@babel/plugin-transform-typescript@npm:7.22.15" +"@babel/plugin-transform-typescript@npm:^7.25.7": + version: 7.25.7 + resolution: "@babel/plugin-transform-typescript@npm:7.25.7" dependencies: - "@babel/helper-annotate-as-pure": "npm:^7.22.5" - "@babel/helper-create-class-features-plugin": "npm:^7.22.15" - "@babel/helper-plugin-utils": "npm:^7.22.5" - "@babel/plugin-syntax-typescript": "npm:^7.22.5" + "@babel/helper-annotate-as-pure": "npm:^7.25.7" + "@babel/helper-create-class-features-plugin": "npm:^7.25.7" + "@babel/helper-plugin-utils": "npm:^7.25.7" + "@babel/helper-skip-transparent-expression-wrappers": "npm:^7.25.7" + "@babel/plugin-syntax-typescript": "npm:^7.25.7" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10/95c35fbc823773058e9f077635fbe579d00b8f1762756b14a6fcae0c2db1aefddb93093fda4ca462e9e7d49edd49d71afe0a17422698d7418a6d156fc2dfba19 + checksum: 10/2648da981cd71c2100a4ea496684f2c0b939fc77eb4bb9cc8f113d433eab17d4930d2e5ed8d280606bcedef58df99002a64dc92579c6cc7f6c6ee71ceaa77418 languageName: node linkType: hard -"@babel/plugin-transform-unicode-escapes@npm:^7.21.5, @babel/plugin-transform-unicode-escapes@npm:^7.22.10": - version: 7.23.3 - resolution: "@babel/plugin-transform-unicode-escapes@npm:7.23.3" +"@babel/plugin-transform-unicode-escapes@npm:^7.25.7": + version: 7.25.7 + resolution: "@babel/plugin-transform-unicode-escapes@npm:7.25.7" dependencies: - "@babel/helper-plugin-utils": "npm:^7.22.5" + "@babel/helper-plugin-utils": "npm:^7.25.7" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10/561c429183a54b9e4751519a3dfba6014431e9cdc1484fad03bdaf96582dfc72c76a4f8661df2aeeae7c34efd0fa4d02d3b83a2f63763ecf71ecc925f9cc1f60 + checksum: 10/3c8d5b36738690c2d0b843fcc213a18766e617d16b6cfd92f13be2eba025228b6a796fd8e5b6e209daffa1b453c52544bf62e40b917a32c7446184fef52c98fc languageName: node linkType: hard -"@babel/plugin-transform-unicode-property-regex@npm:^7.22.5": - version: 7.22.5 - resolution: "@babel/plugin-transform-unicode-property-regex@npm:7.22.5" +"@babel/plugin-transform-unicode-property-regex@npm:^7.25.7": + version: 7.25.7 + resolution: "@babel/plugin-transform-unicode-property-regex@npm:7.25.7" dependencies: - "@babel/helper-create-regexp-features-plugin": "npm:^7.22.5" - "@babel/helper-plugin-utils": "npm:^7.22.5" + "@babel/helper-create-regexp-features-plugin": "npm:^7.25.7" + "@babel/helper-plugin-utils": "npm:^7.25.7" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10/2495e5f663cb388e3d888b4ba3df419ac436a5012144ac170b622ddfc221f9ea9bdba839fa2bc0185cb776b578030666406452ec7791cbf0e7a3d4c88ae9574c + checksum: 10/cccdddc6837f5e82f0aca59fd77dbab5efd5024dcd6d358efc74faccb4892f69e943f7750f613fcc241f33973fe8622a7e96909305697e7e5868f8e9954cb84e languageName: node linkType: hard -"@babel/plugin-transform-unicode-regex@npm:^7.18.6, @babel/plugin-transform-unicode-regex@npm:^7.22.5": - version: 7.23.3 - resolution: "@babel/plugin-transform-unicode-regex@npm:7.23.3" +"@babel/plugin-transform-unicode-regex@npm:^7.25.7": + version: 7.25.7 + resolution: "@babel/plugin-transform-unicode-regex@npm:7.25.7" dependencies: - "@babel/helper-create-regexp-features-plugin": "npm:^7.22.15" - "@babel/helper-plugin-utils": "npm:^7.22.5" + "@babel/helper-create-regexp-features-plugin": "npm:^7.25.7" + "@babel/helper-plugin-utils": "npm:^7.25.7" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10/c5f835d17483ba899787f92e313dfa5b0055e3deab332f1d254078a2bba27ede47574b6599fcf34d3763f0c048ae0779dc21d2d8db09295edb4057478dc80a9a + checksum: 10/1a5a068d39741febd9b8cfce7bf4abf79b282a13c29d39ef7685bffdeb65e5d595e23d9630fedd34428a144d96701efed5a48ea1db0c250c4daf53f44da52983 languageName: node linkType: hard -"@babel/plugin-transform-unicode-sets-regex@npm:^7.22.5": - version: 7.22.5 - resolution: "@babel/plugin-transform-unicode-sets-regex@npm:7.22.5" +"@babel/plugin-transform-unicode-sets-regex@npm:^7.25.7": + version: 7.25.7 + resolution: "@babel/plugin-transform-unicode-sets-regex@npm:7.25.7" dependencies: - "@babel/helper-create-regexp-features-plugin": "npm:^7.22.5" - "@babel/helper-plugin-utils": "npm:^7.22.5" + "@babel/helper-create-regexp-features-plugin": "npm:^7.25.7" + "@babel/helper-plugin-utils": "npm:^7.25.7" peerDependencies: "@babel/core": ^7.0.0 - checksum: 10/c042070f980b139547f8b0179efbc049ac5930abec7fc26ed7a41d89a048d8ab17d362200e204b6f71c3c20d6991a0e74415e1a412a49adc8131c2a40c04822e - languageName: node - linkType: hard - -"@babel/preset-env@npm:~7.21.5": - version: 7.21.5 - resolution: "@babel/preset-env@npm:7.21.5" - dependencies: - "@babel/compat-data": "npm:^7.21.5" - "@babel/helper-compilation-targets": "npm:^7.21.5" - "@babel/helper-plugin-utils": "npm:^7.21.5" - "@babel/helper-validator-option": "npm:^7.21.0" - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "npm:^7.18.6" - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "npm:^7.20.7" - "@babel/plugin-proposal-async-generator-functions": "npm:^7.20.7" - "@babel/plugin-proposal-class-properties": "npm:^7.18.6" - "@babel/plugin-proposal-class-static-block": "npm:^7.21.0" - "@babel/plugin-proposal-dynamic-import": "npm:^7.18.6" - "@babel/plugin-proposal-export-namespace-from": "npm:^7.18.9" - "@babel/plugin-proposal-json-strings": "npm:^7.18.6" - "@babel/plugin-proposal-logical-assignment-operators": "npm:^7.20.7" - "@babel/plugin-proposal-nullish-coalescing-operator": "npm:^7.18.6" - "@babel/plugin-proposal-numeric-separator": "npm:^7.18.6" - "@babel/plugin-proposal-object-rest-spread": "npm:^7.20.7" - "@babel/plugin-proposal-optional-catch-binding": "npm:^7.18.6" - "@babel/plugin-proposal-optional-chaining": "npm:^7.21.0" - "@babel/plugin-proposal-private-methods": "npm:^7.18.6" - "@babel/plugin-proposal-private-property-in-object": "npm:^7.21.0" - "@babel/plugin-proposal-unicode-property-regex": "npm:^7.18.6" - "@babel/plugin-syntax-async-generators": "npm:^7.8.4" - "@babel/plugin-syntax-class-properties": "npm:^7.12.13" - "@babel/plugin-syntax-class-static-block": "npm:^7.14.5" - "@babel/plugin-syntax-dynamic-import": "npm:^7.8.3" - "@babel/plugin-syntax-export-namespace-from": "npm:^7.8.3" - "@babel/plugin-syntax-import-assertions": "npm:^7.20.0" - "@babel/plugin-syntax-import-meta": "npm:^7.10.4" - "@babel/plugin-syntax-json-strings": "npm:^7.8.3" - "@babel/plugin-syntax-logical-assignment-operators": "npm:^7.10.4" - "@babel/plugin-syntax-nullish-coalescing-operator": "npm:^7.8.3" - "@babel/plugin-syntax-numeric-separator": "npm:^7.10.4" - "@babel/plugin-syntax-object-rest-spread": "npm:^7.8.3" - "@babel/plugin-syntax-optional-catch-binding": "npm:^7.8.3" - "@babel/plugin-syntax-optional-chaining": "npm:^7.8.3" - "@babel/plugin-syntax-private-property-in-object": "npm:^7.14.5" - "@babel/plugin-syntax-top-level-await": "npm:^7.14.5" - "@babel/plugin-transform-arrow-functions": "npm:^7.21.5" - "@babel/plugin-transform-async-to-generator": "npm:^7.20.7" - "@babel/plugin-transform-block-scoped-functions": "npm:^7.18.6" - "@babel/plugin-transform-block-scoping": "npm:^7.21.0" - "@babel/plugin-transform-classes": "npm:^7.21.0" - "@babel/plugin-transform-computed-properties": "npm:^7.21.5" - "@babel/plugin-transform-destructuring": "npm:^7.21.3" - "@babel/plugin-transform-dotall-regex": "npm:^7.18.6" - "@babel/plugin-transform-duplicate-keys": "npm:^7.18.9" - "@babel/plugin-transform-exponentiation-operator": "npm:^7.18.6" - "@babel/plugin-transform-for-of": "npm:^7.21.5" - "@babel/plugin-transform-function-name": "npm:^7.18.9" - "@babel/plugin-transform-literals": "npm:^7.18.9" - "@babel/plugin-transform-member-expression-literals": "npm:^7.18.6" - "@babel/plugin-transform-modules-amd": "npm:^7.20.11" - "@babel/plugin-transform-modules-commonjs": "npm:^7.21.5" - "@babel/plugin-transform-modules-systemjs": "npm:^7.20.11" - "@babel/plugin-transform-modules-umd": "npm:^7.18.6" - "@babel/plugin-transform-named-capturing-groups-regex": "npm:^7.20.5" - "@babel/plugin-transform-new-target": "npm:^7.18.6" - "@babel/plugin-transform-object-super": "npm:^7.18.6" - "@babel/plugin-transform-parameters": "npm:^7.21.3" - "@babel/plugin-transform-property-literals": "npm:^7.18.6" - "@babel/plugin-transform-regenerator": "npm:^7.21.5" - "@babel/plugin-transform-reserved-words": "npm:^7.18.6" - "@babel/plugin-transform-shorthand-properties": "npm:^7.18.6" - "@babel/plugin-transform-spread": "npm:^7.20.7" - "@babel/plugin-transform-sticky-regex": "npm:^7.18.6" - "@babel/plugin-transform-template-literals": "npm:^7.18.9" - "@babel/plugin-transform-typeof-symbol": "npm:^7.18.9" - "@babel/plugin-transform-unicode-escapes": "npm:^7.21.5" - "@babel/plugin-transform-unicode-regex": "npm:^7.18.6" - "@babel/preset-modules": "npm:^0.1.5" - "@babel/types": "npm:^7.21.5" - babel-plugin-polyfill-corejs2: "npm:^0.3.3" - babel-plugin-polyfill-corejs3: "npm:^0.6.0" - babel-plugin-polyfill-regenerator: "npm:^0.4.1" - core-js-compat: "npm:^3.25.1" - semver: "npm:^6.3.0" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 10/8ecd96e5869b354fa24930054255d14a0bdc306515809b4dd758de01400d41bbf0323de19ce41cf6f54cbaa62a103343e999a0644ea16e368e99903780d0fb67 + checksum: 10/c06dd8e66704fc60a97ce2555fa9f6cdfa98bb935a811b15e811cf3a8b5723e508e92b24077163ad704ddce56115062b8cf2f5490d1ad7d23f863d93a8125f89 languageName: node linkType: hard -"@babel/preset-env@npm:~7.22.20": - version: 7.22.20 - resolution: "@babel/preset-env@npm:7.22.20" +"@babel/preset-env@npm:~7.25.8": + version: 7.25.8 + resolution: "@babel/preset-env@npm:7.25.8" dependencies: - "@babel/compat-data": "npm:^7.22.20" - "@babel/helper-compilation-targets": "npm:^7.22.15" - "@babel/helper-plugin-utils": "npm:^7.22.5" - "@babel/helper-validator-option": "npm:^7.22.15" - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "npm:^7.22.15" - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "npm:^7.22.15" + "@babel/compat-data": "npm:^7.25.8" + "@babel/helper-compilation-targets": "npm:^7.25.7" + "@babel/helper-plugin-utils": "npm:^7.25.7" + "@babel/helper-validator-option": "npm:^7.25.7" + "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "npm:^7.25.7" + "@babel/plugin-bugfix-safari-class-field-initializer-scope": "npm:^7.25.7" + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "npm:^7.25.7" + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "npm:^7.25.7" + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "npm:^7.25.7" "@babel/plugin-proposal-private-property-in-object": "npm:7.21.0-placeholder-for-preset-env.2" - "@babel/plugin-syntax-async-generators": "npm:^7.8.4" - "@babel/plugin-syntax-class-properties": "npm:^7.12.13" - "@babel/plugin-syntax-class-static-block": "npm:^7.14.5" - "@babel/plugin-syntax-dynamic-import": "npm:^7.8.3" - "@babel/plugin-syntax-export-namespace-from": "npm:^7.8.3" - "@babel/plugin-syntax-import-assertions": "npm:^7.22.5" - "@babel/plugin-syntax-import-attributes": "npm:^7.22.5" - "@babel/plugin-syntax-import-meta": "npm:^7.10.4" - "@babel/plugin-syntax-json-strings": "npm:^7.8.3" - "@babel/plugin-syntax-logical-assignment-operators": "npm:^7.10.4" - "@babel/plugin-syntax-nullish-coalescing-operator": "npm:^7.8.3" - "@babel/plugin-syntax-numeric-separator": "npm:^7.10.4" - "@babel/plugin-syntax-object-rest-spread": "npm:^7.8.3" - "@babel/plugin-syntax-optional-catch-binding": "npm:^7.8.3" - "@babel/plugin-syntax-optional-chaining": "npm:^7.8.3" - "@babel/plugin-syntax-private-property-in-object": "npm:^7.14.5" - "@babel/plugin-syntax-top-level-await": "npm:^7.14.5" + "@babel/plugin-syntax-import-assertions": "npm:^7.25.7" + "@babel/plugin-syntax-import-attributes": "npm:^7.25.7" "@babel/plugin-syntax-unicode-sets-regex": "npm:^7.18.6" - "@babel/plugin-transform-arrow-functions": "npm:^7.22.5" - "@babel/plugin-transform-async-generator-functions": "npm:^7.22.15" - "@babel/plugin-transform-async-to-generator": "npm:^7.22.5" - "@babel/plugin-transform-block-scoped-functions": "npm:^7.22.5" - "@babel/plugin-transform-block-scoping": "npm:^7.22.15" - "@babel/plugin-transform-class-properties": "npm:^7.22.5" - "@babel/plugin-transform-class-static-block": "npm:^7.22.11" - "@babel/plugin-transform-classes": "npm:^7.22.15" - "@babel/plugin-transform-computed-properties": "npm:^7.22.5" - "@babel/plugin-transform-destructuring": "npm:^7.22.15" - "@babel/plugin-transform-dotall-regex": "npm:^7.22.5" - "@babel/plugin-transform-duplicate-keys": "npm:^7.22.5" - "@babel/plugin-transform-dynamic-import": "npm:^7.22.11" - "@babel/plugin-transform-exponentiation-operator": "npm:^7.22.5" - "@babel/plugin-transform-export-namespace-from": "npm:^7.22.11" - "@babel/plugin-transform-for-of": "npm:^7.22.15" - "@babel/plugin-transform-function-name": "npm:^7.22.5" - "@babel/plugin-transform-json-strings": "npm:^7.22.11" - "@babel/plugin-transform-literals": "npm:^7.22.5" - "@babel/plugin-transform-logical-assignment-operators": "npm:^7.22.11" - "@babel/plugin-transform-member-expression-literals": "npm:^7.22.5" - "@babel/plugin-transform-modules-amd": "npm:^7.22.5" - "@babel/plugin-transform-modules-commonjs": "npm:^7.22.15" - "@babel/plugin-transform-modules-systemjs": "npm:^7.22.11" - "@babel/plugin-transform-modules-umd": "npm:^7.22.5" - "@babel/plugin-transform-named-capturing-groups-regex": "npm:^7.22.5" - "@babel/plugin-transform-new-target": "npm:^7.22.5" - "@babel/plugin-transform-nullish-coalescing-operator": "npm:^7.22.11" - "@babel/plugin-transform-numeric-separator": "npm:^7.22.11" - "@babel/plugin-transform-object-rest-spread": "npm:^7.22.15" - "@babel/plugin-transform-object-super": "npm:^7.22.5" - "@babel/plugin-transform-optional-catch-binding": "npm:^7.22.11" - "@babel/plugin-transform-optional-chaining": "npm:^7.22.15" - "@babel/plugin-transform-parameters": "npm:^7.22.15" - "@babel/plugin-transform-private-methods": "npm:^7.22.5" - "@babel/plugin-transform-private-property-in-object": "npm:^7.22.11" - "@babel/plugin-transform-property-literals": "npm:^7.22.5" - "@babel/plugin-transform-regenerator": "npm:^7.22.10" - "@babel/plugin-transform-reserved-words": "npm:^7.22.5" - "@babel/plugin-transform-shorthand-properties": "npm:^7.22.5" - "@babel/plugin-transform-spread": "npm:^7.22.5" - "@babel/plugin-transform-sticky-regex": "npm:^7.22.5" - "@babel/plugin-transform-template-literals": "npm:^7.22.5" - "@babel/plugin-transform-typeof-symbol": "npm:^7.22.5" - "@babel/plugin-transform-unicode-escapes": "npm:^7.22.10" - "@babel/plugin-transform-unicode-property-regex": "npm:^7.22.5" - "@babel/plugin-transform-unicode-regex": "npm:^7.22.5" - "@babel/plugin-transform-unicode-sets-regex": "npm:^7.22.5" + "@babel/plugin-transform-arrow-functions": "npm:^7.25.7" + "@babel/plugin-transform-async-generator-functions": "npm:^7.25.8" + "@babel/plugin-transform-async-to-generator": "npm:^7.25.7" + "@babel/plugin-transform-block-scoped-functions": "npm:^7.25.7" + "@babel/plugin-transform-block-scoping": "npm:^7.25.7" + "@babel/plugin-transform-class-properties": "npm:^7.25.7" + "@babel/plugin-transform-class-static-block": "npm:^7.25.8" + "@babel/plugin-transform-classes": "npm:^7.25.7" + "@babel/plugin-transform-computed-properties": "npm:^7.25.7" + "@babel/plugin-transform-destructuring": "npm:^7.25.7" + "@babel/plugin-transform-dotall-regex": "npm:^7.25.7" + "@babel/plugin-transform-duplicate-keys": "npm:^7.25.7" + "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "npm:^7.25.7" + "@babel/plugin-transform-dynamic-import": "npm:^7.25.8" + "@babel/plugin-transform-exponentiation-operator": "npm:^7.25.7" + "@babel/plugin-transform-export-namespace-from": "npm:^7.25.8" + "@babel/plugin-transform-for-of": "npm:^7.25.7" + "@babel/plugin-transform-function-name": "npm:^7.25.7" + "@babel/plugin-transform-json-strings": "npm:^7.25.8" + "@babel/plugin-transform-literals": "npm:^7.25.7" + "@babel/plugin-transform-logical-assignment-operators": "npm:^7.25.8" + "@babel/plugin-transform-member-expression-literals": "npm:^7.25.7" + "@babel/plugin-transform-modules-amd": "npm:^7.25.7" + "@babel/plugin-transform-modules-commonjs": "npm:^7.25.7" + "@babel/plugin-transform-modules-systemjs": "npm:^7.25.7" + "@babel/plugin-transform-modules-umd": "npm:^7.25.7" + "@babel/plugin-transform-named-capturing-groups-regex": "npm:^7.25.7" + "@babel/plugin-transform-new-target": "npm:^7.25.7" + "@babel/plugin-transform-nullish-coalescing-operator": "npm:^7.25.8" + "@babel/plugin-transform-numeric-separator": "npm:^7.25.8" + "@babel/plugin-transform-object-rest-spread": "npm:^7.25.8" + "@babel/plugin-transform-object-super": "npm:^7.25.7" + "@babel/plugin-transform-optional-catch-binding": "npm:^7.25.8" + "@babel/plugin-transform-optional-chaining": "npm:^7.25.8" + "@babel/plugin-transform-parameters": "npm:^7.25.7" + "@babel/plugin-transform-private-methods": "npm:^7.25.7" + "@babel/plugin-transform-private-property-in-object": "npm:^7.25.8" + "@babel/plugin-transform-property-literals": "npm:^7.25.7" + "@babel/plugin-transform-regenerator": "npm:^7.25.7" + "@babel/plugin-transform-reserved-words": "npm:^7.25.7" + "@babel/plugin-transform-shorthand-properties": "npm:^7.25.7" + "@babel/plugin-transform-spread": "npm:^7.25.7" + "@babel/plugin-transform-sticky-regex": "npm:^7.25.7" + "@babel/plugin-transform-template-literals": "npm:^7.25.7" + "@babel/plugin-transform-typeof-symbol": "npm:^7.25.7" + "@babel/plugin-transform-unicode-escapes": "npm:^7.25.7" + "@babel/plugin-transform-unicode-property-regex": "npm:^7.25.7" + "@babel/plugin-transform-unicode-regex": "npm:^7.25.7" + "@babel/plugin-transform-unicode-sets-regex": "npm:^7.25.7" "@babel/preset-modules": "npm:0.1.6-no-external-plugins" - "@babel/types": "npm:^7.22.19" - babel-plugin-polyfill-corejs2: "npm:^0.4.5" - babel-plugin-polyfill-corejs3: "npm:^0.8.3" - babel-plugin-polyfill-regenerator: "npm:^0.5.2" - core-js-compat: "npm:^3.31.0" + babel-plugin-polyfill-corejs2: "npm:^0.4.10" + babel-plugin-polyfill-corejs3: "npm:^0.10.6" + babel-plugin-polyfill-regenerator: "npm:^0.6.1" + core-js-compat: "npm:^3.38.1" semver: "npm:^6.3.1" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10/e9d6b84c6bf8ecd03ad75bd53f60d885a27d6de94127341ed2ead8c2a6aedf9ecc946dbfe92f87d3f4a6563fca79d3e5259b20edbdc663b83fc0ea43fdb444eb + checksum: 10/501d78f56df8bf6f98a42da5db475db183048c4280b3292cf988b6baf01843915161f3b341ed525e2fcafcc47726798532b0e1dc7eb80aa29cc88c9d6f94ee6e languageName: node linkType: hard @@ -2812,64 +2520,49 @@ __metadata: languageName: node linkType: hard -"@babel/preset-modules@npm:^0.1.5": - version: 0.1.6 - resolution: "@babel/preset-modules@npm:0.1.6" - dependencies: - "@babel/helper-plugin-utils": "npm:^7.0.0" - "@babel/plugin-proposal-unicode-property-regex": "npm:^7.4.4" - "@babel/plugin-transform-dotall-regex": "npm:^7.4.4" - "@babel/types": "npm:^7.4.4" - esutils: "npm:^2.0.2" - peerDependencies: - "@babel/core": ^7.0.0-0 || ^8.0.0-0 <8.0.0 - checksum: 10/339f1e3bbe28439a8b2c70b66505345df6171b42b5842fa28aa47b710176273feeead2f919085fd2cd4dd20628a573bca5e929f0fad48f6cb42df7ce5f05dd1c - languageName: node - linkType: hard - -"@babel/preset-react@npm:~7.22.15": - version: 7.22.15 - resolution: "@babel/preset-react@npm:7.22.15" +"@babel/preset-react@npm:~7.25.7": + version: 7.25.7 + resolution: "@babel/preset-react@npm:7.25.7" dependencies: - "@babel/helper-plugin-utils": "npm:^7.22.5" - "@babel/helper-validator-option": "npm:^7.22.15" - "@babel/plugin-transform-react-display-name": "npm:^7.22.5" - "@babel/plugin-transform-react-jsx": "npm:^7.22.15" - "@babel/plugin-transform-react-jsx-development": "npm:^7.22.5" - "@babel/plugin-transform-react-pure-annotations": "npm:^7.22.5" + "@babel/helper-plugin-utils": "npm:^7.25.7" + "@babel/helper-validator-option": "npm:^7.25.7" + "@babel/plugin-transform-react-display-name": "npm:^7.25.7" + "@babel/plugin-transform-react-jsx": "npm:^7.25.7" + "@babel/plugin-transform-react-jsx-development": "npm:^7.25.7" + "@babel/plugin-transform-react-pure-annotations": "npm:^7.25.7" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10/f9296e45346c3b6ab8296952edde5f1774cc9fdbdbefbc76047278fc3e889d3e15740f038ce017aca562d89f32fcbb6c11783d464fc6ae3066433178fa58513c + checksum: 10/4701a76b45f33b72efc93540e09204ac84eb2b8054de9570d041b6f952477efca2a6c7c916389a1aea4d408c38ebbc997148d693d9aa72d1b4df9a3b4b557c7c languageName: node linkType: hard -"@babel/preset-typescript@npm:~7.22.15": - version: 7.22.15 - resolution: "@babel/preset-typescript@npm:7.22.15" +"@babel/preset-typescript@npm:~7.25.7": + version: 7.25.7 + resolution: "@babel/preset-typescript@npm:7.25.7" dependencies: - "@babel/helper-plugin-utils": "npm:^7.22.5" - "@babel/helper-validator-option": "npm:^7.22.15" - "@babel/plugin-syntax-jsx": "npm:^7.22.5" - "@babel/plugin-transform-modules-commonjs": "npm:^7.22.15" - "@babel/plugin-transform-typescript": "npm:^7.22.15" + "@babel/helper-plugin-utils": "npm:^7.25.7" + "@babel/helper-validator-option": "npm:^7.25.7" + "@babel/plugin-syntax-jsx": "npm:^7.25.7" + "@babel/plugin-transform-modules-commonjs": "npm:^7.25.7" + "@babel/plugin-transform-typescript": "npm:^7.25.7" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10/a31c262a0af103405c05303cbd4528b4eae5e143b93efd6bfa634cfb3a96ea9e395bbf4b95f63ebf9a9cbd94f0cf1f377c3d1c68f6921d6ed67bec9fbe7654bd + checksum: 10/cf6501971f696800096f3b3aef4f7e2c774210b4204bb3a076b2f253970d6649c28003de3afc620b7c7ad67fb346083819c89bae2c644f59995ddb64d6003541 languageName: node linkType: hard -"@babel/register@npm:~7.22.15": - version: 7.22.15 - resolution: "@babel/register@npm:7.22.15" +"@babel/register@npm:~7.25.7": + version: 7.25.7 + resolution: "@babel/register@npm:7.25.7" dependencies: clone-deep: "npm:^4.0.1" find-cache-dir: "npm:^2.0.0" make-dir: "npm:^2.1.0" - pirates: "npm:^4.0.5" + pirates: "npm:^4.0.6" source-map-support: "npm:^0.5.16" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10/5497be6773608cd2d874210edd14499fce464ddbea170219da55955afe4c9173adb591164193458fd639e43b7d1314088a6186f4abf241476c59b3f0da6afd6f + checksum: 10/bb375399aea6d13b92eb4e40befb5ca8ec226d753aa3ff8d9be2c284805a133e3afe7128a7b103af7abb164868c273bd80ea9a9e55a9ec38ef17df4b977f1cd7 languageName: node linkType: hard @@ -2889,16 +2582,16 @@ __metadata: languageName: node linkType: hard -"@babel/runtime@npm:~7.22.15": - version: 7.22.15 - resolution: "@babel/runtime@npm:7.22.15" +"@babel/runtime@npm:~7.25.7": + version: 7.25.7 + resolution: "@babel/runtime@npm:7.25.7" dependencies: regenerator-runtime: "npm:^0.14.0" - checksum: 10/9670da63b77ea6d8234117c55a6d9888be5cf220b91a5954d7faefe7a537e06fa8992e11d36b7cff2ab0ef5301fe6effb3d41bec8b4e0bae10d386b7c377568b + checksum: 10/73411fe0f1bff3a962586cef05b30f49e554b6563767e6d84f7d79d605b2c20e7fc3df291a3aebef69043181a8f893afdab9e6672557a5c2d08b9377d6f678cd languageName: node linkType: hard -"@babel/template@npm:^7.20.7, @babel/template@npm:^7.22.15, @babel/template@npm:^7.3.3": +"@babel/template@npm:^7.22.15, @babel/template@npm:^7.3.3": version: 7.22.15 resolution: "@babel/template@npm:7.22.15" dependencies: @@ -2946,7 +2639,7 @@ __metadata: languageName: node linkType: hard -"@babel/traverse@npm:^7.21.5, @babel/traverse@npm:^7.22.20": +"@babel/traverse@npm:^7.22.20": version: 7.23.5 resolution: "@babel/traverse@npm:7.23.5" dependencies: @@ -2979,7 +2672,7 @@ __metadata: languageName: node linkType: hard -"@babel/types@npm:^7.0.0, @babel/types@npm:^7.20.7, @babel/types@npm:^7.21.5, @babel/types@npm:^7.22.15, @babel/types@npm:^7.22.19, @babel/types@npm:^7.22.5, @babel/types@npm:^7.23.0, @babel/types@npm:^7.23.5, @babel/types@npm:^7.3.3, @babel/types@npm:^7.4.4, @babel/types@npm:^7.8.3": +"@babel/types@npm:^7.0.0, @babel/types@npm:^7.20.7, @babel/types@npm:^7.22.15, @babel/types@npm:^7.22.19, @babel/types@npm:^7.22.5, @babel/types@npm:^7.23.0, @babel/types@npm:^7.23.5, @babel/types@npm:^7.3.3, @babel/types@npm:^7.4.4, @babel/types@npm:^7.8.3": version: 7.23.5 resolution: "@babel/types@npm:7.23.5" dependencies: @@ -8057,8 +7750,8 @@ __metadata: version: 0.0.0-use.local resolution: "@rocket.chat/base64@workspace:packages/base64" dependencies: - "@babel/core": "npm:~7.22.20" - "@babel/preset-env": "npm:~7.22.20" + "@babel/core": "npm:~7.25.8" + "@babel/preset-env": "npm:~7.25.8" "@rocket.chat/eslint-config": "workspace:^" "@rocket.chat/jest-presets": "workspace:~" "@typescript-eslint/eslint-plugin": "npm:~5.60.1" @@ -8083,9 +7776,9 @@ __metadata: version: 0.0.0-use.local resolution: "@rocket.chat/core-services@workspace:packages/core-services" dependencies: - "@babel/core": "npm:~7.22.20" - "@babel/preset-env": "npm:~7.22.20" - "@babel/preset-typescript": "npm:~7.22.15" + "@babel/core": "npm:~7.25.8" + "@babel/preset-env": "npm:~7.25.8" + "@babel/preset-typescript": "npm:~7.25.7" "@rocket.chat/apps-engine": "workspace:^" "@rocket.chat/core-typings": "workspace:^" "@rocket.chat/eslint-config": "workspace:^" @@ -8237,8 +7930,8 @@ __metadata: version: 0.0.0-use.local resolution: "@rocket.chat/eslint-config@workspace:packages/eslint-config" dependencies: - "@babel/core": "npm:^7.20.7" - "@babel/eslint-parser": "npm:~7.23.10" + "@babel/core": "npm:^7.25.8" + "@babel/eslint-parser": "npm:~7.25.8" "@types/eslint": "npm:~8.44.9" "@types/prettier": "npm:^2.6.3" "@typescript-eslint/eslint-plugin": "npm:~5.60.1" @@ -8392,10 +8085,10 @@ __metadata: version: 0.0.0-use.local resolution: "@rocket.chat/fuselage-ui-kit@workspace:packages/fuselage-ui-kit" dependencies: - "@babel/core": "npm:~7.22.20" - "@babel/preset-env": "npm:~7.22.20" - "@babel/preset-react": "npm:~7.22.15" - "@babel/preset-typescript": "npm:~7.22.15" + "@babel/core": "npm:~7.25.8" + "@babel/preset-env": "npm:~7.25.8" + "@babel/preset-react": "npm:~7.25.7" + "@babel/preset-typescript": "npm:~7.25.7" "@rocket.chat/apps-engine": "workspace:^" "@rocket.chat/core-typings": "workspace:^" "@rocket.chat/eslint-config": "workspace:^" @@ -8486,7 +8179,7 @@ __metadata: version: 0.0.0-use.local resolution: "@rocket.chat/gazzodown@workspace:packages/gazzodown" dependencies: - "@babel/core": "npm:~7.22.20" + "@babel/core": "npm:~7.25.8" "@rocket.chat/core-typings": "workspace:^" "@rocket.chat/css-in-js": "npm:~0.31.25" "@rocket.chat/fuselage": "npm:^0.59.1" @@ -8647,9 +8340,9 @@ __metadata: version: 0.0.0-use.local resolution: "@rocket.chat/livechat@workspace:packages/livechat" dependencies: - "@babel/eslint-parser": "npm:~7.23.10" - "@babel/preset-env": "npm:~7.22.20" - "@babel/preset-typescript": "npm:~7.22.15" + "@babel/eslint-parser": "npm:~7.25.8" + "@babel/preset-env": "npm:~7.25.8" + "@babel/preset-typescript": "npm:~7.25.7" "@rocket.chat/core-typings": "workspace:^" "@rocket.chat/ddp-client": "workspace:^" "@rocket.chat/eslint-config": "workspace:^" @@ -8791,9 +8484,9 @@ __metadata: version: 0.0.0-use.local resolution: "@rocket.chat/message-parser@workspace:packages/message-parser" dependencies: - "@babel/core": "npm:~7.21.8" - "@babel/eslint-parser": "npm:~7.21.8" - "@babel/preset-env": "npm:~7.21.5" + "@babel/core": "npm:~7.25.8" + "@babel/eslint-parser": "npm:~7.25.8" + "@babel/preset-env": "npm:~7.25.8" "@rocket.chat/eslint-config": "workspace:^" "@rocket.chat/jest-presets": "workspace:~" "@rocket.chat/peggy-loader": "workspace:~" @@ -8823,12 +8516,12 @@ __metadata: resolution: "@rocket.chat/meteor@workspace:apps/meteor" dependencies: "@axe-core/playwright": "npm:^4.7.3" - "@babel/core": "npm:~7.22.20" - "@babel/eslint-parser": "npm:~7.23.10" - "@babel/preset-env": "npm:~7.22.20" - "@babel/preset-react": "npm:~7.22.15" - "@babel/register": "npm:~7.22.15" - "@babel/runtime": "npm:~7.22.15" + "@babel/core": "npm:~7.25.8" + "@babel/eslint-parser": "npm:~7.25.8" + "@babel/preset-env": "npm:~7.25.8" + "@babel/preset-react": "npm:~7.25.7" + "@babel/register": "npm:~7.25.7" + "@babel/runtime": "npm:~7.25.7" "@bugsnag/js": "npm:~7.20.2" "@bugsnag/plugin-react": "npm:~7.19.0" "@faker-js/faker": "npm:~8.0.2" @@ -9488,9 +9181,9 @@ __metadata: version: 0.0.0-use.local resolution: "@rocket.chat/presence@workspace:ee/packages/presence" dependencies: - "@babel/core": "npm:~7.22.20" - "@babel/preset-env": "npm:~7.22.20" - "@babel/preset-typescript": "npm:~7.22.15" + "@babel/core": "npm:~7.25.8" + "@babel/preset-env": "npm:~7.25.8" + "@babel/preset-typescript": "npm:~7.25.7" "@rocket.chat/apps-engine": "workspace:^" "@rocket.chat/core-services": "workspace:^" "@rocket.chat/core-typings": "workspace:^" @@ -9554,8 +9247,8 @@ __metadata: version: 0.0.0-use.local resolution: "@rocket.chat/random@workspace:packages/random" dependencies: - "@babel/core": "npm:~7.22.20" - "@babel/preset-env": "npm:~7.22.20" + "@babel/core": "npm:~7.25.8" + "@babel/preset-env": "npm:~7.25.8" "@rocket.chat/eslint-config": "workspace:^" "@rocket.chat/jest-presets": "workspace:~" "@typescript-eslint/eslint-plugin": "npm:~5.60.1" @@ -9661,8 +9354,8 @@ __metadata: version: 0.0.0-use.local resolution: "@rocket.chat/sha256@workspace:packages/sha256" dependencies: - "@babel/core": "npm:~7.22.20" - "@babel/preset-env": "npm:~7.22.20" + "@babel/core": "npm:~7.25.8" + "@babel/preset-env": "npm:~7.25.8" "@rocket.chat/eslint-config": "workspace:^" "@rocket.chat/jest-presets": "workspace:~" "@typescript-eslint/eslint-plugin": "npm:~5.60.1" @@ -9751,7 +9444,7 @@ __metadata: version: 0.0.0-use.local resolution: "@rocket.chat/ui-avatar@workspace:packages/ui-avatar" dependencies: - "@babel/core": "npm:~7.22.20" + "@babel/core": "npm:~7.25.8" "@rocket.chat/fuselage": "npm:^0.59.1" "@rocket.chat/ui-contexts": "workspace:^" "@types/react": "npm:~17.0.80" @@ -9774,7 +9467,7 @@ __metadata: version: 0.0.0-use.local resolution: "@rocket.chat/ui-client@workspace:packages/ui-client" dependencies: - "@babel/core": "npm:~7.22.20" + "@babel/core": "npm:~7.25.8" "@react-aria/toolbar": "npm:^3.0.0-beta.1" "@rocket.chat/css-in-js": "npm:~0.31.25" "@rocket.chat/fuselage": "npm:^0.59.1" @@ -9826,7 +9519,7 @@ __metadata: version: 0.0.0-use.local resolution: "@rocket.chat/ui-composer@workspace:packages/ui-composer" dependencies: - "@babel/core": "npm:~7.22.20" + "@babel/core": "npm:~7.25.8" "@react-aria/toolbar": "npm:^3.0.0-beta.1" "@rocket.chat/eslint-config": "workspace:^" "@rocket.chat/fuselage": "npm:^0.59.1" @@ -9893,10 +9586,10 @@ __metadata: version: 0.0.0-use.local resolution: "@rocket.chat/ui-kit@workspace:packages/ui-kit" dependencies: - "@babel/core": "npm:~7.21.8" - "@babel/eslint-parser": "npm:~7.23.10" - "@babel/plugin-transform-runtime": "npm:~7.21.4" - "@babel/preset-env": "npm:~7.21.5" + "@babel/core": "npm:~7.25.8" + "@babel/eslint-parser": "npm:~7.25.8" + "@babel/plugin-transform-runtime": "npm:~7.25.7" + "@babel/preset-env": "npm:~7.25.8" "@rocket.chat/eslint-config": "workspace:~" "@rocket.chat/icons": "npm:~0.38.0" "@rocket.chat/jest-presets": "workspace:~" @@ -9949,7 +9642,7 @@ __metadata: version: 0.0.0-use.local resolution: "@rocket.chat/ui-video-conf@workspace:packages/ui-video-conf" dependencies: - "@babel/core": "npm:~7.22.20" + "@babel/core": "npm:~7.25.8" "@rocket.chat/css-in-js": "npm:~0.31.25" "@rocket.chat/emitter": "npm:~0.31.25" "@rocket.chat/eslint-config": "workspace:^" @@ -9997,7 +9690,7 @@ __metadata: version: 0.0.0-use.local resolution: "@rocket.chat/ui-voip@workspace:packages/ui-voip" dependencies: - "@babel/core": "npm:~7.22.20" + "@babel/core": "npm:~7.25.8" "@faker-js/faker": "npm:~8.0.2" "@rocket.chat/css-in-js": "npm:~0.31.25" "@rocket.chat/emitter": "npm:~0.31.25" @@ -10101,10 +9794,10 @@ __metadata: version: 0.0.0-use.local resolution: "@rocket.chat/web-ui-registration@workspace:packages/web-ui-registration" dependencies: - "@babel/core": "npm:~7.22.20" - "@babel/preset-env": "npm:~7.22.20" - "@babel/preset-react": "npm:~7.22.15" - "@babel/preset-typescript": "npm:~7.22.15" + "@babel/core": "npm:~7.25.8" + "@babel/preset-env": "npm:~7.25.8" + "@babel/preset-react": "npm:~7.25.7" + "@babel/preset-typescript": "npm:~7.25.7" "@rocket.chat/i18n": "workspace:~" "@rocket.chat/layout": "npm:~0.31.27" "@rocket.chat/mock-providers": "workspace:~" @@ -15226,75 +14919,39 @@ __metadata: languageName: node linkType: hard -"babel-plugin-polyfill-corejs2@npm:^0.3.3": - version: 0.3.3 - resolution: "babel-plugin-polyfill-corejs2@npm:0.3.3" - dependencies: - "@babel/compat-data": "npm:^7.17.7" - "@babel/helper-define-polyfill-provider": "npm:^0.3.3" - semver: "npm:^6.1.1" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 10/78584305a614325894b47b88061621b442f3fd7ccf7c61c68e49522e9ec5da300f4e5f09d8738abf7f2e93e578560587bc0af19a3a0fd815cdd0fb16c23442ab - languageName: node - linkType: hard - -"babel-plugin-polyfill-corejs2@npm:^0.4.5": - version: 0.4.5 - resolution: "babel-plugin-polyfill-corejs2@npm:0.4.5" +"babel-plugin-polyfill-corejs2@npm:^0.4.10": + version: 0.4.11 + resolution: "babel-plugin-polyfill-corejs2@npm:0.4.11" dependencies: "@babel/compat-data": "npm:^7.22.6" - "@babel/helper-define-polyfill-provider": "npm:^0.4.2" + "@babel/helper-define-polyfill-provider": "npm:^0.6.2" semver: "npm:^6.3.1" peerDependencies: "@babel/core": ^7.4.0 || ^8.0.0-0 <8.0.0 - checksum: 10/75552d49f7d874e2e9a082d19e3ce9cc95998abadbdc589e5af7de64f5088059863eb194989cfcfefc99623925c46e273bd49333f6aae58f6fff59696279132b - languageName: node - linkType: hard - -"babel-plugin-polyfill-corejs3@npm:^0.6.0": - version: 0.6.0 - resolution: "babel-plugin-polyfill-corejs3@npm:0.6.0" - dependencies: - "@babel/helper-define-polyfill-provider": "npm:^0.3.3" - core-js-compat: "npm:^3.25.1" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 10/cd030ffef418d34093a77264227d293ef6a4b808a1b1adb84b36203ca569504de65cf1185b759657e0baf479c0825c39553d78362445395faf5c4d03085a629f + checksum: 10/9c79908bed61b9f52190f254e22d3dca6ce25769738642579ba8d23832f3f9414567a90d8367a31831fa45d9b9607ac43d8d07ed31167d8ca8cda22871f4c7a1 languageName: node linkType: hard -"babel-plugin-polyfill-corejs3@npm:^0.8.3": - version: 0.8.3 - resolution: "babel-plugin-polyfill-corejs3@npm:0.8.3" +"babel-plugin-polyfill-corejs3@npm:^0.10.6": + version: 0.10.6 + resolution: "babel-plugin-polyfill-corejs3@npm:0.10.6" dependencies: - "@babel/helper-define-polyfill-provider": "npm:^0.4.2" - core-js-compat: "npm:^3.31.0" + "@babel/helper-define-polyfill-provider": "npm:^0.6.2" + core-js-compat: "npm:^3.38.0" peerDependencies: "@babel/core": ^7.4.0 || ^8.0.0-0 <8.0.0 - checksum: 10/95e57300341c52b4954b8c8d9d7dd6f9a5bd26f3ac6f67180f146398e5ea5ec5a8496a79d222e147a3e61b698ce4176677a194397ac9887bfa8072d2d7e4e29c + checksum: 10/360ac9054a57a18c540059dc627ad5d84d15f79790cb3d84d19a02eec7188c67d08a07db789c3822d6f5df22d918e296d1f27c4055fec2e287d328f09ea8a78a languageName: node linkType: hard -"babel-plugin-polyfill-regenerator@npm:^0.4.1": - version: 0.4.1 - resolution: "babel-plugin-polyfill-regenerator@npm:0.4.1" - dependencies: - "@babel/helper-define-polyfill-provider": "npm:^0.3.3" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 10/ab0355efbad17d29492503230387679dfb780b63b25408990d2e4cf421012dae61d6199ddc309f4d2409ce4e9d3002d187702700dd8f4f8770ebbba651ed066c - languageName: node - linkType: hard - -"babel-plugin-polyfill-regenerator@npm:^0.5.2": - version: 0.5.2 - resolution: "babel-plugin-polyfill-regenerator@npm:0.5.2" +"babel-plugin-polyfill-regenerator@npm:^0.6.1": + version: 0.6.2 + resolution: "babel-plugin-polyfill-regenerator@npm:0.6.2" dependencies: - "@babel/helper-define-polyfill-provider": "npm:^0.4.2" + "@babel/helper-define-polyfill-provider": "npm:^0.6.2" peerDependencies: "@babel/core": ^7.4.0 || ^8.0.0-0 <8.0.0 - checksum: 10/d962200f604016a9a09bc9b4aaf60a3db7af876bb65bcefaeac04d44ac9d9ec4037cf24ce117760cc141d7046b6394c7eb0320ba9665cb4a2ee64df2be187c93 + checksum: 10/150233571072b6b3dfe946242da39cba8587b7f908d1c006f7545fc88b0e3c3018d445739beb61e7a75835f0c2751dbe884a94ff9b245ec42369d9267e0e1b3f languageName: node linkType: hard @@ -16033,7 +15690,7 @@ __metadata: languageName: node linkType: hard -"browserslist@npm:^4.0.0, browserslist@npm:^4.12.0, browserslist@npm:^4.21.10, browserslist@npm:^4.22.1, browserslist@npm:^4.24.0": +"browserslist@npm:^4.0.0, browserslist@npm:^4.12.0, browserslist@npm:^4.21.10, browserslist@npm:^4.23.3, browserslist@npm:^4.24.0": version: 4.24.0 resolution: "browserslist@npm:4.24.0" dependencies: @@ -17660,12 +17317,12 @@ __metadata: languageName: node linkType: hard -"core-js-compat@npm:^3.25.1, core-js-compat@npm:^3.31.0": - version: 3.33.3 - resolution: "core-js-compat@npm:3.33.3" +"core-js-compat@npm:^3.38.0, core-js-compat@npm:^3.38.1": + version: 3.38.1 + resolution: "core-js-compat@npm:3.38.1" dependencies: - browserslist: "npm:^4.22.1" - checksum: 10/90d5580bac23946c31aec1b75f1af4ebeafe97528398623780b3728cb6b28444be9aeb3563c857643cc84b3579007c45281fcb69fba9d9a7a011bea370e5e940 + browserslist: "npm:^4.23.3" + checksum: 10/4e2f219354fd268895f79486461a12df96f24ed307321482fe2a43529c5a64e7c16bcba654980ba217d603444f5141d43a79058aeac77511085f065c5da72207 languageName: node linkType: hard @@ -25983,7 +25640,7 @@ __metadata: languageName: node linkType: hard -"jsesc@npm:^3.0.2": +"jsesc@npm:^3.0.2, jsesc@npm:~3.0.2": version: 3.0.2 resolution: "jsesc@npm:3.0.2" bin: @@ -30384,13 +30041,20 @@ __metadata: languageName: node linkType: hard -"pirates@npm:^4.0.1, pirates@npm:^4.0.4, pirates@npm:^4.0.5": +"pirates@npm:^4.0.1, pirates@npm:^4.0.4": version: 4.0.5 resolution: "pirates@npm:4.0.5" checksum: 10/3728bae0cf6c18c3d25f5449ee8c5bc1a6a83bca688abe0e1654ce8c069bfd408170397cef133ed9ec8b0faeb4093c5c728d0e72ab7b3385256cd87008c40364 languageName: node linkType: hard +"pirates@npm:^4.0.6": + version: 4.0.6 + resolution: "pirates@npm:4.0.6" + checksum: 10/d02dda76f4fec1cbdf395c36c11cf26f76a644f9f9a1bfa84d3167d0d3154d5289aacc72677aa20d599bb4a6937a471de1b65c995e2aea2d8687cbcd7e43ea5f + languageName: node + linkType: hard + "pkg-dir@npm:^3.0.0": version: 3.0.0 resolution: "pkg-dir@npm:3.0.0" @@ -32763,6 +32427,15 @@ __metadata: languageName: node linkType: hard +"regenerate-unicode-properties@npm:^10.2.0": + version: 10.2.0 + resolution: "regenerate-unicode-properties@npm:10.2.0" + dependencies: + regenerate: "npm:^1.4.2" + checksum: 10/9150eae6fe04a8c4f2ff06077396a86a98e224c8afad8344b1b656448e89e84edcd527e4b03aa5476774129eb6ad328ed684f9c1459794a935ec0cc17ce14329 + languageName: node + linkType: hard + "regenerate@npm:^1.4.2": version: 1.4.2 resolution: "regenerate@npm:1.4.2" @@ -32833,6 +32506,20 @@ __metadata: languageName: node linkType: hard +"regexpu-core@npm:^6.1.1": + version: 6.1.1 + resolution: "regexpu-core@npm:6.1.1" + dependencies: + regenerate: "npm:^1.4.2" + regenerate-unicode-properties: "npm:^10.2.0" + regjsgen: "npm:^0.8.0" + regjsparser: "npm:^0.11.0" + unicode-match-property-ecmascript: "npm:^2.0.0" + unicode-match-property-value-ecmascript: "npm:^2.1.0" + checksum: 10/6a7ffb42781cacedd7df3c47c72e2d725401a699855be94a37ece5e29d3f25ab3abdd81d73f2d9d32ebc4d41bd25e3c3cc21e5284203faf19e60943adc55252d + languageName: node + linkType: hard + "registry-auth-token@npm:3.3.2": version: 3.3.2 resolution: "registry-auth-token@npm:3.3.2" @@ -32862,6 +32549,24 @@ __metadata: languageName: node linkType: hard +"regjsgen@npm:^0.8.0": + version: 0.8.0 + resolution: "regjsgen@npm:0.8.0" + checksum: 10/b930f03347e4123c917d7b40436b4f87f625b8dd3e705b447ddd44804e4616c3addb7453f0902d6e914ab0446c30e816e445089bb641a4714237fe8141a0ef9d + languageName: node + linkType: hard + +"regjsparser@npm:^0.11.0": + version: 0.11.1 + resolution: "regjsparser@npm:0.11.1" + dependencies: + jsesc: "npm:~3.0.2" + bin: + regjsparser: bin/parser + checksum: 10/06295f1666f8e378c3b70eb01578b46e075eee0556865a297497ab40753f04cce526e96513b18e21e66b79c972e7377bd3b5caa86935ed5d736e9b3e0f857363 + languageName: node + linkType: hard + "regjsparser@npm:^0.9.1": version: 0.9.1 resolution: "regjsparser@npm:0.9.1" @@ -33775,7 +33480,7 @@ __metadata: languageName: node linkType: hard -"semver@npm:^6.0.0, semver@npm:^6.1.1, semver@npm:^6.1.2, semver@npm:^6.3.0, semver@npm:^6.3.1": +"semver@npm:^6.0.0, semver@npm:^6.3.0, semver@npm:^6.3.1": version: 6.3.1 resolution: "semver@npm:6.3.1" bin: From 94a1119bc707a97ce538f5a2ee4fbac772f8b22d Mon Sep 17 00:00:00 2001 From: Douglas Gubert Date: Fri, 18 Oct 2024 12:18:51 -0300 Subject: [PATCH 07/25] chore: sync latest Apps-engine alpha (#33649) --- .../src/definition/accessors/IRoomRead.ts | 20 +++++++++++++++++ .../src/server/accessors/RoomRead.ts | 22 +++++++++++++++++++ .../src/server/bridges/RoomBridge.ts | 16 ++++++++++++++ .../src/server/bridges/UserBridge.ts | 4 ++-- .../src/server/managers/AppListenerManager.ts | 18 ++++++++------- .../tests/server/accessors/RoomRead.spec.ts | 21 ++++++++++++++++++ .../tests/test-data/bridges/roomBridge.ts | 8 +++++++ .../tests/test-data/bridges/userBridge.ts | 2 +- 8 files changed, 100 insertions(+), 11 deletions(-) diff --git a/packages/apps-engine/src/definition/accessors/IRoomRead.ts b/packages/apps-engine/src/definition/accessors/IRoomRead.ts index f4e0df33239d..dd7fa1b2a684 100644 --- a/packages/apps-engine/src/definition/accessors/IRoomRead.ts +++ b/packages/apps-engine/src/definition/accessors/IRoomRead.ts @@ -90,4 +90,24 @@ export interface IRoomRead { * @returns a list of the users with the leader role in the room */ getLeaders(roomId: string): Promise>; + + /** + * Retrieves an array of unread messages for a specific user in a specific room. + * + * @param roomId The unique identifier of the room from which to retrieve unread messages. + * @param uid The unique identifier of the user for whom to retrieve unread messages. + * @param options Optional parameters for retrieving messages: + * - limit: The maximum number of messages to retrieve. If more than 100 is passed, it defaults to 100. + * - skip: The number of messages to skip (for pagination). + * - sort: An object defining the sorting order of the messages. Each key is a field to sort by, and the value is either 'asc' for ascending order or 'desc' for descending order. + * @returns A Promise that resolves to an array of IMessage objects representing the unread messages for the specified user in the specified room. + */ + getUnreadByUser(roomId: string, uid: string, options?: Partial): Promise; + + /** + * Gets the user's unread messages count in a room. + * @param roomId room's id + * @param uid user's id + */ + getUserUnreadMessageCount(roomId: string, uid: string): Promise; } diff --git a/packages/apps-engine/src/server/accessors/RoomRead.ts b/packages/apps-engine/src/server/accessors/RoomRead.ts index 32786f529e56..5b7d58ef67e1 100644 --- a/packages/apps-engine/src/server/accessors/RoomRead.ts +++ b/packages/apps-engine/src/server/accessors/RoomRead.ts @@ -58,6 +58,28 @@ export class RoomRead implements IRoomRead { return this.roomBridge.doGetLeaders(roomId, this.appId); } + public async getUnreadByUser(roomId: string, uid: string, options: Partial = {}): Promise { + const { limit = 100, sort = { createdAt: 'asc' }, skip = 0 } = options; + + if (typeof roomId !== 'string' || roomId.trim().length === 0) { + throw new Error('Invalid roomId: must be a non-empty string'); + } + + if (!Number.isFinite(limit) || limit <= 0 || limit > 100) { + throw new Error(`Invalid limit provided. Expected number between 1 and 100, got ${limit}`); + } + + this.validateSort(sort); + + const completeOptions: GetMessagesOptions = { limit, sort, skip }; + + return this.roomBridge.doGetUnreadByUser(roomId, uid, completeOptions, this.appId); + } + + public getUserUnreadMessageCount(roomId: string, uid: string): Promise { + return this.roomBridge.doGetUserUnreadMessageCount(roomId, uid, this.appId); + } + // If there are any invalid fields or values, throw private validateSort(sort: Record) { Object.entries(sort).forEach(([key, value]) => { diff --git a/packages/apps-engine/src/server/bridges/RoomBridge.ts b/packages/apps-engine/src/server/bridges/RoomBridge.ts index 19fecfab9667..deb6f81667d9 100644 --- a/packages/apps-engine/src/server/bridges/RoomBridge.ts +++ b/packages/apps-engine/src/server/bridges/RoomBridge.ts @@ -111,6 +111,18 @@ export abstract class RoomBridge extends BaseBridge { } } + public async doGetUnreadByUser(roomId: string, uid: string, options: GetMessagesOptions, appId: string): Promise { + if (this.hasReadPermission(appId)) { + return this.getUnreadByUser(roomId, uid, options, appId); + } + } + + public async doGetUserUnreadMessageCount(roomId: string, uid: string, appId: string): Promise { + if (this.hasReadPermission(appId)) { + return this.getUserUnreadMessageCount(roomId, uid, appId); + } + } + protected abstract create(room: IRoom, members: Array, appId: string): Promise; protected abstract getById(roomId: string, appId: string): Promise; @@ -147,6 +159,10 @@ export abstract class RoomBridge extends BaseBridge { protected abstract removeUsers(roomId: string, usernames: Array, appId: string): Promise; + protected abstract getUnreadByUser(roomId: string, uid: string, options: GetMessagesOptions, appId: string): Promise; + + protected abstract getUserUnreadMessageCount(roomId: string, uid: string, appId: string): Promise; + private hasWritePermission(appId: string): boolean { if (AppPermissionManager.hasPermission(appId, AppPermissions.room.write)) { return true; diff --git a/packages/apps-engine/src/server/bridges/UserBridge.ts b/packages/apps-engine/src/server/bridges/UserBridge.ts index 232f665078ff..d67f2a091dda 100644 --- a/packages/apps-engine/src/server/bridges/UserBridge.ts +++ b/packages/apps-engine/src/server/bridges/UserBridge.ts @@ -41,7 +41,7 @@ export abstract class UserBridge extends BaseBridge { public async doGetUserUnreadMessageCount(uid: string, appId: string): Promise { if (this.hasReadPermission(appId)) { - return this.getUserUnreadMessageCount(uid); + return this.getUserUnreadMessageCount(uid, appId); } } @@ -65,7 +65,7 @@ export abstract class UserBridge extends BaseBridge { protected abstract getActiveUserCount(): Promise; - protected abstract getUserUnreadMessageCount(uid: string): Promise; + protected abstract getUserUnreadMessageCount(uid: string, appId: string): Promise; /** * Creates a user. diff --git a/packages/apps-engine/src/server/managers/AppListenerManager.ts b/packages/apps-engine/src/server/managers/AppListenerManager.ts index 40c1a78725ed..dc145408a795 100644 --- a/packages/apps-engine/src/server/managers/AppListenerManager.ts +++ b/packages/apps-engine/src/server/managers/AppListenerManager.ts @@ -22,8 +22,7 @@ import type { IUIKitIncomingInteractionMessageContainer, IUIKitIncomingInteractionModalContainer, } from '../../definition/uikit/UIKitIncomingInteractionContainer'; -import type { IUIKitLivechatIncomingInteraction } from '../../definition/uikit/livechat'; -import { UIKitLivechatBlockInteractionContext } from '../../definition/uikit/livechat'; +import type { IUIKitLivechatBlockIncomingInteraction, IUIKitLivechatIncomingInteraction } from '../../definition/uikit/livechat'; import type { IFileUploadContext } from '../../definition/uploads/IFileUploadContext'; import type { IUser, IUserContext, IUserStatusContext, IUserUpdateContext } from '../../definition/users'; import type { AppManager } from '../AppManager'; @@ -1016,14 +1015,17 @@ export class AppListenerManager { const app = this.manager.getOneById(appId); - const interactionContext = ((interactionType: UIKitIncomingInteractionType, interactionData: IUIKitLivechatIncomingInteraction) => { - const { actionId, message, visitor, room, triggerId, container } = interactionData; + const interactionData = (( + interactionType: UIKitIncomingInteractionType, + interaction: IUIKitLivechatIncomingInteraction, + ): IUIKitLivechatBlockIncomingInteraction => { + const { actionId, message, visitor, room, triggerId, container } = interaction; switch (interactionType) { case UIKitIncomingInteractionType.BLOCK: { - const { value, blockId } = interactionData.payload as { value: string; blockId: string }; + const { value, blockId } = interaction.payload as { value: string; blockId: string }; - return new UIKitLivechatBlockInteractionContext({ + return { appId, actionId, blockId, @@ -1033,12 +1035,12 @@ export class AppListenerManager { value, message, container: container as IUIKitIncomingInteractionModalContainer | IUIKitIncomingInteractionMessageContainer, - }); + }; } } })(type, data); - return app.call(method, interactionContext); + return app.call(method, interactionData); } // Livechat diff --git a/packages/apps-engine/tests/server/accessors/RoomRead.spec.ts b/packages/apps-engine/tests/server/accessors/RoomRead.spec.ts index 0fe209a41616..7d5ca58eb488 100644 --- a/packages/apps-engine/tests/server/accessors/RoomRead.spec.ts +++ b/packages/apps-engine/tests/server/accessors/RoomRead.spec.ts @@ -14,6 +14,10 @@ export class RoomReadAccessorTestFixture { private messages: IMessageRaw[]; + private unreadRoomId: string; + + private unreadUserId: string; + private mockRoomBridgeWithRoom: RoomBridge; @SetupFixture @@ -21,10 +25,16 @@ export class RoomReadAccessorTestFixture { this.room = TestData.getRoom(); this.user = TestData.getUser(); this.messages = ['507f1f77bcf86cd799439011', '507f191e810c19729de860ea'].map((id) => TestData.getMessageRaw(id)); + this.unreadRoomId = this.messages[0].roomId; + this.unreadUserId = this.messages[0].sender._id; const theRoom = this.room; const theUser = this.user; const theMessages = this.messages; + + const theUnreadMsg = this.messages; + const { unreadRoomId } = this; + const { unreadUserId } = this; this.mockRoomBridgeWithRoom = { doGetById(id, appId): Promise { return Promise.resolve(theRoom); @@ -47,6 +57,12 @@ export class RoomReadAccessorTestFixture { doGetMessages(roomId, options, appId): Promise { return Promise.resolve(theMessages); }, + doGetUnreadByUser(roomId, uid, options, appId): Promise { + if (roomId === unreadRoomId && uid === unreadUserId) { + return Promise.resolve(theUnreadMsg); + } + return Promise.resolve([]); + }, } as RoomBridge; } @@ -68,6 +84,11 @@ export class RoomReadAccessorTestFixture { Expect(await rr.getDirectByUsernames([this.user.username])).toBe(this.room); Expect(await rr.getMessages('testing')).toBeDefined(); Expect(await rr.getMessages('testing')).toBe(this.messages); + Expect(await rr.getUnreadByUser(this.unreadRoomId, this.unreadUserId)).toBeDefined(); + Expect(await rr.getUnreadByUser(this.unreadRoomId, this.unreadUserId)).toEqual(this.messages); + + Expect(await rr.getUnreadByUser('fake', 'fake')).toBeDefined(); + Expect(await rr.getUnreadByUser('fake', 'fake')).toEqual([]); } @AsyncTest() diff --git a/packages/apps-engine/tests/test-data/bridges/roomBridge.ts b/packages/apps-engine/tests/test-data/bridges/roomBridge.ts index e5e7408b3d9d..05fa810c5a66 100644 --- a/packages/apps-engine/tests/test-data/bridges/roomBridge.ts +++ b/packages/apps-engine/tests/test-data/bridges/roomBridge.ts @@ -64,4 +64,12 @@ export class TestsRoomBridge extends RoomBridge { public removeUsers(roomId: string, usernames: string[], appId: string): Promise { throw new Error('Method not implemented'); } + + public getUnreadByUser(roomId: string, uid: string, options: GetMessagesOptions, appId: string): Promise { + throw new Error('Method not implemented.'); + } + + protected getUserUnreadMessageCount(roomId: string, uid: string, appId: string): Promise { + throw new Error('Method not implemented.'); + } } diff --git a/packages/apps-engine/tests/test-data/bridges/userBridge.ts b/packages/apps-engine/tests/test-data/bridges/userBridge.ts index c9399f5b6c79..83464315b00a 100644 --- a/packages/apps-engine/tests/test-data/bridges/userBridge.ts +++ b/packages/apps-engine/tests/test-data/bridges/userBridge.ts @@ -34,7 +34,7 @@ export class TestsUserBridge extends UserBridge { throw new Error('Method not implemented'); } - protected getUserUnreadMessageCount(uid: string): Promise { + protected getUserUnreadMessageCount(uid: string, appId: string): Promise { throw new Error('Method not implemented.'); } From 4aa731d6e9dd50b9224eeab480e23ebc134adec8 Mon Sep 17 00:00:00 2001 From: Hugo Costa Date: Fri, 18 Oct 2024 12:52:42 -0300 Subject: [PATCH 08/25] chore!: e2ee settings update and removing beta wording (#33605) --- .changeset/tender-turkeys-breathe.md | 6 ++++ .../account/security/AccountSecurityPage.tsx | 4 +-- .../views/account/security/EndToEnd.tsx | 31 ++++++++++++++----- apps/meteor/server/settings/e2e.ts | 14 ++++----- .../meteor/server/startup/migrations/index.ts | 1 + apps/meteor/server/startup/migrations/v318.ts | 11 +++++++ .../tests/e2e/page-objects/account-profile.ts | 4 +-- packages/i18n/src/locales/ar.i18n.json | 2 -- packages/i18n/src/locales/ca.i18n.json | 4 +-- packages/i18n/src/locales/cs.i18n.json | 6 ++-- packages/i18n/src/locales/da.i18n.json | 6 ++-- packages/i18n/src/locales/de-IN.i18n.json | 6 ++-- packages/i18n/src/locales/de.i18n.json | 3 -- packages/i18n/src/locales/en.i18n.json | 26 +++++++++------- packages/i18n/src/locales/es.i18n.json | 4 +-- packages/i18n/src/locales/fi.i18n.json | 5 +-- packages/i18n/src/locales/fr.i18n.json | 4 +-- packages/i18n/src/locales/hi-IN.i18n.json | 5 +-- packages/i18n/src/locales/hr.i18n.json | 1 - packages/i18n/src/locales/hu.i18n.json | 3 -- packages/i18n/src/locales/it.i18n.json | 3 +- packages/i18n/src/locales/ja.i18n.json | 4 +-- packages/i18n/src/locales/ka-GE.i18n.json | 6 ++-- packages/i18n/src/locales/km.i18n.json | 1 - packages/i18n/src/locales/ko.i18n.json | 6 ++-- packages/i18n/src/locales/nl.i18n.json | 4 +-- packages/i18n/src/locales/nn.i18n.json | 4 +-- packages/i18n/src/locales/no.i18n.json | 4 +-- packages/i18n/src/locales/pl.i18n.json | 5 +-- packages/i18n/src/locales/pt-BR.i18n.json | 6 ++-- packages/i18n/src/locales/pt.i18n.json | 4 +-- packages/i18n/src/locales/ru.i18n.json | 4 +-- packages/i18n/src/locales/se.i18n.json | 5 +-- packages/i18n/src/locales/sv.i18n.json | 3 -- packages/i18n/src/locales/tr.i18n.json | 1 - packages/i18n/src/locales/uk.i18n.json | 4 +-- packages/i18n/src/locales/zh-TW.i18n.json | 6 ++-- packages/i18n/src/locales/zh.i18n.json | 4 +-- 38 files changed, 95 insertions(+), 125 deletions(-) create mode 100644 .changeset/tender-turkeys-breathe.md create mode 100644 apps/meteor/server/startup/migrations/v318.ts diff --git a/.changeset/tender-turkeys-breathe.md b/.changeset/tender-turkeys-breathe.md new file mode 100644 index 000000000000..c9c533fc7291 --- /dev/null +++ b/.changeset/tender-turkeys-breathe.md @@ -0,0 +1,6 @@ +--- +"@rocket.chat/meteor": major +"@rocket.chat/i18n": patch +--- + +Updates End-to-end settings translations and removes beta wording diff --git a/apps/meteor/client/views/account/security/AccountSecurityPage.tsx b/apps/meteor/client/views/account/security/AccountSecurityPage.tsx index 81435536e981..c7d4157ddbd7 100644 --- a/apps/meteor/client/views/account/security/AccountSecurityPage.tsx +++ b/apps/meteor/client/views/account/security/AccountSecurityPage.tsx @@ -62,8 +62,8 @@ const AccountSecurityPage = (): ReactElement => { )} {e2eEnabled && ( diff --git a/apps/meteor/client/views/account/security/EndToEnd.tsx b/apps/meteor/client/views/account/security/EndToEnd.tsx index b2fb982ebb9b..5546cbd00a88 100644 --- a/apps/meteor/client/views/account/security/EndToEnd.tsx +++ b/apps/meteor/client/views/account/security/EndToEnd.tsx @@ -1,4 +1,4 @@ -import { Box, Margins, PasswordInput, Field, FieldGroup, FieldLabel, FieldRow, FieldError, FieldHint, Button } from '@rocket.chat/fuselage'; +import { Box, PasswordInput, Field, FieldGroup, FieldLabel, FieldRow, FieldError, FieldHint, Button, Divider } from '@rocket.chat/fuselage'; import { useUniqueId } from '@rocket.chat/fuselage-hooks'; import { useToastMessageDispatch, useMethod, useTranslation, useLogout } from '@rocket.chat/ui-contexts'; import type { ComponentProps, ReactElement } from 'react'; @@ -70,10 +70,19 @@ const EndToEnd = (props: ComponentProps): ReactElement => { const passwordConfirmId = useUniqueId(); return ( - - - {t('E2E_Encryption_Password_Change')} - + + + + + + {t('E2E_Encryption_Password_Change')} + + {t('New_encryption_password')} @@ -137,18 +146,24 @@ const EndToEnd = (props: ComponentProps): ReactElement => { primary disabled={!(keysExist && isValid)} onClick={handleSubmit(saveNewPassword)} + mbs={12} data-qa-type='e2e-encryption-save-password-button' > {t('Save_changes')} - + + + + + + {t('Reset_E2E_Key')} - + - + ); }; diff --git a/apps/meteor/server/settings/e2e.ts b/apps/meteor/server/settings/e2e.ts index 8d80ad5b8c18..b94bfc38d8c6 100644 --- a/apps/meteor/server/settings/e2e.ts +++ b/apps/meteor/server/settings/e2e.ts @@ -1,13 +1,13 @@ import { settingsRegistry } from '../../app/settings/server'; export const createE2ESettings = () => - settingsRegistry.addGroup('E2E Encryption', async function () { + settingsRegistry.addGroup('End-to-end_encryption', async function () { await this.add('E2E_Enable', false, { type: 'boolean', - i18nLabel: 'Enabled', + i18nLabel: 'End-to-end_encryption', i18nDescription: 'E2E_Enable_description', public: true, - alert: 'E2E_Enable_alert', + alert: 'E2EE_alert', }); await this.add('E2E_Allow_Unencrypted_Messages', false, { @@ -16,21 +16,19 @@ export const createE2ESettings = () => enableQuery: { _id: 'E2E_Enable', value: true }, }); - await this.add('E2E_Enable_Encrypt_Files', true, { + await this.add('E2E_Enabled_Default_DirectRooms', false, { type: 'boolean', - i18nLabel: 'E2E_Enable_Encrypt_Files', - i18nDescription: 'E2E_Enable_Encrypt_Files_Description', public: true, enableQuery: { _id: 'E2E_Enable', value: true }, }); - await this.add('E2E_Enabled_Default_DirectRooms', false, { + await this.add('E2E_Enabled_Default_PrivateRooms', false, { type: 'boolean', public: true, enableQuery: { _id: 'E2E_Enable', value: true }, }); - await this.add('E2E_Enabled_Default_PrivateRooms', false, { + await this.add('E2E_Enable_Encrypt_Files', true, { type: 'boolean', public: true, enableQuery: { _id: 'E2E_Enable', value: true }, diff --git a/apps/meteor/server/startup/migrations/index.ts b/apps/meteor/server/startup/migrations/index.ts index 384c77cbc14e..5ca570d216fe 100644 --- a/apps/meteor/server/startup/migrations/index.ts +++ b/apps/meteor/server/startup/migrations/index.ts @@ -23,5 +23,6 @@ import './v314'; import './v315'; import './v316'; import './v317'; +import './v318'; export * from './xrun'; diff --git a/apps/meteor/server/startup/migrations/v318.ts b/apps/meteor/server/startup/migrations/v318.ts new file mode 100644 index 000000000000..be22cd78d272 --- /dev/null +++ b/apps/meteor/server/startup/migrations/v318.ts @@ -0,0 +1,11 @@ +import { Settings } from '@rocket.chat/models'; + +import { addMigration } from '../../lib/migrations'; + +addMigration({ + version: 318, + name: 'Remove "E2E Encryption" setting group', + async up() { + await Settings.deleteOne({ _id: 'E2E Encryption', type: 'group' }); + }, +}); diff --git a/apps/meteor/tests/e2e/page-objects/account-profile.ts b/apps/meteor/tests/e2e/page-objects/account-profile.ts index 468f3f0abb1e..971b6dffc86e 100644 --- a/apps/meteor/tests/e2e/page-objects/account-profile.ts +++ b/apps/meteor/tests/e2e/page-objects/account-profile.ts @@ -93,11 +93,11 @@ export class AccountProfile { } get securityE2EEncryptionSection(): Locator { - return this.page.locator('[role="button"]:has-text("E2E Encryption")'); + return this.page.locator('[role="button"]:has-text("End-to-end encryption")'); } get securityE2EEncryptionResetKeyButton(): Locator { - return this.page.locator("role=button[name='Reset E2E Key']"); + return this.page.locator("role=button[name='Reset E2EE key']"); } get securityE2EEncryptionPassword(): Locator { diff --git a/packages/i18n/src/locales/ar.i18n.json b/packages/i18n/src/locales/ar.i18n.json index 5d49eefc16ff..ee0e6f179b0e 100644 --- a/packages/i18n/src/locales/ar.i18n.json +++ b/packages/i18n/src/locales/ar.i18n.json @@ -1482,13 +1482,11 @@ "Duplicated_Email_address_will_be_ignored": "سيتم تجاهل عنوان البريد الإلكتروني المكرر.", "Markdown_Marked_Tables": "تمكين الجداول المحددة", "duplicated-account": "حساب مكرر", - "E2E Encryption": "التشفير بين الطرفيات", "Markdown_Parser": "محلل Markdown", "Markdown_SupportSchemesForLink": "مخططات دعم Markdown للرابط", "Markdown_SupportSchemesForLink_Description": "قائمة المخططات المسموح بها مفصولة بفواصل", "E2E_enable": "تمكين التشفير بين الطرفيات", "E2E_disable": "تعطيل التشفير بين الطرفيات", - "E2E_Enable_alert": "تبقى هذه الميزة حاليًا في مرحلة تجريبية! يُرجى الإبلاغ عن الأخطاء إلى github.com/RocketChat/Rocket.Chat/issues وكن على دراية بما يأتي:
- لن يتم العثور على الرسائل المشفرة للغرف المشفرة من خلال عمليات البحث.
- قد لا تدعم تطبيقات الهاتف المحمول الرسائل المشفرة (يتم تنفيذها).
- قد لا تتمكن الروبوتات من رؤية الرسائل المشفرة حتى تقوم بتنفيذ الدعم لها.
- لن يتم تشفير عمليات الرفع في هذا الإصدار.", "E2E_Enable_description": "يمكنك تمكين الخيار لإنشاء مجموعات مشفرة وتكون قادرًا على تغيير المجموعات والرسائل المباشرة ليتم تشفيرها", "E2E_Enabled": "تم تمكين التشفير بين الطرفيات", "E2E_Enabled_Default_DirectRooms": "تمكين التشفير لـ Room المباشرة بصورة افتراضية", diff --git a/packages/i18n/src/locales/ca.i18n.json b/packages/i18n/src/locales/ca.i18n.json index c124c6fc4247..c0bde7e54311 100644 --- a/packages/i18n/src/locales/ca.i18n.json +++ b/packages/i18n/src/locales/ca.i18n.json @@ -1466,13 +1466,11 @@ "Duplicated_Email_address_will_be_ignored": "S'ignorarà l'adreça electrònica duplicada.", "Markdown_Marked_Tables": "Activa les Taules Marcades", "duplicated-account": "Compte duplicat", - "E2E Encryption": "Xifrat E2E", "Markdown_Parser": "Parsejador Markdown", "Markdown_SupportSchemesForLink": "Esquemes de suport de Markdown per a enllaç", "Markdown_SupportSchemesForLink_Description": "Llista dels scheme:// permesos separats per comes", "E2E_enable": "Habilitar E2E", "E2E_disable": "Deshabilitat E2E", - "E2E_Enable_alert": "Aquesta funció està actualment en versió beta. Informeu d’errors a github.com/RocketChat/Rocket.Chat/issues i tingueu en compte:
- Les operacions de cerca no trobaran els missatges xifrats de les sales xifrades.
- És possible que les aplicacions mòbils no admetin els missatges encriptats. (ho estan implementant).
- És possible que els robots no puguin veure els missatges xifrats fins que no implementin el suport.
- Les càrregues no es xifraran en aquesta versió.", "E2E_Enable_description": "Habiliteu l'opció per crear grups encriptats i poder canviar grups i missatges directes per ser encriptats", "E2E_Enabled": "E2E activat", "E2E_Enabled_Default_DirectRooms": "Habilitar l'encriptació per a les Rooms directes per defecte", @@ -3467,7 +3465,7 @@ "Resend_verification_email": "Reenviar el correu-e de verificació", "Reset": "Reinicialitza (reset)", "Reset_Connection": "Restablir la connexió", - "Reset_E2E_Key": "Restableix la clau E2E", + "Reset_E2E_Key": "Restableix la clau E2EE", "Reset_password": "Reinicialitza la contrasenya", "Reset_section_settings": "Reinicialitza els ajustos de la secció", "Reset_TOTP": "Restableix TOTP", diff --git a/packages/i18n/src/locales/cs.i18n.json b/packages/i18n/src/locales/cs.i18n.json index 8cb9cc35128c..3aec98442180 100644 --- a/packages/i18n/src/locales/cs.i18n.json +++ b/packages/i18n/src/locales/cs.i18n.json @@ -1260,11 +1260,9 @@ "Duplicated_Email_address_will_be_ignored": "Duplicitní e-mailová adresa bude ignorována.", "Markdown_Marked_Tables": "Povolit značky tabulek", "duplicated-account": "Duplicitní účet", - "E2E Encryption": "Šifrování E2E", "Markdown_Parser": "Parser markdownu", "Markdown_SupportSchemesForLink": "Schémata používaná pro automatické odkazy markdown", "Markdown_SupportSchemesForLink_Description": "Čárkami oddělený seznam povolených schémat", - "E2E_Enable_alert": "Tato funkce je aktuálně ve verzi beta! Nahlaste chyby na github.com/RocketChat/Rocket.Chat/issues a mějte na paměti:
- Šifrované zprávy šifrovaných místností nebudou nalezeny vyhledávacími operacemi.
- Mobilní aplikace nemusí podporovat šifrované zprávy (teprve se implementují).
- Boti nemusí vidět šifrované zprávy, dokud pro ně neimplementují podporu.
- Nahráváné soubory nebudou v této verzi šifrovány.", "E2E_Enable_description": "Povolit možnost vytváření šifrovaných skupin a možnost měnit skupiny a přímé zprávy, které mají být šifrovány", "E2E_Enabled": "E2E povoleno", "E2E_Enabled_Default_DirectRooms": "Přímé konverzace ve výchozím stavu šifrovat", @@ -2958,7 +2956,7 @@ "Resend_verification_email": "Opětovné zaslání ověřovacího e-mailu", "Reset": "Obnovit", "Reset_Connection": "Resetovat připojení", - "Reset_E2E_Key": "Resetování klíče E2E", + "Reset_E2E_Key": "Resetování klíče E2EE", "Reset_password": "Obnovit heslo", "Reset_section_settings": "Obnovit výchozí nastavení sekce", "reset-other-user-e2e-key": "Resetování klíče E2E jiného uživatele", @@ -3970,4 +3968,4 @@ "Enterprise": "Korporace", "UpgradeToGetMore_engagement-dashboard_Title": "Analytika", "UpgradeToGetMore_auditing_Title": "Audit zpráv" -} \ No newline at end of file +} diff --git a/packages/i18n/src/locales/da.i18n.json b/packages/i18n/src/locales/da.i18n.json index 3167d718bbc4..d3b1fa007652 100644 --- a/packages/i18n/src/locales/da.i18n.json +++ b/packages/i18n/src/locales/da.i18n.json @@ -1342,11 +1342,9 @@ "Duplicated_Email_address_will_be_ignored": "Duplikeret e-mail-adresse ignoreres.", "Markdown_Marked_Tables": "Aktivér markerede tabeller", "duplicated-account": "Konto-duplet", - "E2E Encryption": "E2E-kryptering", "Markdown_Parser": "Markdown Parser", "Markdown_SupportSchemesForLink": "Markdown Support Schemes for Link", "Markdown_SupportSchemesForLink_Description": "Kommasepareret liste over tilladte ordninger", - "E2E_Enable_alert": "Denne funktion er i øjeblikket i beta! Rapportér fejl til github.com/RocketChat/Rocket.Chat/issues og vær opmærksom på:
- Krypterede meddelelser om krypterede rum vil ikke blive fundet ikke ved søgning.
- Mobilapps understøtter muligvis ikke de krypterede meddelelser (de er ved at implementere det).
- Bots er muligvis ikke i stand til at se krypterede meddelelser før de implementerer support for det.
- Uploads vil ikke blive krypteret i denne version.", "E2E_Enable_description": "Aktivér mulighed for at oprette krypterede grupper og at være i stand til at ændre grupper og direkte beskeder med kryptering", "E2E_Enabled": "E2E aktiveret", "E2E_Enabled_Default_DirectRooms": "Aktivér kryptering for direkte rum som standard", @@ -3054,7 +3052,7 @@ "Resend_verification_email": "Gensend bekræftelses mail", "Reset": "Nulstil", "Reset_Connection": "Nulstil forbindelse", - "Reset_E2E_Key": "Nulstil E2E-nøgle", + "Reset_E2E_Key": "Nulstil E2EE-nøgle", "Reset_password": "Nulstille kodeord", "Reset_section_settings": "Nulstil sektionsindstillinger", "reset-other-user-e2e-key": "Nulstil den anden brugers E2E-nøgle", @@ -4083,4 +4081,4 @@ "Enterprise": "Firma", "UpgradeToGetMore_engagement-dashboard_Title": "Analyse", "UpgradeToGetMore_auditing_Title": "Meddelelsesovervågning" -} \ No newline at end of file +} diff --git a/packages/i18n/src/locales/de-IN.i18n.json b/packages/i18n/src/locales/de-IN.i18n.json index 3ab7a222d068..4ed95330c89b 100644 --- a/packages/i18n/src/locales/de-IN.i18n.json +++ b/packages/i18n/src/locales/de-IN.i18n.json @@ -1042,11 +1042,9 @@ "Duplicated_Email_address_will_be_ignored": "Doppelte E-Mail_Adressen werden ignoriert.", "Markdown_Marked_Tables": "Formatierte Tabellen", "duplicated-account": "Doppeltes Konto", - "E2E Encryption": "Ende-zu-Ende-Verschlüsselung", "Markdown_Parser": "Markdown Parser", "Markdown_SupportSchemesForLink": "Unterstützte Markdown- Schemata für Links", "Markdown_SupportSchemesForLink_Description": "Kommata getrennte Liste von erlaubten Schemata", - "E2E_Enable_alert": "Dieses Feature ist derzeit im Beta-Test. Bitte melde Fehler unter github.com/RocketChat/Rocket.Chat/issues und beachte folgende Auswirkungen:
- Verschlüsselte Nachrichten werden durch die Suche nicht gefunden werden.
- Die mobilen Apps unterstützen derzeit noch keine verschlüsselten Nachrichten.
- Bots werden wahrscheinlich keine verschlüsselten Nachrichten verarbeiten können.
- Uploads werden in dieser Version nicht verschlüsselt werden.", "E2E_Enable_description": "Aktiviere diese Option, um Direktnachrichten und private Gruppen verschlüsseln zu können.", "E2E_Enabled": "E2E aktiviert", "E2E_Encryption_Password_Explanation": "Du kannst jetzt verschlüsselte private Gruppen und Direktnachrichten erstellen. Du kannst auch vorhandene private Gruppen oder Direktnachrichten verschlüsseln.

Bitte bewahre Dein Passwort an einem sicheren Ort auf - Du musst ihn auf anderen Geräten eingeben, auf denen Du die Ende-zu-Ende-Verschlüsselung verwenden möchtest.", @@ -2659,7 +2657,7 @@ "Thank_you_for_your_feedback": "Vielen Dank für Deine Rückmeldung", "The_application_name_is_required": "Es muss ein Name für diese Anwendung angegeben werden", "The_channel_name_is_required": "Ein Name für den Kanal muss angegeben werden", - "The_emails_are_being_sent": "E-Mails werden gesendet", + "The_emails_are_being_sent": "E-Mails werden gesendet", "The_image_resize_will_not_work_because_we_can_not_detect_ImageMagick_or_GraphicsMagick_installed_in_your_server": "Die automatische Skalierung der Bilder funktioniert nicht, da ImageMagick oder GraphicsMagick nicht auf dem Server installiert sind.", "The_message_is_a_discussion_you_will_not_be_able_to_recover": "Diese Nachricht ist eine Diskussion. Wenn Du diese löschst, wirst Du die Nachrichten der Diskussion nicht mehr auffinden können.", "The_peer__peer__does_not_exist": "Der Peer {{peer}} ist nicht vorhanden.", @@ -3080,4 +3078,4 @@ "Your_question": "Deine Frage", "Your_server_link": "Dein Server-Link", "Your_workspace_is_ready": "Dein Arbeitsbereich ist einsatzbereit 🎉" -} \ No newline at end of file +} diff --git a/packages/i18n/src/locales/de.i18n.json b/packages/i18n/src/locales/de.i18n.json index 8935b95fc2ea..ad91a1593789 100644 --- a/packages/i18n/src/locales/de.i18n.json +++ b/packages/i18n/src/locales/de.i18n.json @@ -1653,14 +1653,11 @@ "Duplicated_Email_address_will_be_ignored": "Doppelte E-Mail-Adressen werden ignoriert.", "Markdown_Marked_Tables": "Markierte Tabellen aktivieren", "duplicated-account": "Doppeltes Konto", - "E2E Encryption": "Ende-zu-Ende-Verschlüsselung", "Markdown_Parser": "Markdown-Parser", "Markdown_SupportSchemesForLink": "Unterstützte Markdown- Schemata für Links", - "E2E Encryption_Description": "Halten Sie Unterhaltungen privat und stellen Sie sicher, dass nur der Absender und die beabsichtigten Empfänger sie lesen können.", "Markdown_SupportSchemesForLink_Description": "Kommagetrennte Liste von erlaubten Schemata", "E2E_enable": "E2E aktivieren", "E2E_disable": "E2E deaktivieren", - "E2E_Enable_alert": "Dieses Feature ist derzeit im Beta-Test. Bitte melden Sie Fehler unter github.com/RocketChat/Rocket.Chat/issues und beachten Sie folgende Auswirkungen:
- Verschlüsselte Nachrichten werden durch die Suche nicht gefunden werden.
- Die mobilen Apps unterstützen derzeit noch keine verschlüsselten Nachrichten.
- Bots werden wahrscheinlich derzeit keine verschlüsselten Nachrichten verarbeiten können.
- Uploads werden in dieser Version nicht verschlüsselt werden.", "E2E_Enable_description": "Aktivieren Sie diese Option, um Direktnachrichten und private Gruppen verschlüsseln zu können.", "E2E_Enabled": "E2E aktiviert", "E2E_Enabled_Default_DirectRooms": "Verschlüsselung für Direktnachrichten standardmäßig einschalten", diff --git a/packages/i18n/src/locales/en.i18n.json b/packages/i18n/src/locales/en.i18n.json index d8e29b5bff96..c69fd36b00e2 100644 --- a/packages/i18n/src/locales/en.i18n.json +++ b/packages/i18n/src/locales/en.i18n.json @@ -1806,37 +1806,39 @@ "Duplicated_Email_address_will_be_ignored": "Duplicated email address will be ignored.", "Markdown_Marked_Tables": "Enable Marked Tables", "duplicated-account": "Duplicated account", - "E2E_Allow_Unencrypted_Messages": "Access unencrypted content in encrypted rooms", - "E2E_Allow_Unencrypted_Messages_Description": "Allow access to encrypted rooms to people without room encryption keys. They'll be able to see and send unencrypted messages.", - "E2E Encryption": "E2E Encryption", + "E2E_Allow_Unencrypted_Messages": "Unencrypted messages in encrypted rooms", + "E2E_Allow_Unencrypted_Messages_Description": "Allow plain text messages to be sent in rooms with encrypted content. These messages will not be encrypted.", "E2E_Encryption_enabled_for_room": "End-to-end encryption enabled for #{{roomName}}", "E2E_Encryption_disabled_for_room": "End-to-end encryption disabled for #{{roomName}}", "E2EE_not_available_OTR": "This room has OTR enabled, E2E encryption cannot work with OTR.", "E2EE_Composer_Unencrypted_Message": "You're sending an unencrypted message", "Markdown_Parser": "Markdown Parser", "Markdown_SupportSchemesForLink": "Markdown Support Schemes for Link", - "E2E Encryption_Description": "Keep conversations private, ensuring only the sender and intended recipients are able to read them.", + "End-to-end_encryption": "End-to-end encryption", + "End-to-end_encryption_Description": "Ensure conversations are kept private", "Markdown_SupportSchemesForLink_Description": "Comma-separated list of allowed schemes", "E2E_enable": "Enable E2E", "E2E_disable": "Disable E2E", - "E2E_Enable_alert": "This feature is currently in beta! Please report bugs to github.com/RocketChat/Rocket.Chat/issues and be aware of:
- Encrypted messages of encrypted rooms will not be found by search operations.
- Bots may not be able to see encrypted messages until they implement support for it.", - "E2E_Enable_description": "Enable option to create encrypted groups and be able to change groups and direct messages to be encrypted", + "E2EE_alert": "Enabling E2EE affects other functionalities
  • - Encrypted content cannot be found by search
  • - Encrypted content cannot be audited
  • - Bot interactions may not work with encrypted messages
", + "E2E_Enable_description": "Allow channel, team, discussion and direct message content to be encrypted.", "E2E_Enabled": "E2E Enabled", - "E2E_Enabled_Default_DirectRooms": "Enable encryption for Direct Rooms by default", - "E2E_Enabled_Default_PrivateRooms": "Enable encryption for Private Rooms by default", + "E2E_Enabled_Default_DirectRooms": "Encrypt direct messages", + "E2E_Enabled_Default_DirectRooms_Description": "Turn encryption on by default each time a new direct message room is started.", + "E2E_Enabled_Default_PrivateRooms": "Encrypt private rooms", + "E2E_Enabled_Default_PrivateRooms_Description": "Turn encryption on by default each time a new private channel, private team or a discussion associated to either is created.", "E2E_Enabled_Mentions": "Mentions", "E2E_Enabled_Mentions_Description": "Notify people, and highlight user, channel, and team mentions in encrypted content.", "E2E_Enable_Encrypt_Files": "Encrypt files", "E2E_Enable_Encrypt_Files_Description": "Encrypt files sent inside encrypted rooms. Check for possible conflicts in [file upload settings.](admin/settings/FileUpload)", - "E2E_Encryption_Password_Change": "Change Encryption Password", - "E2E_Encryption_Password_Explanation": "You can now create encrypted private groups and direct messages. You may also change existing private groups or DMs to encrypted.

This is end-to-end encryption so the key to encode/decode your messages will not be saved on the server. For that reason you need to store your password somewhere safe. You will be required to enter it on other devices you wish to use e2e encryption on.", + "E2E_Encryption_Password_Change": "Change encryption password", + "E2E_Encryption_Password_Explanation": "Create encrypted private groups and direct messages or change existing private groups or direct messages to be encrypted.

This is end-to-end encryption so the key to encode/decode your messages will not be saved on the server. For that reason you need to store your password somewhere safe. You will be required to enter it on other devices you wish to use E2EE on.", "E2E_key_reset_email": "E2E Key Reset Notification", "E2E_message_encrypted_placeholder": "This message is end-to-end encrypted. To view it, you must enter your encryption key in your account settings.", "E2E_password_request_text": "To access your encrypted channels and direct messages, enter your encryption password. This is not stored on the server, so you’ll need to use it on every device.", "E2E_password_reveal_text": "Create secure private rooms and direct messages with end-to-end encryption. This password won’t be stored on the server. You can use it on all your devices.", "E2E_password_save_text": "This will only be displayed once, please save it now.", "E2E_Reset_Email_Content": "You've been automatically logged out. When you log in again, a new key will be generated and access will be restored to any encrypted room with at least one member online. If no members are online, access will be restored as soon as a member comes online.", - "E2E_Reset_Key_Explanation": "This will remove your current E2EE key and log you out.
When you log in again, a new key will be generated and access will be restored to any encrypted room with at least one member online.
If no members are online, access will be restored as soon as a member comes online.", + "E2E_Reset_Key_Explanation": "Resetting will remove your current E2EE key and log you out. When you log in again, a new key will be generated and your access restored to any encrypted room that has one or more members online. Due to the nature of E2EE, access cannot be restored to encrypted rooms that have no members online.", "E2E_Reset_Other_Key_Warning": "Resetting the E2EE key will log out the user. When the user logs in again, a new key will be generated and access will be restored to any encrypted room with at least one member online. If no members are online, access will be restored as soon as a member comes online.", "E2E_unavailable_for_federation": "E2E is unavailable for federated rooms", "E2E_indecipherable": "This message is end-to-end encrypted and cannot be decrypted due to multiple room key resets", @@ -4569,7 +4571,7 @@ "Reset": "Reset", "Reset_priorities": "Reset priorities", "Reset_Connection": "Reset Connection", - "Reset_E2E_Key": "Reset E2E Key", + "Reset_E2E_Key": "Reset E2EE key", "Reset_password": "Reset password", "Reset_section_settings": "Restore defaults", "Reset_TOTP": "Reset TOTP", diff --git a/packages/i18n/src/locales/es.i18n.json b/packages/i18n/src/locales/es.i18n.json index 7b8bcb68c085..fe2c2fa81493 100644 --- a/packages/i18n/src/locales/es.i18n.json +++ b/packages/i18n/src/locales/es.i18n.json @@ -1488,13 +1488,11 @@ "Duplicated_Email_address_will_be_ignored": "Se ignorará la dirección de correo electrónico duplicada.", "Markdown_Marked_Tables": "Habilitar tablas marcadas", "duplicated-account": "Cuenta duplicada", - "E2E Encryption": "Cifrado E2E", "Markdown_Parser": "Analizador de Markdown", "Markdown_SupportSchemesForLink": "Esquemas de soporte de Markdown para enlace", "Markdown_SupportSchemesForLink_Description": "Lista de esquemas permitidos separados por comas", "E2E_enable": "Habilitar E2E", "E2E_disable": "Deshabilitar E2E", - "E2E_Enable_alert": "Esta función se encuentra actualmente en versión beta. Informa acerca de los errores a github.com/RocketChat/Rocket.Chat/issues y ten en cuenta lo siguiente:
- Las operaciones de búsqueda no encontrarán mensajes cifrados de salas cifradas.
- Puede que las aplicaciones móviles no admitan los mensajes cifrados (se están implementando).
- Puede que los bots no puedan ver los mensajes cifrados hasta que se implemente el soporte para ellos.
- Las subidas no se cifrarán en esta versión.", "E2E_Enable_description": "Habilita la opción para crear grupos cifrados y para cambiar los grupos y mensajes directos para cifrarlos ", "E2E_Enabled": "E2E habilitado", "E2E_Enabled_Default_DirectRooms": "Habilitar cifrado de Rooms directas por defecto", @@ -3514,7 +3512,7 @@ "Resend_verification_email": "Reenviar correo electrónico de verificación", "Reset": "Restablecer", "Reset_Connection": "Restablecer conexión", - "Reset_E2E_Key": "Restablecer clave E2E", + "Reset_E2E_Key": "Restablecer clave E2EE", "Reset_password": "Restablecer contraseña", "Reset_section_settings": "Restablecer sección a valores por defecto", "Reset_TOTP": "Restablecer TOTP", diff --git a/packages/i18n/src/locales/fi.i18n.json b/packages/i18n/src/locales/fi.i18n.json index 34439a0bd9d3..b6511df18861 100644 --- a/packages/i18n/src/locales/fi.i18n.json +++ b/packages/i18n/src/locales/fi.i18n.json @@ -1668,14 +1668,11 @@ "Duplicated_Email_address_will_be_ignored": "Päällekkäinen sähköpostiosoite ohitetaan.", "Markdown_Marked_Tables": "Ota merkityt taulukot käyttöön", "duplicated-account": "Päällekkäinen tili", - "E2E Encryption": "E2E-salaus", "Markdown_Parser": "Markdown-jäsennin", "Markdown_SupportSchemesForLink": "Linkin Markdown-tukimallit", - "E2E Encryption_Description": "Pidä keskustelut yksityisinä, jotta vain lähettäjä ja halutut vastaanottajat voivat lukea niitä.", "Markdown_SupportSchemesForLink_Description": "Pilkulla erotettu luettelo sallittuista malleista", "E2E_enable": "Ota käyttöön E2E", "E2E_disable": "Poista käytöstä E2E", - "E2E_Enable_alert": "Ominaisuus on betavaiheessa! Ilmoita virheistä kanavalla github.com/RocketChat/Rocket.Chat/issues ja huomioi seuraavat:
- Salattujen huoneiden salattuja viestejä ei löydy hakutoiminnoilla.
- Mobiilisovellukset eivät välttämättä tue salattuja viestejä (ominaisuutta otetaan käyttöön).
- Botit eivät välttämättä näe salattuja viestejä, ennen kuin niiden tuki otetaan käyttöön.
- Latauksia ei salata tässä versiossa.", "E2E_Enable_description": "Ottamalla asetuksen käyttöön voit luoda salattuja ryhmiä ja voit muuttaa ryhmiä ja salattavia suoria viestejä", "E2E_Enabled": "E2E käytössä", "E2E_Enabled_Default_DirectRooms": "Ota salaus käyttöön Direct Room Room -huoneissa oletuksena", @@ -4031,7 +4028,7 @@ "Reset": "Nollaa", "Reset_priorities": "Nollaa prioriteetit", "Reset_Connection": "Nollaa yhteys", - "Reset_E2E_Key": "E2E-avaimen nollaus", + "Reset_E2E_Key": "E2EE-avaimen nollaus", "Reset_password": "Nollaa salasana", "Reset_section_settings": "Palauta osio oletusasetuksiin", "Reset_TOTP": "Nollaa TOTP", diff --git a/packages/i18n/src/locales/fr.i18n.json b/packages/i18n/src/locales/fr.i18n.json index 0d9665febbcb..e10120333982 100644 --- a/packages/i18n/src/locales/fr.i18n.json +++ b/packages/i18n/src/locales/fr.i18n.json @@ -1481,13 +1481,11 @@ "Duplicated_Email_address_will_be_ignored": "L'adresse e-mail en double sera ignorée.", "Markdown_Marked_Tables": "Activer les tables Marked", "duplicated-account": "Compte en double", - "E2E Encryption": "Chiffrement de bout en bout (E2E)", "Markdown_Parser": "Analyseur Markdown", "Markdown_SupportSchemesForLink": "Schémas de liens pris en charge par Markdown", "Markdown_SupportSchemesForLink_Description": "Liste de schémas autorisés séparés par des virgules", "E2E_enable": "Activer E2E", "E2E_disable": "Désactiver E2E", - "E2E_Enable_alert": "Cette fonctionnalité est actuellement en version bêta. Signalez les bogues sur github.com/RocketChat/Rocket.Chat/issues et tenez compte des éléments suivants :
- Les messages chiffrés des salons chiffrées ne seront pas détectés par les opérations de recherche.
Les applications mobiles peuvent ne pas prendre en charge les messages chiffrés (l'implémentation est en cours).
- Les bots ne pourront peut-être pas voir les messages chiffrés tant que la prise en charge n'aura pas été implémentée.
- Les téléchargements ne seront pas chiffrés dans cette version.", "E2E_Enable_description": "Activer l'option pour créer des groupes chiffrés et pouvoir modifier les groupes et les messages privés à chiffrer", "E2E_Enabled": "Chiffrement de bout en bout (E2E) activé", "E2E_Enabled_Default_DirectRooms": "Activer le chiffrement pour les salons directs par défaut", @@ -3514,7 +3512,7 @@ "Resend_verification_email": "Renvoyer l'e-mail de vérification", "Reset": "Réinitialiser", "Reset_Connection": "Réinitialiser la connexion", - "Reset_E2E_Key": "Réinitialiser la clé E2E", + "Reset_E2E_Key": "Réinitialiser la clé E2EE", "Reset_password": "Réinitialiser le mot de passe", "Reset_section_settings": "Réinitialiser les paramètres par défaut de la section", "Reset_TOTP": "Réinitialiser TOTP", diff --git a/packages/i18n/src/locales/hi-IN.i18n.json b/packages/i18n/src/locales/hi-IN.i18n.json index ceb6e47b0b50..b5e231768b5e 100644 --- a/packages/i18n/src/locales/hi-IN.i18n.json +++ b/packages/i18n/src/locales/hi-IN.i18n.json @@ -1723,16 +1723,13 @@ "Duplicated_Email_address_will_be_ignored": "डुप्लिकेट ईमेल पते पर ध्यान नहीं दिया जाएगा.", "Markdown_Marked_Tables": "चिह्नित तालिकाएँ सक्षम करें", "duplicated-account": "डुप्लिकेट खाता", - "E2E Encryption": "E2E एन्क्रिप्शन", "E2E_Encryption_enabled_for_room": "#{{roomName}} के लिए एंड-टू-एंड एन्क्रिप्शन सक्षम किया गया", "E2E_Encryption_disabled_for_room": "#{{roomName}} के लिए एंड-टू-एंड एन्क्रिप्शन अक्षम किया गया", "Markdown_Parser": "मार्कडाउन पार्सर", "Markdown_SupportSchemesForLink": "लिंक के लिए मार्कडाउन सहायता योजनाएँ", - "E2E Encryption_Description": "बातचीत को निजी रखें, यह सुनिश्चित करते हुए कि केवल प्रेषक और इच्छित प्राप्तकर्ता ही उन्हें पढ़ सकें।", "Markdown_SupportSchemesForLink_Description": "अनुमत योजनाओं की अल्पविराम से अलग की गई सूची", "E2E_enable": "E2E सक्षम करें", "E2E_disable": "E2E अक्षम करें", - "E2E_Enable_alert": "यह विशेषता अभी बीटा संस्करण में है! कृपया github.com/RocketChat/Rocket.Chat/issues पर बग की रिपोर्ट करें और इनसे अवगत रहें:
- एन्क्रिप्टेड रूम के एन्क्रिप्टेड संदेश सर्च ऑपरेशन से नहीं मिलेंगे।
- मोबाइल ऐप्स एन्क्रिप्टेड संदेशों का समर्थन नहीं कर सकते (वे इसे लागू कर रहे हैं)।
- बॉट एन्क्रिप्टेड संदेशों को तब तक नहीं देख पाएंगे जब तक वे इसके लिए समर्थन लागू नहीं करते।
- इस संस्करण में अपलोड एन्क्रिप्टेड नहीं होंगे.", "E2E_Enable_description": "एन्क्रिप्टेड समूह बनाने का विकल्प सक्षम करें और समूहों को बदलने और एन्क्रिप्ट किए जाने वाले संदेशों को निर्देशित करने में सक्षम हों", "E2E_Enabled": "E2E सक्षम", "E2E_Enabled_Default_DirectRooms": "डिफ़ॉल्ट रूप से डायरेक्ट रूम के लिए एन्क्रिप्शन सक्षम करें", @@ -4264,7 +4261,7 @@ "Reset": "रीसेट", "Reset_priorities": "प्राथमिकताएँ रीसेट करें", "Reset_Connection": "कनेक्शन रीसेट करें", - "Reset_E2E_Key": "E2E कुंजी रीसेट करें", + "Reset_E2E_Key": "E2EE कुंजी रीसेट करें", "Reset_password": "पासवर्ड रीसेट", "Reset_section_settings": "डिफॉल्ट्स का पुनःस्थापन", "Reset_TOTP": "टीओटीपी रीसेट करें", diff --git a/packages/i18n/src/locales/hr.i18n.json b/packages/i18n/src/locales/hr.i18n.json index ef6cf511ee7a..0ab9b6e1dad0 100644 --- a/packages/i18n/src/locales/hr.i18n.json +++ b/packages/i18n/src/locales/hr.i18n.json @@ -1027,7 +1027,6 @@ "Duplicated_Email_address_will_be_ignored": "Dupla adresa e-pošte bit će zanemarena.", "Markdown_Marked_Tables": "Omogući označene tablice", "duplicated-account": "Dupli račun", - "E2E Encryption": "E2E šifriranje", "Markdown_Parser": "Ispitivač odziva", "Markdown_SupportSchemesForLink": "Markdown Support Sheme za poveznice", "Markdown_SupportSchemesForLink_Description": "Popis dozvoljenih shema, odvojenih zarezom", diff --git a/packages/i18n/src/locales/hu.i18n.json b/packages/i18n/src/locales/hu.i18n.json index b787fc5789e1..da26cc26203c 100644 --- a/packages/i18n/src/locales/hu.i18n.json +++ b/packages/i18n/src/locales/hu.i18n.json @@ -1619,14 +1619,11 @@ "Duplicated_Email_address_will_be_ignored": "A kettőzött e-mail-cím mellőzve lesz.", "Markdown_Marked_Tables": "Marked táblázatok engedélyezése", "duplicated-account": "Kettőzött fiók", - "E2E Encryption": "Végpontok közötti titkosítás", "Markdown_Parser": "Markdown-feldolgozó", "Markdown_SupportSchemesForLink": "Markdown támogatási sémák a hivatkozáshoz", - "E2E Encryption_Description": "A beszélgetések bizalmasan tartása, és annak biztosítása, hogy csak a feladó és a szándékolt címzettek legyenek képesek elolvasni azokat.", "Markdown_SupportSchemesForLink_Description": "Engedélyezett sémák vesszővel elválasztott listája", "E2E_enable": "Végpontok közötti titkosítás engedélyezése", "E2E_disable": "Végpontok közötti titkosítás letiltása", - "E2E_Enable_alert": "Ez a funkció jelenleg béta állapotú! A hibákat a github.com/RocketChat/Rocket.Chat/issues címen jelezze, és legyen tisztában a következőkkel:
- A titkosított szobák titkosított üzenetei nem lesznek megtalálhatók a keresési műveletekkel.
- A mobilalkalmazások esetleg nem támogatják a titkosított üzeneteket (ők valósítják meg azt).
- A robotok esetleg nem lesznek képesek megtekinteni a titkosított üzeneteket, amíg nem valósítják meg a támogatást ehhez.
- A feltöltések nem lesznek titkosítva ebben a verzióban.", "E2E_Enable_description": "Beállítás engedélyezése a titkosított csoportok létrehozásához, valamint a titkosítandó csoportok és közvetlen üzenetek megváltoztatásának képességéhez", "E2E_Enabled": "Végpontok közötti titkosítás engedélyezve", "E2E_Enabled_Default_DirectRooms": "Titkosítás engedélyezése alapértelmezetten a közvetlen szobáknál", diff --git a/packages/i18n/src/locales/it.i18n.json b/packages/i18n/src/locales/it.i18n.json index e049bb8b6aa0..d97e288cd5b8 100644 --- a/packages/i18n/src/locales/it.i18n.json +++ b/packages/i18n/src/locales/it.i18n.json @@ -1184,7 +1184,6 @@ "Duplicated_Email_address_will_be_ignored": "Gli indirizzi e-mail duplicati saranno ignorati.", "Markdown_Marked_Tables": "Abilita tabelle contrassegnate", "duplicated-account": "Account duplicato", - "E2E Encryption": "Crittografia E2E", "Markdown_Parser": "Markdown Parser", "Markdown_SupportSchemesForLink": "Schemi di supporto Markdown per i link", "Markdown_SupportSchemesForLink_Description": "Elenco separato da virgole per i programmi consentiti", @@ -2568,7 +2567,7 @@ "Resend_verification_email": "Invia nuovamente email di verifica", "Reset": "Reimposta", "Reset_Connection": "Reimposta connessione", - "Reset_E2E_Key": "Ripristina la Chiave E2E", + "Reset_E2E_Key": "Ripristina la Chiave E2EE", "Reset_password": "Reimposta password", "Reset_section_settings": "Resetta le impostazioni della sezione", "Restart": "Riavvia", diff --git a/packages/i18n/src/locales/ja.i18n.json b/packages/i18n/src/locales/ja.i18n.json index 03c16d68eb65..5f90bf07d5bd 100644 --- a/packages/i18n/src/locales/ja.i18n.json +++ b/packages/i18n/src/locales/ja.i18n.json @@ -1459,13 +1459,11 @@ "Duplicated_Email_address_will_be_ignored": "重複したメールアドレスは無視されます。", "Markdown_Marked_Tables": "markedテーブルを有効にする", "duplicated-account": "重複アカウント", - "E2E Encryption": "E2E暗号化", "Markdown_Parser": "マークダウンパーサー", "Markdown_SupportSchemesForLink": "リンクのマークダウンサポートスキーム", "Markdown_SupportSchemesForLink_Description": "許可するスキームのコンマ区切り一覧", "E2E_enable": "E2Eを有効にする", "E2E_disable": "E2Eを無効にする", - "E2E_Enable_alert": "この機能は現在ベータ版です!バグはgithub.com/RocketChat/Rocket.Chat/issuesに報告してください。
- 暗号化されたルームの暗号化されたメッセージは検索操作で見つかりません。
- モバイルアプリは暗号化されたメッセージをサポートしていない可能性があります(実装はしています)。
- ボットは、サポートを実装するまで、暗号化されたメッセージを見ることができません。
- このバージョンでは、アップロードは暗号化されません。", "E2E_Enable_description": "暗号化されたグループを作成しプライベートグループとダイレクトメッセージを暗号化できるオプションを有効にする", "E2E_Enabled": "E2Eが有効", "E2E_Enabled_Default_DirectRooms": "デフォルトでダイレクトRoomの暗号化を有効にする", @@ -3477,7 +3475,7 @@ "Resend_verification_email": "確認メールを再送信", "Reset": "リセット", "Reset_Connection": "接続のリセット", - "Reset_E2E_Key": "E2Eキーのリセット", + "Reset_E2E_Key": "E2EEキーのリセット", "Reset_password": "パスワードをリセット", "Reset_section_settings": "セクションをデフォルトにリセット", "Reset_TOTP": "TOTPをリセット", diff --git a/packages/i18n/src/locales/ka-GE.i18n.json b/packages/i18n/src/locales/ka-GE.i18n.json index cf231b838dce..0437c60992eb 100644 --- a/packages/i18n/src/locales/ka-GE.i18n.json +++ b/packages/i18n/src/locales/ka-GE.i18n.json @@ -1195,9 +1195,7 @@ "Duplicated_Email_address_will_be_ignored": "დუბლირებული ელ.ფოსტის მისამართის უგულებელყოფა მოხდება.", "Markdown_Marked_Tables": "მონიშნული ცხრილების ჩართვა", "duplicated-account": "დუბლირებული ანგარიში", - "E2E Encryption": "E2E დაშიფვრა", "Markdown_SupportSchemesForLink_Description": "დაშვებული სქემების მძიმით გამოყოფილი სია", - "E2E_Enable_alert": "ეს ფუნქცია ამჟამად ბეტა რეჟიმშია! გთხოვთ, აცნობეთ შეცდომები github.com/RocketChat/Rocket.Chat/issues და გაითვალისწინეთ:
- დაშიფრული ოთახების დაშიფრული შეტყობინებების მოძებნა შეუძლებელია საძიებო ოპერაციებით.
მობილურ აპლიკაციებს შეიძლება არ ქონდეთ შიფრირებული შეტყობინებების მხარდაჭერა (მიმდინარეობს მუშაობა ჩანერგვაზე).
ბოტებმა შეიძლება ვერ ნახონ დაშიფრული მესიჯები სანამ მხარდაჭერა არ ექნებათ.
ატვირთული ფაილები არ იქნება დაშიფრული ამ ვერსიაში", "E2E_Enable_description": "ჩართეთ ეს ფუნქცია და შექმენით დაშიფრული ჯგუფები, გექნებათ ჯგუფების და პირდაპირი შეტყობინებების დაშიფვრის საშუალება", "E2E_Enabled": "E2E ჩართულია", "E2E_Enabled_Default_DirectRooms": "პირდაპირი ოთახების შიფრაციის ჩართვა დეფაულტად", @@ -2762,7 +2760,7 @@ "Resend_verification_email": "დადასტურების ელ.ფოსტის ხელახლა გაგზავნა", "Reset": "გადატვირთვა", "Reset_Connection": "კავშირის გადატვირთვა", - "Reset_E2E_Key": "E2E გასაღების გადატვირთვა", + "Reset_E2E_Key": "E2EE გასაღების გადატვირთვა", "Reset_password": "პაროლის განახლება", "Reset_section_settings": "განყოფილების პარამეტრების გადატვირთვა", "reset-other-user-e2e-key": "სხვა მომხმარებლის E2E გასაღებების გადატვირთვა", @@ -3671,4 +3669,4 @@ "onboarding.form.registerOfflineForm.title": "ხელით დარეგისტრირება", "UpgradeToGetMore_engagement-dashboard_Title": "ანალიტიკა", "UpgradeToGetMore_auditing_Title": "შეტყობინებების შემოწმება" -} \ No newline at end of file +} diff --git a/packages/i18n/src/locales/km.i18n.json b/packages/i18n/src/locales/km.i18n.json index 77815b2a8e49..e2f35e19e770 100644 --- a/packages/i18n/src/locales/km.i18n.json +++ b/packages/i18n/src/locales/km.i18n.json @@ -1107,7 +1107,6 @@ "Duplicated_Email_address_will_be_ignored": "អាសយដ្ឋានអ៊ីមែលស្ទួននឹងត្រូវបានមិនអើពើ។", "Markdown_Marked_Tables": "បើកដំណើរការតារាងដែលបានសម្គាល់", "duplicated-account": "គណនីស្ទួន", - "E2E Encryption": "E2E Encryption", "Markdown_Parser": "កម្មវិធីញែក Markdown", "Markdown_SupportSchemesForLink": "គម្រោងគាំទ្រ Markdown សម្រាប់តំណ", "Markdown_SupportSchemesForLink_Description": "បញ្ជីបំបែកដោយសញ្ញាក្បៀសនៃគម្រោងដែលបានអនុញ្ញាត", diff --git a/packages/i18n/src/locales/ko.i18n.json b/packages/i18n/src/locales/ko.i18n.json index 8ad8378ebd4b..6a523855aa02 100644 --- a/packages/i18n/src/locales/ko.i18n.json +++ b/packages/i18n/src/locales/ko.i18n.json @@ -1306,13 +1306,11 @@ "Duplicated_Email_address_will_be_ignored": "중복된 이메일은 무시됩니다.", "Markdown_Marked_Tables": "마크된 표 사용", "duplicated-account": "중복된 계정", - "E2E Encryption": "E2E 암호화", "Markdown_Parser": "Markdown 구문해석", "Markdown_SupportSchemesForLink": "Markdown 링크 지원", "Markdown_SupportSchemesForLink_Description": "쉼표로 구분된 허용할 스키마", "E2E_enable": "E2E 활성화", "E2E_disable": "E2E 비활성화", - "E2E_Enable_alert": "이 기능은 현재 베타 버전입니다! 버그를 github.com/RocketChat/Rocket.Chat/issues에 보고해 보세요.
- 암호화된 대화방의 암호화된 메시지는 검색할 수 없습니다.
- 모바일 앱이 암호화된 메시지를 지원하지 않을 수 있습니다. (구현 중입니다.)
- 봇은 암호화된 메시지를 지원할 때까지 암호화된 메시지를 볼 수 없습니다.
-이 버전에서는 업로드가 암호화되지 않습니다.", "E2E_Enable_description": "암호화 된 그룹을 생성하고, 기존 생성된 그룹과 1:1대화방을 암호화할 수 있는 옵션 사용", "E2E_Enabled": "E2E 사용", "E2E_Enabled_Default_DirectRooms": "기본적으로 1:1 Room에 대한 암호화 사용", @@ -3011,7 +3009,7 @@ "Resend_verification_email": "확인 메일을 다시 보내기", "Reset": "초기화", "Reset_Connection": "연결 초기화", - "Reset_E2E_Key": "E2E 키 재설정", + "Reset_E2E_Key": "E2EE 키 재설정", "Reset_password": "비밀번호 초기화", "Reset_section_settings": "섹션 설정 초기화", "reset-other-user-e2e-key": "다른 사용자 E2E 키 재설정", @@ -4028,4 +4026,4 @@ "Enterprise": "기업", "UpgradeToGetMore_engagement-dashboard_Title": "분석(에널리틱스)", "UpgradeToGetMore_auditing_Title": "메시지 감사" -} \ No newline at end of file +} diff --git a/packages/i18n/src/locales/nl.i18n.json b/packages/i18n/src/locales/nl.i18n.json index 6781da3d4a2a..6e1944940d92 100644 --- a/packages/i18n/src/locales/nl.i18n.json +++ b/packages/i18n/src/locales/nl.i18n.json @@ -1473,13 +1473,11 @@ "Duplicated_Email_address_will_be_ignored": "Gedupliceerd e-mailadres wordt genegeerd.", "Markdown_Marked_Tables": "Schakel gemarkeerde tabellen in", "duplicated-account": "Gedupliceerd account", - "E2E Encryption": "E2E-codering", "Markdown_Parser": "Markdown Parser", "Markdown_SupportSchemesForLink": "Markdown-ondersteuningsregelingen voor Link", "Markdown_SupportSchemesForLink_Description": "Door komma's gescheiden lijst met toegestane schema's", "E2E_enable": "Schakel E2E in", "E2E_disable": "Schakel E2E uit", - "E2E_Enable_alert": "Deze functie is momenteel in bèta! Rapporteer bugs op github.com/RocketChat/Rocket.Chat/issues en let op:
- Versleutelde berichten van versleutelde kamers worden niet gevonden door zoekmachines.
- De mobiele apps ondersteunen de versleutelde berichten mogelijk niet (hier wordt aan gewerkt).
- Bots kunnen mogelijk geen versleutelde berichten zien totdat ze er ondersteuning voor hebben geïmplementeerd.
- Uploads worden niet versleuteld in deze versie.", "E2E_Enable_description": "Schakel de optie in om versleutelde groepen te maken en om groepen en directe berichten te kunnen wijzigen die moeten worden versleuteld", "E2E_Enabled": "E2E ingeschakeld", "E2E_Enabled_Default_DirectRooms": "Encryptie standaard inschakelen voor directe kamers", @@ -3504,7 +3502,7 @@ "Resend_verification_email": "Verificatie e-mail opnieuw verzenden", "Reset": "Reset", "Reset_Connection": "Verbinding resetten", - "Reset_E2E_Key": "Reset E2E-sleutel", + "Reset_E2E_Key": "Reset E2EE-sleutel", "Reset_password": "Wachtwoord opnieuw instellen", "Reset_section_settings": "Sectie naar standaard-instellingen resetten", "Reset_TOTP": "TOTP resetten", diff --git a/packages/i18n/src/locales/nn.i18n.json b/packages/i18n/src/locales/nn.i18n.json index c920f409ecfe..58daf7bf24a8 100644 --- a/packages/i18n/src/locales/nn.i18n.json +++ b/packages/i18n/src/locales/nn.i18n.json @@ -1560,12 +1560,10 @@ "Markdown_Marked_Smartypants": "Aktiver merkede Smartypants", "Duplicated_Email_address_will_be_ignored": "Duplisert e-postadresse vil bli ignorert.", "Markdown_Marked_Tables": "Aktiver merkede tabeller", - "E2E Encryption": "E2E-kryptering", "E2E_Encryption_enabled_for_room": "Ende-til-Ende-kryptering er aktivert for #{{roomName}}", "E2E_Encryption_disabled_for_room": "Ende-til-Ende-kryptering deaktivert for #{{roomName}}", "Markdown_Parser": "Markdown Parser", "Markdown_SupportSchemesForLink": "Markdown Støtteordninger for Link", - "E2E Encryption_Description": "Hold samtaler private, sørger for at bare avsender og tiltenkte mottakere kan lese dem.", "Markdown_SupportSchemesForLink_Description": "Kommaseparert liste over tillatte ordninger", "E2E_enable": "Aktiver E2E", "E2E_disable": "Deaktiver E2E", @@ -3443,7 +3441,7 @@ "Reset": "Tilbakestill", "Reset_priorities": "Tilbakestill prioriteter", "Reset_Connection": "Tilbakestill tilkobling", - "Reset_E2E_Key": "Tilbakestill E2E-nøkkel", + "Reset_E2E_Key": "Tilbakestill E2EE-nøkkel", "Reset_password": "Tilbakestilling av passord", "Reset_section_settings": "Tilbakestill til standardinnstillinger", "Reset_TOTP": "Tilbakestill TOTP", diff --git a/packages/i18n/src/locales/no.i18n.json b/packages/i18n/src/locales/no.i18n.json index 9582f1beea48..071ec7508def 100644 --- a/packages/i18n/src/locales/no.i18n.json +++ b/packages/i18n/src/locales/no.i18n.json @@ -1560,12 +1560,10 @@ "Markdown_Marked_Smartypants": "Aktiver merkede Smartypants", "Duplicated_Email_address_will_be_ignored": "Duplisert e-postadresse vil bli ignorert.", "Markdown_Marked_Tables": "Aktiver merkede tabeller", - "E2E Encryption": "E2E-kryptering", "E2E_Encryption_enabled_for_room": "Ende-til-Ende-kryptering er aktivert for #{{roomName}}", "E2E_Encryption_disabled_for_room": "Ende-til-Ende-kryptering deaktivert for #{{roomName}}", "Markdown_Parser": "Markdown Parser", "Markdown_SupportSchemesForLink": "Markdown Støtteordninger for Link", - "E2E Encryption_Description": "Hold samtaler private, sørger for at bare avsender og tiltenkte mottakere kan lese dem.", "Markdown_SupportSchemesForLink_Description": "Kommaseparert liste over tillatte ordninger", "E2E_enable": "Aktiver E2E", "E2E_disable": "Deaktiver E2E", @@ -3443,7 +3441,7 @@ "Reset": "Tilbakestill", "Reset_priorities": "Tilbakestill prioriteter", "Reset_Connection": "Tilbakestill tilkobling", - "Reset_E2E_Key": "Tilbakestill E2E-nøkkel", + "Reset_E2E_Key": "Tilbakestill E2EE-nøkkel", "Reset_password": "Tilbakestilling av passord", "Reset_section_settings": "Tilbakestill til standardinnstillinger", "Reset_TOTP": "Tilbakestill TOTP", diff --git a/packages/i18n/src/locales/pl.i18n.json b/packages/i18n/src/locales/pl.i18n.json index bb2fde5dd417..5ba5c97f18a0 100644 --- a/packages/i18n/src/locales/pl.i18n.json +++ b/packages/i18n/src/locales/pl.i18n.json @@ -1656,14 +1656,11 @@ "Duplicated_Email_address_will_be_ignored": "Zduplikowany adres e-mail zostanie zignorowany.", "Markdown_Marked_Tables": "Włącz oznaczone tablice", "duplicated-account": "Zduplikowane konto", - "E2E Encryption": "Szyfrowanie E2E", "Markdown_Parser": "Markdown Parser", "Markdown_SupportSchemesForLink": "Przecena systemy wsparcia dla Łącze", - "E2E Encryption_Description": "Zachowaj prywatność rozmów, zapewniając, że tylko nadawca i zamierzeni odbiorcy mogą je przeczytać.", "Markdown_SupportSchemesForLink_Description": "Oddzielonych przecinkami lista dozwolonych programów", "E2E_enable": "Włącz E2E", "E2E_disable": "Wyłącz E2E", - "E2E_Enable_alert": "Ta funkcja jest obecnie w wersji beta! Wszelkie błędy prosimy zgłaszać na github.com/RocketChat/Rocket.Chat/issues oraz pamiętać o:
- Zaszyfrowane wiadomości zaszyfrowanych pomieszczeń nie zostaną znalezione podczas operacji wyszukiwania.
- Aplikacje mobilne mogą nie obsługiwać zaszyfrowanych wiadomości (funkcjonalność w trakcie implementacji).
- Boty mogą nie widzieć zaszyfrowanych wiadomości, dopóki obsługa nie zostanie zaimplementowana po ich stronie.
- Przesłane pliki nie będą szyfrowane w tej wersji.", "E2E_Enable_description": "Włącz opcję tworzenia zaszyfrowanych grup i możliwość zmiany grup, oraz szyfrowania bezpośrednich wiadomości", "E2E_Enabled": "E2E włączone", "E2E_Enabled_Default_DirectRooms": "Domyślnie włączyć szyfrowanie dla Direct Rooms", @@ -3880,7 +3877,7 @@ "Resend_verification_email": "Ponownie wyślij e-mail weryfikacyjny", "Reset": "Zresetuj", "Reset_Connection": "Zresetuj połączenie", - "Reset_E2E_Key": "Resetuj klucz E2E", + "Reset_E2E_Key": "Resetuj klucz E2EE", "Reset_password": "Zresetuj hasło", "Reset_section_settings": "Zresetuj ustawienia sekcji", "Reset_TOTP": "Zresetuj TOTP", diff --git a/packages/i18n/src/locales/pt-BR.i18n.json b/packages/i18n/src/locales/pt-BR.i18n.json index 5980a6bb51c3..d74478c2fd72 100644 --- a/packages/i18n/src/locales/pt-BR.i18n.json +++ b/packages/i18n/src/locales/pt-BR.i18n.json @@ -1530,14 +1530,12 @@ "Duplicated_Email_address_will_be_ignored": "Endereço de e-mail duplicado será ignorado", "Markdown_Marked_Tables": "Ativar tabelas marcadas", "duplicated-account": "Conta duplicada", - "E2E Encryption": "Criptografia E2E", "E2E_Encryption_enabled_for_room": "Criptografia E2E habilitada para #{{roomName}}", "Markdown_Parser": "Parser de marcação", "Markdown_SupportSchemesForLink": "Esquemas de links compatíveis com marcação", "Markdown_SupportSchemesForLink_Description": "Lista de esquemas permitidos separados por vírgulas", "E2E_enable": "Ativar E2E", "E2E_disable": "Desativar E2E", - "E2E_Enable_alert": "Este recurso está atualmente em beta! Relate os erros para github.com/RocketChat/Rocket.Chat/issues e esteja ciente de que:
- As mensagens criptografadas de salas criptografadas não serão encontradas pelas operações de pesquisa.
- Os aplicativos para dispositivos móveis talvez não ofereçam suporte às mensagens critografadas (eles estão implementando).
- Os bots talvez não consigam ver as mensagens criptografadas até implementarem o suporte.
- Os uploads não serão criptografados nesta versão.", "E2E_Enable_description": "Ative a opção para criar grupos criptografados e poder alterar grupos e mensagens privadas para serem criptografadas", "E2E_Enabled": "E2E ativada", "E2E_Enabled_Default_DirectRooms": "Ativar criptografia em salas diretas por padrão", @@ -3636,7 +3634,7 @@ "Reset": "Redefinir", "Reset_priorities": "Redefinir prioridades", "Reset_Connection": "Redefinir conexão", - "Reset_E2E_Key": "Redefinir chave E2E", + "Reset_E2E_Key": "Redefinir chave E2EE", "Reset_password": "Redefinir senha", "Reset_section_settings": "Redefinir seção ao padrão", "Reset_TOTP": "Redefinir TOTP", @@ -5060,4 +5058,4 @@ "Enterprise": "Enterprise", "UpgradeToGetMore_engagement-dashboard_Title": "Analytics", "UpgradeToGetMore_auditing_Title": "Auditoria de mensagem" -} \ No newline at end of file +} diff --git a/packages/i18n/src/locales/pt.i18n.json b/packages/i18n/src/locales/pt.i18n.json index bd3c2dda458c..b5ade7fed5e7 100644 --- a/packages/i18n/src/locales/pt.i18n.json +++ b/packages/i18n/src/locales/pt.i18n.json @@ -1086,11 +1086,9 @@ "Duplicated_Email_address_will_be_ignored": "Endereço de email duplicado será ignorado", "Markdown_Marked_Tables": "Habilitar tabelas marcadas", "duplicated-account": "Conta duplicada", - "E2E Encryption": "Criptografia E2e", "Markdown_Parser": "Markdown Parser", "Markdown_SupportSchemesForLink": "Protocolos Suportados para Markdown de Links", "Markdown_SupportSchemesForLink_Description": "Lista de protocolos separados por vírgulas", - "E2E_Enable_alert": "Este recurso está atualmente em beta! Relate os erros para github.com/RocketChat/Rocket.Chat/issues e esteja ciente de:
- As mensagens criptografadas de salas criptografadas não serão encontradas pelas operações de pesquisa.
- Os aplicativos para dispositivos móveis talvez não ofereçam suporte às mensagens critografadas (eles estão implementando).
- Os bots talvez não consigam ver as mensagens criptografadas até implementarem o suporte.
- Os uploads não serão criptografados nesta versão.", "E2E_Enable_description": "Ative a opção para criar grupos criptografados e ser capaz de alterar grupos e mensagens privadas para serem criptografadas", "E2E_Enabled": "E2E activado", "E2E_Encryption_Password_Explanation": "Agora você pode criar grupos privados criptografados e mensagens diretas. Você também pode alterar os grupos privados ou DMs existentes para criptografados.

Esta é uma criptografia de ponta a ponta, logo a chave para codificar / decodificar suas mensagens não será salva no servidor. Por esse motivo, você precisa armazenar sua senha em algum lugar seguro. Será solicitada a inserção de senha em outros dispositivos nos quais deseja usar a criptografia E2E.", @@ -3176,4 +3174,4 @@ "registration.component.form.sendConfirmationEmail": "Enviar email de confirmação", "Enterprise": "Empreendimento", "UpgradeToGetMore_engagement-dashboard_Title": "Analytics" -} \ No newline at end of file +} diff --git a/packages/i18n/src/locales/ru.i18n.json b/packages/i18n/src/locales/ru.i18n.json index eea408677d54..24dfdfe266c6 100644 --- a/packages/i18n/src/locales/ru.i18n.json +++ b/packages/i18n/src/locales/ru.i18n.json @@ -1611,13 +1611,11 @@ "Duplicated_Email_address_will_be_ignored": "Дублированный адрес электронной почты будет игнорироваться.", "Markdown_Marked_Tables": "Enable Marked Tables", "duplicated-account": "Дублированный аккаунт", - "E2E Encryption": "Шифрование E2E", "Markdown_Parser": "Парсер Markdown", "Markdown_SupportSchemesForLink": "Поддерживать Markdown систему ссылок", "Markdown_SupportSchemesForLink_Description": "Разрешённые Markdown системы через запятую", "E2E_enable": "Включить E2E", "E2E_disable": "Отключить E2E", - "E2E_Enable_alert": "Эта функция в настоящее время находится в бета-версии! Сообщайте об ошибках в github.com/RocketChat/Rocket.Chat/issues и будьте в курсе:
- Зашифрованные сообщения зашифрованных комнат не будут найдены в результате операций поиска.
- мобильные приложения могут не поддерживать кодированные сообщения (они будут реализованы позже).
- Боты могут не видеть зашифрованные сообщения до тех пор, пока они не реализуют поддержку кодированных сообщений.
- Загрузка не будет зашифрована в этой версии.", "E2E_Enable_description": "Включить возможность создания зашифрованных групп и возможность изменять группы и прямые сообщения для шифрования", "E2E_Enabled": "E2E включен", "E2E_Enabled_Default_DirectRooms": "Включить шифрование для личной переписки по умолчанию", @@ -3675,7 +3673,7 @@ "Resend_verification_email": "Отправить проверочный email ещё раз", "Reset": "Восстановить", "Reset_Connection": "Сбросить соединение", - "Reset_E2E_Key": "Сбросить ключ E2E", + "Reset_E2E_Key": "Сбросить ключ E2EE", "Reset_password": "Восстановить пароль", "Reset_section_settings": "Восстановить значения по умолчанию", "Reset_TOTP": "Сброс TOTP", diff --git a/packages/i18n/src/locales/se.i18n.json b/packages/i18n/src/locales/se.i18n.json index 163c09701a33..7e05cb34c94b 100644 --- a/packages/i18n/src/locales/se.i18n.json +++ b/packages/i18n/src/locales/se.i18n.json @@ -1799,17 +1799,14 @@ "duplicated-account": "Duplicated account", "E2E_Allow_Unencrypted_Messages": "Access unencrypted content in encrypted rooms", "E2E_Allow_Unencrypted_Messages_Description": "Allow access to encrypted rooms to people without room encryption keys. They'll be able to see and send unencrypted messages.", - "E2E Encryption": "E2E Encryption", "E2E_Encryption_enabled_for_room": "End-to-end encryption enabled for #{{roomName}}", "E2E_Encryption_disabled_for_room": "End-to-end encryption disabled for #{{roomName}}", "E2EE_not_available_OTR": "This room has OTR enabled, E2E encryption cannot work with OTR.", "Markdown_Parser": "Markdown Parser", "Markdown_SupportSchemesForLink": "Markdown Support Schemes for Link", - "E2E Encryption_Description": "Keep conversations private, ensuring only the sender and intended recipients are able to read them.", "Markdown_SupportSchemesForLink_Description": "Comma-separated list of allowed schemes", "E2E_enable": "Enable E2E", "E2E_disable": "Disable E2E", - "E2E_Enable_alert": "This feature is currently in beta! Please report bugs to github.com/RocketChat/Rocket.Chat/issues and be aware of:
- Encrypted messages of encrypted rooms will not be found by search operations.
- Bots may not be able to see encrypted messages until they implement support for it.", "E2E_Enable_description": "Enable option to create encrypted groups and be able to change groups and direct messages to be encrypted", "E2E_Enabled": "E2E Enabled", "E2E_Enabled_Default_DirectRooms": "Enable encryption for Direct Rooms by default", @@ -4542,7 +4539,7 @@ "Reset": "Reset", "Reset_priorities": "Reset priorities", "Reset_Connection": "Reset Connection", - "Reset_E2E_Key": "Reset E2E Key", + "Reset_E2E_Key": "Reset E2EE Key", "Reset_password": "Reset password", "Reset_section_settings": "Restore defaults", "Reset_TOTP": "Reset TOTP", diff --git a/packages/i18n/src/locales/sv.i18n.json b/packages/i18n/src/locales/sv.i18n.json index 9e72eefbd36a..f17370fedb9c 100644 --- a/packages/i18n/src/locales/sv.i18n.json +++ b/packages/i18n/src/locales/sv.i18n.json @@ -1672,14 +1672,11 @@ "Duplicated_Email_address_will_be_ignored": "Duplicerade e-postadresser kommer ignoreras", "Markdown_Marked_Tables": "Aktivera markerade tabeller", "duplicated-account": "Duplicerat konto", - "E2E Encryption": "E2E-kryptering", "Markdown_Parser": "Markdown Parser", "Markdown_SupportSchemesForLink": "Stödsystem för Markdown för länkar", - "E2E Encryption_Description": "Håll konversationerna privata så att endast avsändaren och de tilltänkta mottagarna kan läsa dem.", "Markdown_SupportSchemesForLink_Description": "Kommaseparerad lista över tillåtna system", "E2E_enable": "Aktivera E2E", "E2E_disable": "Inaktivera E2E", - "E2E_Enable_alert": "Den här funktionen är för närvarande i en betaversion. Rapportera fel till github.com/RocketChat/Rocket.Chat/issues och tänk på följande:
- Krypterade meddelanden för krypterade rum kan inte hittas med hjälp av sökåtgärder.
- Mobilapparna kanske inte har stöd för krypterade meddelanden (det här håller på att införas).
- Bottar kanske inte kan se krypterade meddelanden förrän stöd har införts för det.
- Uppladdade filer krypteras inte i den här versionen.", "E2E_Enable_description": "Aktivera alternativet för att skapa krypterade grupper och kryptera befintliga grupper och direktmeddelanden", "E2E_Enabled": "E2E har aktiverats", "E2E_Enabled_Default_DirectRooms": "Aktivera kryptering för direkta rum som standard", diff --git a/packages/i18n/src/locales/tr.i18n.json b/packages/i18n/src/locales/tr.i18n.json index 86385ed56b37..dd27825eca25 100644 --- a/packages/i18n/src/locales/tr.i18n.json +++ b/packages/i18n/src/locales/tr.i18n.json @@ -1093,7 +1093,6 @@ "Duplicated_Email_address_will_be_ignored": "İkinci defa girilen e-posta adresi dikkate alınmayacak.", "Markdown_Marked_Tables": "İşaretli Tabloları Etkinleştir", "duplicated-account": "Yinelenen hesap", - "E2E Encryption": "Uçtan Uca Şifreleme", "Markdown_Parser": "Markdown Ayrıştırıcı", "Markdown_SupportSchemesForLink": "Bağlantı için Markdown Destek Şemaları", "Markdown_SupportSchemesForLink_Description": "İzin verilen şemaların virgülle ayrılmış listesi", diff --git a/packages/i18n/src/locales/uk.i18n.json b/packages/i18n/src/locales/uk.i18n.json index 40f4f74bf8ae..1a17fed4cc75 100644 --- a/packages/i18n/src/locales/uk.i18n.json +++ b/packages/i18n/src/locales/uk.i18n.json @@ -1184,11 +1184,9 @@ "Duplicated_Email_address_will_be_ignored": "Дубльовані електронні адреси будуть проігноровані.", "Markdown_Marked_Tables": "Увімкнути позначені таблиці", "duplicated-account": "Дублювання облікового запису", - "E2E Encryption": "Шифрування E2E", "Markdown_Parser": "Аналізатор зі зниженням курсу", "Markdown_SupportSchemesForLink": "Markdown Схеми підтримки для Link", "Markdown_SupportSchemesForLink_Description": "Розділених комами список дозволених схем", - "E2E_Enable_alert": "Ця функція наразі знаходиться в бета-версії!. Повідомляйте про помилки на github.com/RocketChat/Rocket.Chat/isissue і будьте в курсі:
- Зашифровані повідомлення зашифрованих кімнат не будуть знайдені пошуковими запитами.
- Мобільні програми можуть не підтримувати кодовані повідомлення (вони працюють над цим)
- Боти можуть не бачити зашифровані повідомлення, поки вони не реалізують підтримку
- Завантаження не будуть зашифровані у цій версії.", "E2E_Enable_description": "Увімкнути опцію для створення зашифрованих груп і мати можливість робити групи та особисті повідомлення зашифровании", "E2E_Enabled": "E2E увімкнено", "E2E_Enabled_Default_PrivateRooms": "Увімкніть шифрування для приватних Room по замовчуванню", @@ -3351,4 +3349,4 @@ "Enterprise": "Підприємство", "UpgradeToGetMore_engagement-dashboard_Title": "Аналітика", "UpgradeToGetMore_auditing_Title": "Аудит повідомлень" -} \ No newline at end of file +} diff --git a/packages/i18n/src/locales/zh-TW.i18n.json b/packages/i18n/src/locales/zh-TW.i18n.json index 6dc853cb69e6..883cc09de12c 100644 --- a/packages/i18n/src/locales/zh-TW.i18n.json +++ b/packages/i18n/src/locales/zh-TW.i18n.json @@ -1456,13 +1456,11 @@ "Duplicated_Email_address_will_be_ignored": "已複製的電子郵件位址將會忽略。", "Markdown_Marked_Tables": "啟用標記表", "duplicated-account": "已複製帳號", - "E2E Encryption": "E2E 加密", "Markdown_Parser": "降價解析器", "Markdown_SupportSchemesForLink": "Markdown 支援連結協議", "Markdown_SupportSchemesForLink_Description": "以逗號分隔列出允許的協議", "E2E_enable": "E2E 已啟用", "E2E_disable": "E2E 已停用", - "E2E_Enable_alert": "這個功能目前還在測試! 請回報臭蟲到 github.com/RocketChat/Rocket.Chat/issues 且要知道:
- 在已加密的房間中加密的訊息是沒辦法搜尋得到的。
- 手機應用程式可能不支援加密訊息 (他們實施中)。
- 機器人可能也無法看到已加密訊息直到他們支援這個。
- 這個版本無法加密檔案。", "E2E_Enable_description": "啟用選項來建立加密群組就可以變更群組和直接訊息加密", "E2E_Enabled": "E2E 已啟用", "E2E_Enabled_Default_DirectRooms": "預設情況下為 Direct Rooms 啟用加密", @@ -3393,7 +3391,7 @@ "Resend_verification_email": "重新發送驗證郵件", "Reset": "重設", "Reset_Connection": "重設連接", - "Reset_E2E_Key": "重設 E2E 金鑰", + "Reset_E2E_Key": "重設 E2EE 金鑰", "Reset_password": "重設密碼", "Reset_section_settings": "重設部分設定", "Reset_TOTP": "重設 TOTP", @@ -4576,4 +4574,4 @@ "Enterprise": "企業", "UpgradeToGetMore_engagement-dashboard_Title": "分析", "UpgradeToGetMore_auditing_Title": "訊息稽核" -} \ No newline at end of file +} diff --git a/packages/i18n/src/locales/zh.i18n.json b/packages/i18n/src/locales/zh.i18n.json index 31ba47b5f279..e15ca26a86d1 100644 --- a/packages/i18n/src/locales/zh.i18n.json +++ b/packages/i18n/src/locales/zh.i18n.json @@ -1322,13 +1322,11 @@ "Duplicated_Email_address_will_be_ignored": "重复的电子邮件地址将被忽略。", "Markdown_Marked_Tables": "启用标记表", "duplicated-account": "重复的账号", - "E2E Encryption": "端到端(E2E)加密", "Markdown_Parser": "降价解析器", "Markdown_SupportSchemesForLink": "Markdown 支持的链接协议", "Markdown_SupportSchemesForLink_Description": "由英文逗号分割的协议列表", "E2E_enable": "启用端到端加密", "E2E_disable": "禁用端到端加密", - "E2E_Enable_alert": "此功能目前处于测试阶段!请将错误报告给 github.com/RocketChat/Rocket.Chat/issues 并注意:
- 搜索操作无法找到加密聊天室的加密消息。
- 移动应用暂时不支持加密消息(他们正在开发)。
- 机器人可能无法看到加密消息,直到实现对它的支持。
- 此版本中不会加密上传文件。", "E2E_Enable_description": "启用选项以创建加密组,并能够更改要加密的组和私聊消息。", "E2E_Enabled": "端到端已启用", "E2E_Enabled_Default_DirectRooms": "默认为私聊启用加密", @@ -4135,4 +4133,4 @@ "Enterprise": "企业", "UpgradeToGetMore_engagement-dashboard_Title": "分析", "UpgradeToGetMore_auditing_Title": "消息审计" -} \ No newline at end of file +} From 2806cb5d3ef5c9485eea6ab2a80189be027e092a Mon Sep 17 00:00:00 2001 From: Hugo Costa Date: Fri, 18 Oct 2024 13:46:00 -0300 Subject: [PATCH 09/25] feat: E2EE room key reset modal (#33503) Co-authored-by: Kevin Aleman <11577696+KevLehman@users.noreply.github.com> --- .changeset/e2ee-modal-keys.md | 8 ++ .../app/e2e/client/rocketchat.e2e.room.js | 36 ++++-- apps/meteor/app/e2e/client/rocketchat.e2e.ts | 6 - .../components/GenericModal/GenericModal.tsx | 6 +- .../roomActions/useE2EERoomAction.spec.ts | 22 ++-- .../hooks/roomActions/useE2EERoomAction.ts | 59 ++++++++- .../room/hooks/useE2EEResetRoomKey.spec.ts | 114 ++++++++++++++++++ .../views/room/hooks/useE2EEResetRoomKey.ts | 35 ++++++ .../E2EEModals/BaseDisableE2EEModal.tsx | 49 ++++++++ .../modals/E2EEModals/DisableE2EEModal.tsx | 54 +++++++++ .../modals/E2EEModals/EnableE2EEModal.tsx | 33 +++++ .../modals/E2EEModals/ResetKeysE2EEModal.tsx | 62 ++++++++++ apps/meteor/tests/e2e/e2e-encryption.spec.ts | 14 +++ packages/i18n/src/locales/en.i18n.json | 12 ++ 14 files changed, 482 insertions(+), 28 deletions(-) create mode 100644 .changeset/e2ee-modal-keys.md create mode 100644 apps/meteor/client/views/room/hooks/useE2EEResetRoomKey.spec.ts create mode 100644 apps/meteor/client/views/room/hooks/useE2EEResetRoomKey.ts create mode 100644 apps/meteor/client/views/room/modals/E2EEModals/BaseDisableE2EEModal.tsx create mode 100644 apps/meteor/client/views/room/modals/E2EEModals/DisableE2EEModal.tsx create mode 100644 apps/meteor/client/views/room/modals/E2EEModals/EnableE2EEModal.tsx create mode 100644 apps/meteor/client/views/room/modals/E2EEModals/ResetKeysE2EEModal.tsx diff --git a/.changeset/e2ee-modal-keys.md b/.changeset/e2ee-modal-keys.md new file mode 100644 index 000000000000..bd324347f88c --- /dev/null +++ b/.changeset/e2ee-modal-keys.md @@ -0,0 +1,8 @@ +--- +"@rocket.chat/meteor": major +"@rocket.chat/i18n": patch +--- + +Adds modal confirmation to enable and disable End-to-end encryption + +Adds a reset room key option to the modal that disables End-to-end encryption, this is useful when all the members of a room lose their room E2EE keys diff --git a/apps/meteor/app/e2e/client/rocketchat.e2e.room.js b/apps/meteor/app/e2e/client/rocketchat.e2e.room.js index d08719d95506..4c9de837dce0 100644 --- a/apps/meteor/app/e2e/client/rocketchat.e2e.room.js +++ b/apps/meteor/app/e2e/client/rocketchat.e2e.room.js @@ -309,7 +309,7 @@ export class E2ERoom extends Emitter { try { const room = ChatRoom.findOne({ _id: this.roomId }); // Only room creator can set keys for room - if (!room.e2eKeyId && room.u._id === this.userId) { + if (!room.e2eKeyId && this.userShouldCreateKeys(room)) { this.setState(E2ERoomState.CREATING_KEYS); await this.createGroupKey(); this.setState(E2ERoomState.READY); @@ -325,6 +325,15 @@ export class E2ERoom extends Emitter { } } + userShouldCreateKeys(room) { + // On DMs, we'll allow any user to set the keys + if (room.t === 'd') { + return true; + } + + return room.u._id === this.userId; + } + isSupportedRoomType(type) { return roomCoordinator.getRoomDirectives(type).allowRoomSettingChange({}, RoomSettingsEnum.E2E); } @@ -644,8 +653,16 @@ export class E2ERoom extends Emitter { }; } + async doDecrypt(vector, key, cipherText) { + const result = await decryptAES(vector, key, cipherText); + return EJSON.parse(new TextDecoder('UTF-8').decode(new Uint8Array(result))); + } + async decrypt(message) { const keyID = message.slice(0, 12); + message = message.slice(12); + + const [vector, cipherText] = splitVectorAndEcryptedData(Base64.decode(message)); let oldKey = ''; if (keyID !== this.keyID) { @@ -653,20 +670,21 @@ export class E2ERoom extends Emitter { // Messages already contain a keyID stored with them // That means that if we cannot find a keyID for the key the message has preppended to // The message is indecipherable. + // In these cases, we'll give a last shot using the current session key, which may not work + // but will be enough to help with some mobile issues. if (!oldRoomKey) { - this.error(`Message is indecipherable. Message KeyID ${keyID} not found in old room keys`); - return { msg: t('E2E_indecipherable') }; + try { + return await this.doDecrypt(vector, this.groupSessionKey, cipherText); + } catch (error) { + this.error('Error decrypting message: ', error, message); + return { msg: t('E2E_indecipherable') }; + } } oldKey = oldRoomKey.E2EKey; } - message = message.slice(12); - - const [vector, cipherText] = splitVectorAndEcryptedData(Base64.decode(message)); - try { - const result = await decryptAES(vector, oldKey || this.groupSessionKey, cipherText); - return EJSON.parse(new TextDecoder('UTF-8').decode(new Uint8Array(result))); + return await this.doDecrypt(vector, oldKey || this.groupSessionKey, cipherText); } catch (error) { this.error('Error decrypting message: ', error, message); return { msg: t('E2E_Key_Error') }; diff --git a/apps/meteor/app/e2e/client/rocketchat.e2e.ts b/apps/meteor/app/e2e/client/rocketchat.e2e.ts index fa1cda643902..3b2fd01621e4 100644 --- a/apps/meteor/app/e2e/client/rocketchat.e2e.ts +++ b/apps/meteor/app/e2e/client/rocketchat.e2e.ts @@ -139,8 +139,6 @@ class E2E extends Emitter { this.log('decryptSubscriptions'); await this.decryptSubscriptions(); this.log('decryptSubscriptions -> Done'); - await this.initiateDecryptingPendingMessages(); - this.log('DecryptingPendingMessages -> Done'); await this.initiateKeyDistribution(); this.log('initiateKeyDistribution -> Done'); this.observeSubscriptions(); @@ -332,10 +330,6 @@ class E2E extends Emitter { Object.keys(this.instancesByRoomId).map((key) => this.instancesByRoomId[key].handshake()); } - async initiateDecryptingPendingMessages() { - await Promise.all(Object.keys(this.instancesByRoomId).map((key) => this.instancesByRoomId[key].decryptPendingMessages())); - } - openSaveE2EEPasswordModal(randomPassword: string) { imperativeModal.open({ component: SaveE2EPasswordModal, diff --git a/apps/meteor/client/components/GenericModal/GenericModal.tsx b/apps/meteor/client/components/GenericModal/GenericModal.tsx index d91e3c066007..409855e8c0ea 100644 --- a/apps/meteor/client/components/GenericModal/GenericModal.tsx +++ b/apps/meteor/client/components/GenericModal/GenericModal.tsx @@ -21,6 +21,7 @@ type GenericModalProps = RequiredModalProps & { tagline?: ReactNode; onCancel?: () => Promise | void; onClose?: () => Promise | void; + onDismiss?: () => Promise | void; annotation?: ReactNode; } & Omit, 'title'>; @@ -67,6 +68,7 @@ const GenericModal = ({ icon, onCancel, onClose = onCancel, + onDismiss = onClose, onConfirm, dontAskAgain, confirmDisabled, @@ -98,9 +100,9 @@ const GenericModal = ({ useEffect( () => () => { if (!dismissedRef.current) return; - onClose?.(); + onDismiss?.(); }, - [onClose], + [onDismiss], ); return ( diff --git a/apps/meteor/client/hooks/roomActions/useE2EERoomAction.spec.ts b/apps/meteor/client/hooks/roomActions/useE2EERoomAction.spec.ts index e04e25f574e6..e5b98490f1c4 100644 --- a/apps/meteor/client/hooks/roomActions/useE2EERoomAction.spec.ts +++ b/apps/meteor/client/hooks/roomActions/useE2EERoomAction.spec.ts @@ -4,6 +4,7 @@ import { act, renderHook, waitFor } from '@testing-library/react'; import { E2EEState } from '../../../app/e2e/client/E2EEState'; import { e2e } from '../../../app/e2e/client/rocketchat.e2e'; import { OtrRoomState } from '../../../app/otr/lib/OtrRoomState'; +import { imperativeModal } from '../../lib/imperativeModal'; import { dispatchToastMessage } from '../../lib/toast'; import { useRoom, useRoomSubscription } from '../../views/room/contexts/RoomContext'; import { useE2EEState } from '../../views/room/hooks/useE2EEState'; @@ -20,6 +21,13 @@ jest.mock('../../lib/toast', () => ({ dispatchToastMessage: jest.fn(), })); +jest.mock('../../lib/imperativeModal', () => ({ + imperativeModal: { + open: jest.fn(), + close: jest.fn(), + }, +})); + jest.mock('../../views/room/contexts/RoomContext', () => ({ useRoom: jest.fn(), useRoomSubscription: jest.fn(), @@ -39,6 +47,10 @@ jest.mock('../../views/room/hooks/useE2EEState', () => ({ useE2EEState: jest.fn(), })); +jest.mock('../../views/room/hooks/useE2EERoomState', () => ({ + useE2EERoomState: jest.fn(), +})); + jest.mock('react-i18next', () => ({ useTranslation: () => ({ t: (key: string) => key, @@ -105,20 +117,14 @@ describe('useE2EERoomAction', () => { await waitFor(() => expect(dispatchToastMessage).toHaveBeenCalledWith({ type: 'error', message: 'E2EE_not_available_OTR' })); }); - it('should dispatch success toast message when encryption is enabled', async () => { + it('should open Enable E2EE confirmation modal', async () => { (useOTR as jest.Mock).mockReturnValue({ otrState: OtrRoomState.NOT_STARTED }); const { result } = renderHook(() => useE2EERoomAction(), { legacyRoot: true }); - act(() => { result?.current?.action?.(); }); - await waitFor(() => - expect(dispatchToastMessage).toHaveBeenCalledWith({ - type: 'success', - message: 'E2E_Encryption_enabled_for_room', - }), - ); + await waitFor(() => expect(imperativeModal.open).toHaveBeenCalledTimes(1)); }); }); diff --git a/apps/meteor/client/hooks/roomActions/useE2EERoomAction.ts b/apps/meteor/client/hooks/roomActions/useE2EERoomAction.ts index bed73ab45c6b..fc76d2b9a470 100644 --- a/apps/meteor/client/hooks/roomActions/useE2EERoomAction.ts +++ b/apps/meteor/client/hooks/roomActions/useE2EERoomAction.ts @@ -5,11 +5,17 @@ import { useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { E2EEState } from '../../../app/e2e/client/E2EEState'; +import { E2ERoomState } from '../../../app/e2e/client/E2ERoomState'; import { OtrRoomState } from '../../../app/otr/lib/OtrRoomState'; +import { getRoomTypeTranslation } from '../../lib/getRoomTypeTranslation'; +import { imperativeModal } from '../../lib/imperativeModal'; import { dispatchToastMessage } from '../../lib/toast'; import { useRoom, useRoomSubscription } from '../../views/room/contexts/RoomContext'; import type { RoomToolboxActionConfig } from '../../views/room/contexts/RoomToolboxContext'; +import { useE2EERoomState } from '../../views/room/hooks/useE2EERoomState'; import { useE2EEState } from '../../views/room/hooks/useE2EEState'; +import BaseDisableE2EEModal from '../../views/room/modals/E2EEModals/BaseDisableE2EEModal'; +import EnableE2EEModal from '../../views/room/modals/E2EEModals/EnableE2EEModal'; import { useOTR } from '../useOTR'; export const useE2EERoomAction = () => { @@ -17,6 +23,7 @@ export const useE2EERoomAction = () => { const room = useRoom(); const subscription = useRoomSubscription(); const e2eeState = useE2EEState(); + const e2eeRoomState = useE2EERoomState(room._id); const isE2EEReady = e2eeState === E2EEState.READY || e2eeState === E2EEState.SAVE_PASSWORD; const readyToEncrypt = isE2EEReady || room.encrypted; const permittedToToggleEncryption = usePermission('toggle-room-e2e-encryption', room._id); @@ -26,8 +33,30 @@ export const useE2EERoomAction = () => { const { t } = useTranslation(); const { otrState } = useOTR(); + const isE2EERoomNotReady = () => { + if ( + e2eeRoomState === E2ERoomState.NO_PASSWORD_SET || + e2eeRoomState === E2ERoomState.NOT_STARTED || + e2eeRoomState === E2ERoomState.DISABLED || + e2eeRoomState === E2ERoomState.ERROR || + e2eeRoomState === E2ERoomState.WAITING_KEYS + ) { + return true; + } + + return false; + }; + + const enabledOnRoom = !!room.encrypted; + + const roomType = useMemo(() => getRoomTypeTranslation(room)?.toLowerCase(), [room]); + + const roomId = room._id; + const toggleE2E = useEndpoint('POST', '/v1/rooms.saveRoomSettings'); + const canResetRoomKey = enabled && isE2EEReady && (room.t === 'd' || permittedToToggleEncryption) && isE2EERoomNotReady(); + const action = useEffectEvent(async () => { if (otrState === OtrRoomState.ESTABLISHED || otrState === OtrRoomState.ESTABLISHING || otrState === OtrRoomState.REQUESTED) { dispatchToastMessage({ type: 'error', message: t('E2EE_not_available_OTR') }); @@ -35,11 +64,37 @@ export const useE2EERoomAction = () => { return; } + if (enabledOnRoom) { + imperativeModal.open({ + component: BaseDisableE2EEModal, + props: { + onClose: imperativeModal.close, + onConfirm: handleToogleE2E, + roomType, + roomId, + canResetRoomKey, + }, + }); + } else { + imperativeModal.open({ + component: EnableE2EEModal, + props: { + onClose: imperativeModal.close, + onConfirm: handleToogleE2E, + roomType, + }, + }); + } + }); + + const handleToogleE2E = async () => { const { success } = await toggleE2E({ rid: room._id, encrypted: !room.encrypted }); if (!success) { return; } + imperativeModal.close(); + dispatchToastMessage({ type: 'success', message: room.encrypted @@ -50,9 +105,7 @@ export const useE2EERoomAction = () => { if (subscription?.autoTranslate) { dispatchToastMessage({ type: 'success', message: t('AutoTranslate_Disabled_for_room', { roomName: room.name }) }); } - }); - - const enabledOnRoom = !!room.encrypted; + }; return useMemo((): RoomToolboxActionConfig | undefined => { if (!enabled || !permitted) { diff --git a/apps/meteor/client/views/room/hooks/useE2EEResetRoomKey.spec.ts b/apps/meteor/client/views/room/hooks/useE2EEResetRoomKey.spec.ts new file mode 100644 index 000000000000..44fb054ee219 --- /dev/null +++ b/apps/meteor/client/views/room/hooks/useE2EEResetRoomKey.spec.ts @@ -0,0 +1,114 @@ +import { mockAppRoot } from '@rocket.chat/mock-providers'; +import { renderHook, waitFor } from '@testing-library/react'; + +import { e2e } from '../../../../app/e2e/client'; +import { useE2EEResetRoomKey } from './useE2EEResetRoomKey'; + +jest.mock('../../../../app/e2e/client', () => ({ + e2e: { + getInstanceByRoomId: jest.fn(), + }, +})); + +describe('useE2EEResetRoomKey', () => { + const e2eResetRoomKeyMock = jest.fn().mockResolvedValue({ + e2eKeyId: 'E2E_KEY_ID', + e2eKey: 'E2E_KEY', + }); + const resetRoomKeyMock = jest.fn(); + const roomId = 'ROOM_ID'; + + afterEach(() => { + jest.clearAllMocks(); + }); + + beforeEach(() => { + (e2e.getInstanceByRoomId as jest.Mock).mockImplementation(() => ({ + resetRoomKey: e2eResetRoomKeyMock, + })); + }); + + it('should call resetRoomKey endpoint with correct params', async () => { + const { result } = renderHook(() => useE2EEResetRoomKey(), { + legacyRoot: true, + wrapper: mockAppRoot().withEndpoint('POST', '/v1/e2e.resetRoomKey', resetRoomKeyMock).build(), + }); + + await waitFor(() => result.current.mutate({ roomId })); + + expect(e2e.getInstanceByRoomId).toHaveBeenCalledTimes(1); + expect(e2e.getInstanceByRoomId).toHaveBeenCalledWith('ROOM_ID'); + expect(e2eResetRoomKeyMock).toHaveBeenCalledTimes(1); + + expect(resetRoomKeyMock).toHaveBeenCalledWith({ + rid: roomId, + e2eKeyId: 'E2E_KEY_ID', + e2eKey: 'E2E_KEY', + }); + + await waitFor(() => expect(result.current.status).toBe('success')); + }); + + it('should return an errror if e2e.getInstanceByRoomId() does not return correct params', async () => { + (e2e.getInstanceByRoomId as jest.Mock).mockReturnValue(null); + + const { result } = renderHook(() => useE2EEResetRoomKey(), { + legacyRoot: true, + wrapper: mockAppRoot().withEndpoint('POST', '/v1/e2e.resetRoomKey', resetRoomKeyMock).build(), + }); + + await waitFor(() => result.current.mutate({ roomId })); + + expect(e2e.getInstanceByRoomId).toHaveBeenCalledTimes(1); + expect(e2e.getInstanceByRoomId).toHaveBeenCalledWith('ROOM_ID'); + expect(e2eResetRoomKeyMock).toHaveBeenCalledTimes(0); + + await waitFor(() => expect(result.current.status).toBe('error')); + }); + + it('should return an errror if e2e.resetRoomKey() does not return correct params', async () => { + const e2eResetRoomKeyMock = jest.fn().mockResolvedValue(null); + const roomId = 'ROOM_ID'; + + (e2e.getInstanceByRoomId as jest.Mock).mockImplementation(() => ({ + resetRoomKey: e2eResetRoomKeyMock, + })); + + const { result } = renderHook(() => useE2EEResetRoomKey(), { + legacyRoot: true, + wrapper: mockAppRoot().withEndpoint('POST', '/v1/e2e.resetRoomKey', resetRoomKeyMock).build(), + }); + + await waitFor(() => result.current.mutate({ roomId })); + + expect(e2e.getInstanceByRoomId).toHaveBeenCalledTimes(1); + expect(e2e.getInstanceByRoomId).toHaveBeenCalledWith('ROOM_ID'); + expect(e2eResetRoomKeyMock).toHaveBeenCalledTimes(1); + + expect(resetRoomKeyMock).toHaveBeenCalledTimes(0); + + await waitFor(() => expect(result.current.status).toBe('error')); + }); + + it('should return an error if resetRoomKey does not resolve', async () => { + resetRoomKeyMock.mockRejectedValue(new Error('error-e2e-key-reset-in-progress')); + const { result } = renderHook(() => useE2EEResetRoomKey(), { + legacyRoot: true, + wrapper: mockAppRoot().withEndpoint('POST', '/v1/e2e.resetRoomKey', resetRoomKeyMock).build(), + }); + + await waitFor(() => result.current.mutate({ roomId })); + + expect(e2e.getInstanceByRoomId).toHaveBeenCalledTimes(1); + expect(e2e.getInstanceByRoomId).toHaveBeenCalledWith('ROOM_ID'); + expect(e2eResetRoomKeyMock).toHaveBeenCalledTimes(1); + + expect(resetRoomKeyMock).toHaveBeenCalledWith({ + rid: roomId, + e2eKeyId: 'E2E_KEY_ID', + e2eKey: 'E2E_KEY', + }); + + await waitFor(() => expect(result.current.status).toBe('error')); + }); +}); diff --git a/apps/meteor/client/views/room/hooks/useE2EEResetRoomKey.ts b/apps/meteor/client/views/room/hooks/useE2EEResetRoomKey.ts new file mode 100644 index 000000000000..a8c93cab2a74 --- /dev/null +++ b/apps/meteor/client/views/room/hooks/useE2EEResetRoomKey.ts @@ -0,0 +1,35 @@ +import type { RoomID } from '@rocket.chat/core-typings'; +import { useEndpoint } from '@rocket.chat/ui-contexts'; +import type { UseMutationOptions, UseMutationResult } from '@tanstack/react-query'; +import { useMutation } from '@tanstack/react-query'; + +import { e2e } from '../../../../app/e2e/client'; + +type UseE2EEResetRoomKeyVariables = { + roomId: RoomID; +}; + +export const useE2EEResetRoomKey = ( + options?: Omit, 'mutationFn'>, +): UseMutationResult => { + const resetRoomKey = useEndpoint('POST', '/v1/e2e.resetRoomKey'); + + return useMutation(async ({ roomId }) => { + const e2eRoom = await e2e.getInstanceByRoomId(roomId); + if (!e2eRoom) { + throw new Error('Cannot reset room key'); + } + + const { e2eKey, e2eKeyId } = (await e2eRoom.resetRoomKey()) ?? {}; + + if (!e2eKey || !e2eKeyId) { + throw new Error('Cannot reset room key'); + } + + try { + await resetRoomKey({ rid: roomId, e2eKeyId, e2eKey }); + } catch (error) { + throw error; + } + }, options); +}; diff --git a/apps/meteor/client/views/room/modals/E2EEModals/BaseDisableE2EEModal.tsx b/apps/meteor/client/views/room/modals/E2EEModals/BaseDisableE2EEModal.tsx new file mode 100644 index 000000000000..c64002d38ec4 --- /dev/null +++ b/apps/meteor/client/views/room/modals/E2EEModals/BaseDisableE2EEModal.tsx @@ -0,0 +1,49 @@ +import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import type { ReactElement } from 'react'; +import React, { useState } from 'react'; + +import DisableE2EEModal from './DisableE2EEModal'; +import ResetKeysE2EEModal from './ResetKeysE2EEModal'; + +const STEPS = { + DISABLE_E2EE: 'DISABLE_E2EE', + RESET_ROOM_KEY: 'RESET_ROOM_KEY', +}; + +type BaseDisableE2EEModalProps = { + onConfirm: () => void; + onClose: () => void; + roomType: string; + roomId: string; + canResetRoomKey: boolean; +}; + +const BaseDisableE2EEModal = ({ + onConfirm, + onClose, + roomType, + roomId, + canResetRoomKey, +}: BaseDisableE2EEModalProps): ReactElement | null => { + const [step, setStep] = useState(STEPS.DISABLE_E2EE); + + const onResetRoomKey = useEffectEvent(() => { + setStep(STEPS.RESET_ROOM_KEY); + }); + + if (step === STEPS.RESET_ROOM_KEY && canResetRoomKey) { + return ; + } + + return ( + + ); +}; + +export default BaseDisableE2EEModal; diff --git a/apps/meteor/client/views/room/modals/E2EEModals/DisableE2EEModal.tsx b/apps/meteor/client/views/room/modals/E2EEModals/DisableE2EEModal.tsx new file mode 100644 index 000000000000..e3b231fa9476 --- /dev/null +++ b/apps/meteor/client/views/room/modals/E2EEModals/DisableE2EEModal.tsx @@ -0,0 +1,54 @@ +import { Accordion, Box, Button } from '@rocket.chat/fuselage'; +import type { ReactElement } from 'react'; +import React from 'react'; +import { Trans, useTranslation } from 'react-i18next'; + +import GenericModal from '../../../../components/GenericModal'; + +type DisableE2EEModalProps = { + onConfirm: () => void; + onCancel: () => void; + roomType: string; + canResetRoomKey: boolean; + onResetRoomKey: () => void; +}; + +const DisableE2EEModal = ({ onConfirm, onCancel, roomType, canResetRoomKey, onResetRoomKey }: DisableE2EEModalProps): ReactElement => { + const { t } = useTranslation(); + + return ( + undefined} + > + + + + + {canResetRoomKey && ( + <> + + {t('E2E_disable_encryption_reset_keys_description')} + + + + + {t('E2E_reset_encryption_keys_description')} + + + + + + )} + + ); +}; + +export default DisableE2EEModal; diff --git a/apps/meteor/client/views/room/modals/E2EEModals/EnableE2EEModal.tsx b/apps/meteor/client/views/room/modals/E2EEModals/EnableE2EEModal.tsx new file mode 100644 index 000000000000..37de20c76120 --- /dev/null +++ b/apps/meteor/client/views/room/modals/E2EEModals/EnableE2EEModal.tsx @@ -0,0 +1,33 @@ +import { Box } from '@rocket.chat/fuselage'; +import type { ReactElement } from 'react'; +import React from 'react'; +import { useTranslation } from 'react-i18next'; + +import GenericModal from '../../../../components/GenericModal'; + +type EnableE2EEModalProps = { + onConfirm: () => void; + onClose: () => void; + roomType: string; +}; + +const EnableE2EEModal = ({ onConfirm, onClose, roomType }: EnableE2EEModalProps): ReactElement => { + const { t } = useTranslation(); + + return ( + + + {t('E2E_enable_encryption_description', { roomType })} + + + ); +}; + +export default EnableE2EEModal; diff --git a/apps/meteor/client/views/room/modals/E2EEModals/ResetKeysE2EEModal.tsx b/apps/meteor/client/views/room/modals/E2EEModals/ResetKeysE2EEModal.tsx new file mode 100644 index 000000000000..41e9ccbee341 --- /dev/null +++ b/apps/meteor/client/views/room/modals/E2EEModals/ResetKeysE2EEModal.tsx @@ -0,0 +1,62 @@ +import { Box, Modal } from '@rocket.chat/fuselage'; +import { ExternalLink } from '@rocket.chat/ui-client'; +import type { ReactElement } from 'react'; +import React from 'react'; +import { Trans, useTranslation } from 'react-i18next'; + +import GenericModal from '../../../../components/GenericModal'; +import { dispatchToastMessage } from '../../../../lib/toast'; +import { useE2EEResetRoomKey } from '../../hooks/useE2EEResetRoomKey'; + +const E2EE_RESET_KEY_LINK = 'https://go.rocket.chat/i/e2ee-guide'; + +type ResetKeysE2EEModalProps = { + roomType: string; + roomId: string; + onCancel: () => void; +}; + +const ResetKeysE2EEModal = ({ roomType, roomId, onCancel }: ResetKeysE2EEModalProps): ReactElement => { + const { t } = useTranslation(); + const resetRoomKeyMutation = useE2EEResetRoomKey(); + + const handleResetRoomKey = () => { + resetRoomKeyMutation.mutate( + { roomId }, + { + onSuccess: () => { + dispatchToastMessage({ type: 'success', message: t('E2E_reset_encryption_keys_success') }); + }, + onError: () => { + dispatchToastMessage({ type: 'error', message: t('E2E_reset_encryption_keys_error') }); + }, + onSettled: () => { + onCancel(); + }, + }, + ); + }; + + return ( + } + title={t('E2E_reset_encryption_keys')} + variant='danger' + confirmText={t('E2E_reset_encryption_keys')} + dontAskAgain={{t('This_action_cannot_be_undone')}} + onCancel={onCancel} + onConfirm={handleResetRoomKey} + onDismiss={() => undefined} + > + + + Resetting E2EE keys is only recommend if no {roomType} member has a valid key to regain access to the previously encrypted + content. All members may lose access to previously encrypted content. + Learn more about resetting encryption keys. Proceed with caution. + + + + ); +}; + +export default ResetKeysE2EEModal; diff --git a/apps/meteor/tests/e2e/e2e-encryption.spec.ts b/apps/meteor/tests/e2e/e2e-encryption.spec.ts index 0dc92a5274c1..d92ddad1d5f3 100644 --- a/apps/meteor/tests/e2e/e2e-encryption.spec.ts +++ b/apps/meteor/tests/e2e/e2e-encryption.spec.ts @@ -160,6 +160,8 @@ test.describe.serial('e2e-encryption', () => { await expect(poHomeChannel.tabs.btnDisableE2E).toBeVisible(); await poHomeChannel.tabs.btnDisableE2E.click({ force: true }); + await expect(page.getByRole('dialog', { name: 'Disable encryption' })).toBeVisible(); + await page.getByRole('button', { name: 'Disable encryption' }).click(); await poHomeChannel.dismissToast(); await page.waitForTimeout(1000); @@ -171,6 +173,8 @@ test.describe.serial('e2e-encryption', () => { await poHomeChannel.tabs.kebab.click({ force: true }); await expect(poHomeChannel.tabs.btnEnableE2E).toBeVisible(); await poHomeChannel.tabs.btnEnableE2E.click({ force: true }); + await expect(page.getByRole('dialog', { name: 'Enable encryption' })).toBeVisible(); + await page.getByRole('button', { name: 'Enable encryption' }).click(); await poHomeChannel.dismissToast(); await page.waitForTimeout(1000); @@ -255,6 +259,8 @@ test.describe.serial('e2e-encryption', () => { await poHomeChannel.tabs.kebab.click({ force: true }); await expect(poHomeChannel.tabs.btnEnableE2E).toBeVisible(); await poHomeChannel.tabs.btnEnableE2E.click({ force: true }); + await expect(page.getByRole('dialog', { name: 'Enable encryption' })).toBeVisible(); + await page.getByRole('button', { name: 'Enable encryption' }).click(); await page.waitForTimeout(1000); await expect(poHomeChannel.content.encryptedRoomHeaderIcon).toBeVisible(); @@ -367,6 +373,8 @@ test.describe.serial('e2e-encryption', () => { await poHomeChannel.tabs.kebab.click({ force: true }); await expect(poHomeChannel.tabs.btnEnableE2E).toBeVisible(); await poHomeChannel.tabs.btnEnableE2E.click({ force: true }); + await expect(page.getByRole('dialog', { name: 'Enable encryption' })).toBeVisible(); + await page.getByRole('button', { name: 'Enable encryption' }).click(); await page.waitForTimeout(1000); await expect(poHomeChannel.content.encryptedRoomHeaderIcon).toBeVisible(); @@ -446,6 +454,8 @@ test.describe.serial('e2e-encryption', () => { await expect(poHomeChannel.tabs.btnDisableE2E).toBeVisible(); await poHomeChannel.tabs.btnDisableE2E.click(); + await expect(page.getByRole('dialog', { name: 'Disable encryption' })).toBeVisible(); + await page.getByRole('button', { name: 'Disable encryption' }).click(); await poHomeChannel.dismissToast(); // will wait till the key icon in header goes away await expect(poHomeChannel.content.encryptedRoomHeaderIcon).toHaveCount(0); @@ -472,6 +482,8 @@ test.describe.serial('e2e-encryption', () => { await expect(poHomeChannel.tabs.btnEnableE2E).toBeVisible(); await poHomeChannel.tabs.btnEnableE2E.click(); + await expect(page.getByRole('dialog', { name: 'Enable encryption' })).toBeVisible(); + await page.getByRole('button', { name: 'Enable encryption' }).click(); await poHomeChannel.dismissToast(); // will wait till the key icon in header appears await expect(poHomeChannel.content.encryptedRoomHeaderIcon).toHaveCount(1); @@ -727,6 +739,8 @@ test.describe.serial('e2e-encryption', () => { await poHomeChannel.tabs.kebab.click({ force: true }); await expect(poHomeChannel.tabs.btnEnableE2E).toBeVisible(); await poHomeChannel.tabs.btnEnableE2E.click({ force: true }); + await expect(page.getByRole('dialog', { name: 'Enable encryption' })).toBeVisible(); + await page.getByRole('button', { name: 'Enable encryption' }).click(); await page.waitForTimeout(1000); await expect(poHomeChannel.content.encryptedRoomHeaderIcon).toBeVisible(); diff --git a/packages/i18n/src/locales/en.i18n.json b/packages/i18n/src/locales/en.i18n.json index c69fd36b00e2..b9494e65046c 100644 --- a/packages/i18n/src/locales/en.i18n.json +++ b/packages/i18n/src/locales/en.i18n.json @@ -1812,6 +1812,17 @@ "E2E_Encryption_disabled_for_room": "End-to-end encryption disabled for #{{roomName}}", "E2EE_not_available_OTR": "This room has OTR enabled, E2E encryption cannot work with OTR.", "E2EE_Composer_Unencrypted_Message": "You're sending an unencrypted message", + "E2E_enable_encryption": "Enable encryption", + "E2E_enable_encryption_description": "Keep conversations private with E2EE, ensuring only intended recipients can access messages and files in this {{roomType}}.", + "E2E_disable_encryption": "Disable encryption", + "E2E_disable_encryption_description": "Disabling E2EE will compromise the privacy of this {{roomType}}. Access to any encrypted content will be lost for all {{roomType}} members.

Encryption can be re-enabled later. Proceed with caution.", + "E2E_disable_encryption_reset_keys_description": "If no one is able to access the encrypted content you can reset encryption keys instead.", + "E2E_reset_encryption_keys": "Reset encryption keys", + "E2E_reset_encryption_keys_description": "Alternatively, resetting encryption keys will keep encryption enabled but access to previously encrypted content may be lost.", + "E2E_reset_encryption_keys_button": "Reset {{roomType}} encryption keys", + "E2E_reset_encryption_keys_modal_description": "Resetting E2EE keys is only recommend if no {{roomType}} member has a valid key to regain access to the previously encrypted content. All members may lose access to previously encrypted content.

<3>Learn more about resetting encryption keys.

Proceed with caution.", + "E2E_reset_encryption_keys_success": "Encryption keys reset", + "E2E_reset_encryption_keys_error": "Encryption keys reset failed", "Markdown_Parser": "Markdown Parser", "Markdown_SupportSchemesForLink": "Markdown Support Schemes for Link", "End-to-end_encryption": "End-to-end encryption", @@ -5443,6 +5454,7 @@ "This_room_has_been_unarchived": "unarchived room", "This_server_will_be_available_while_your_session_is_active": "This server will be available while your session is active", "This_week": "This Week", + "This_action_cannot_be_undone": "This action cannot be undone", "thread": "thread", "thread_message": "thread message", "Thread_message_list": "Thread message list", From 81998f3450e184d4952375774ce7d8946bba67f0 Mon Sep 17 00:00:00 2001 From: Douglas Gubert Date: Fri, 18 Oct 2024 14:37:29 -0300 Subject: [PATCH 10/25] feat: license add-ons (external modules) (#33433) --- .changeset/three-crews-allow.md | 8 + .../ee/app/license/server/canEnableApp.ts | 36 ++- .../ee/server/apps/communication/rest.ts | 36 +-- .../server/apps/marketplace/appEnableCheck.ts | 42 ---- apps/meteor/ee/server/apps/orchestrator.js | 19 +- .../lib/apps/disableAppsWithAddonsCallback.ts | 46 ++++ apps/meteor/ee/server/startup/apps.ts | 20 ++ apps/meteor/ee/server/startup/apps/index.ts | 1 - .../ee/server/startup/apps/trialExpiration.ts | 16 -- apps/meteor/tests/mocks/data.ts | 1 + .../disableAppsWithAddonsCallback.spec.ts | 233 ++++++++++++++++++ .../app/license/server/canEnableApp.spec.ts | 129 ++++++++++ .../license/__tests__/setLicense.spec.ts | 4 +- .../license/src/MockedLicenseBuilder.ts | 20 +- ee/packages/license/src/events/emitter.ts | 11 +- ee/packages/license/src/events/listeners.ts | 2 +- ee/packages/license/src/license.spec.ts | 28 ++- ee/packages/license/src/license.ts | 6 +- ee/packages/license/src/licenseImp.ts | 8 +- ee/packages/license/src/modules.spec.ts | 101 ++++++++ ee/packages/license/src/modules.ts | 29 ++- ee/packages/license/src/v2/bundles.ts | 32 +-- ee/packages/license/src/v2/convertToV3.ts | 4 +- .../src/definition/metadata/IAppInfo.ts | 1 + packages/core-typings/src/Apps.ts | 3 + .../core-typings/src/license/ILicenseV3.ts | 11 +- .../core-typings/src/license/LicenseInfo.ts | 3 +- .../core-typings/src/license/LicenseModule.ts | 53 ++-- packages/core-typings/src/license/events.ts | 2 +- packages/i18n/src/locales/en.i18n.json | 2 + 30 files changed, 722 insertions(+), 185 deletions(-) create mode 100644 .changeset/three-crews-allow.md delete mode 100644 apps/meteor/ee/server/apps/marketplace/appEnableCheck.ts create mode 100644 apps/meteor/ee/server/lib/apps/disableAppsWithAddonsCallback.ts create mode 100644 apps/meteor/ee/server/startup/apps.ts delete mode 100644 apps/meteor/ee/server/startup/apps/index.ts delete mode 100644 apps/meteor/ee/server/startup/apps/trialExpiration.ts create mode 100644 apps/meteor/tests/unit/app/lib/server/apps/disableAppsWithAddonsCallback.spec.ts create mode 100644 apps/meteor/tests/unit/app/license/server/canEnableApp.spec.ts create mode 100644 ee/packages/license/src/modules.spec.ts diff --git a/.changeset/three-crews-allow.md b/.changeset/three-crews-allow.md new file mode 100644 index 000000000000..534cac8ec6b6 --- /dev/null +++ b/.changeset/three-crews-allow.md @@ -0,0 +1,8 @@ +--- +'@rocket.chat/core-typings': minor +'@rocket.chat/apps-engine': minor +'@rocket.chat/license': minor +'@rocket.chat/meteor': minor +--- + +Added support for interacting with add-ons issued in the license diff --git a/apps/meteor/ee/app/license/server/canEnableApp.ts b/apps/meteor/ee/app/license/server/canEnableApp.ts index 72220e27acad..c4ad4d5bcf79 100644 --- a/apps/meteor/ee/app/license/server/canEnableApp.ts +++ b/apps/meteor/ee/app/license/server/canEnableApp.ts @@ -1,25 +1,49 @@ import type { IAppStorageItem } from '@rocket.chat/apps-engine/server/storage'; import { Apps } from '@rocket.chat/core-services'; -import { License } from '@rocket.chat/license'; +import type { LicenseModule } from '@rocket.chat/core-typings'; +import { License, type LicenseImp } from '@rocket.chat/license'; import { getInstallationSourceFromAppStorageItem } from '../../../../lib/apps/getInstallationSourceFromAppStorageItem'; -export const canEnableApp = async (app: IAppStorageItem): Promise => { +type _canEnableAppDependencies = { + Apps: typeof Apps; + License: LicenseImp; +}; + +export const _canEnableApp = async ({ Apps, License }: _canEnableAppDependencies, app: IAppStorageItem): Promise => { if (!(await Apps.isInitialized())) { - return false; + throw new Error('apps-engine-not-initialized'); } // Migrated apps were installed before the validation was implemented // so they're always allowed to be enabled if (app.migrated) { - return true; + return; + } + + if (app.info.addon && !License.hasModule(app.info.addon as LicenseModule)) { + throw new Error('app-addon-not-valid'); } const source = getInstallationSourceFromAppStorageItem(app); switch (source) { case 'private': - return !(await License.shouldPreventAction('privateApps')); + if (await License.shouldPreventAction('privateApps')) { + throw new Error('license-prevented'); + } + + break; default: - return !(await License.shouldPreventAction('marketplaceApps')); + if (await License.shouldPreventAction('marketplaceApps')) { + throw new Error('license-prevented'); + } + + if (app.marketplaceInfo?.isEnterpriseOnly && !License.hasValidLicense()) { + throw new Error('invalid-license'); + } + + break; } }; + +export const canEnableApp = async (app: IAppStorageItem): Promise => _canEnableApp({ Apps, License }, app); diff --git a/apps/meteor/ee/server/apps/communication/rest.ts b/apps/meteor/ee/server/apps/communication/rest.ts index 3b80c37f7990..fc597d00857c 100644 --- a/apps/meteor/ee/server/apps/communication/rest.ts +++ b/apps/meteor/ee/server/apps/communication/rest.ts @@ -1,9 +1,7 @@ import { AppStatus, AppStatusUtils } from '@rocket.chat/apps-engine/definition/AppStatus'; import type { IAppInfo } from '@rocket.chat/apps-engine/definition/metadata'; import type { AppManager } from '@rocket.chat/apps-engine/server/AppManager'; -import { AppInstallationSource } from '@rocket.chat/apps-engine/server/storage'; import type { IUser, IMessage } from '@rocket.chat/core-typings'; -import { License } from '@rocket.chat/license'; import { Settings, Users } from '@rocket.chat/models'; import { serverFetch as fetch } from '@rocket.chat/server-fetch'; import { Meteor } from 'meteor/meteor'; @@ -20,7 +18,6 @@ import { i18n } from '../../../../server/lib/i18n'; import { sendMessagesToAdmins } from '../../../../server/lib/sendMessagesToAdmins'; import { canEnableApp } from '../../../app/license/server/canEnableApp'; import { formatAppInstanceForRest } from '../../../lib/misc/formatAppInstanceForRest'; -import { appEnableCheck } from '../marketplace/appEnableCheck'; import { notifyAppInstall } from '../marketplace/appInstall'; import type { AppServerOrchestrator } from '../orchestrator'; import { Apps } from '../orchestrator'; @@ -418,9 +415,13 @@ export class AppsRestApi { void notifyAppInstall(orchestrator.getMarketplaceUrl() as string, 'install', info); - if (await canEnableApp(aff.getApp().getStorageItem())) { + try { + await canEnableApp(aff.getApp().getStorageItem()); + const success = await manager.enable(info.id); info.status = success ? AppStatus.AUTO_ENABLED : info.status; + } catch (error) { + orchestrator.getRocketChatLogger().warn(`App "${info.id}" was installed but could not be enabled: `, error); } void orchestrator.getNotifier().appAdded(info.id); @@ -1157,33 +1158,14 @@ export class AppsRestApi { return API.v1.notFound(`No App found by the id of: ${appId}`); } - const storedApp = prl.getStorageItem(); - const { installationSource, marketplaceInfo } = storedApp; - - if (!License.hasValidLicense() && installationSource === AppInstallationSource.MARKETPLACE) { + if (AppStatusUtils.isEnabled(status)) { try { - const baseUrl = orchestrator.getMarketplaceUrl() as string; - const headers = getDefaultHeaders(); - const { version } = prl.getInfo(); - - await appEnableCheck({ - baseUrl, - headers, - appId, - version, - marketplaceInfo, - status, - logger: orchestrator.getRocketChatLogger(), - }); - } catch (error: any) { - return API.v1.failure(error.message); + await canEnableApp(prl.getStorageItem()); + } catch (error: unknown) { + return API.v1.failure((error as Error).message); } } - if (AppStatusUtils.isEnabled(status) && !(await canEnableApp(storedApp))) { - return API.v1.failure('Enabled apps have been maxed out'); - } - const result = await manager.changeStatus(prl.getID(), status); return API.v1.success({ status: result.getStatus() }); }, diff --git a/apps/meteor/ee/server/apps/marketplace/appEnableCheck.ts b/apps/meteor/ee/server/apps/marketplace/appEnableCheck.ts deleted file mode 100644 index 959b8ff5f8e9..000000000000 --- a/apps/meteor/ee/server/apps/marketplace/appEnableCheck.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { AppStatus } from '@rocket.chat/apps-engine/definition/AppStatus'; -import type { IMarketplaceInfo } from '@rocket.chat/apps-engine/server/marketplace'; -import type { Logger } from '@rocket.chat/logger'; - -import { getMarketplaceAppInfo } from './appInfo'; - -export const appEnableCheck = async ({ - baseUrl, - headers, - appId, - version, - logger, - status, - marketplaceInfo, -}: { - baseUrl: string; - headers: Record; - appId: string; - version: string; - logger: Logger; - status: AppStatus; - marketplaceInfo?: IMarketplaceInfo; -}) => { - let isAppEnterpriseOnly = false; - - if (marketplaceInfo?.isEnterpriseOnly !== undefined) { - isAppEnterpriseOnly = marketplaceInfo.isEnterpriseOnly; - } else { - try { - const { isEnterpriseOnly } = await getMarketplaceAppInfo({ baseUrl, headers, appId, version }); - - isAppEnterpriseOnly = !!isEnterpriseOnly; - } catch (error: any) { - logger.error('Error getting the app info from the Marketplace:', error.message); - throw new Error(error.message); - } - } - - if (![AppStatus.DISABLED, AppStatus.MANUALLY_DISABLED].includes(status) && isAppEnterpriseOnly) { - throw new Error('Invalid environment for enabling enterprise app'); - } -}; diff --git a/apps/meteor/ee/server/apps/orchestrator.js b/apps/meteor/ee/server/apps/orchestrator.js index 1d5e7c8c8812..9c1ea6397a76 100644 --- a/apps/meteor/ee/server/apps/orchestrator.js +++ b/apps/meteor/ee/server/apps/orchestrator.js @@ -174,21 +174,16 @@ export class AppServerOrchestrator { // Before enabling each app we verify if there is still room for it const apps = await this.getManager().get(); - /* eslint-disable no-await-in-loop */ // This needs to happen sequentially to keep track of app limits - for (const app of apps) { - const canEnable = await canEnableApp(app.getStorageItem()); - - if (!canEnable) { - this._rocketchatLogger.warn(`App "${app.getInfo().name}" can't be enabled due to CE limits.`); - // We need to continue as the limits are applied depending on the app installation source - // i.e. if one limit is hit, we can't break the loop as the following apps might still be valid - continue; - } + for await (const app of apps) { + try { + await canEnableApp(app.getStorageItem()); - await this.getManager().loadOne(app.getID(), true); + await this.getManager().loadOne(app.getID(), true); + } catch (error) { + this._rocketchatLogger.warn(`App "${app.getInfo().name}" could not be enabled: `, error.message); + } } - /* eslint-enable no-await-in-loop */ await this.getBridges().getSchedulerBridge().startScheduler(); diff --git a/apps/meteor/ee/server/lib/apps/disableAppsWithAddonsCallback.ts b/apps/meteor/ee/server/lib/apps/disableAppsWithAddonsCallback.ts new file mode 100644 index 000000000000..03f7011ba727 --- /dev/null +++ b/apps/meteor/ee/server/lib/apps/disableAppsWithAddonsCallback.ts @@ -0,0 +1,46 @@ +import { AppStatus } from '@rocket.chat/apps-engine/definition/AppStatus'; +import type { LicenseImp } from '@rocket.chat/license'; + +import { i18n } from '../../../../server/lib/i18n'; +import { sendMessagesToAdmins } from '../../../../server/lib/sendMessagesToAdmins'; +import { Apps } from '../../apps'; + +type OnModuleCallbackParameter = Parameters[0]>[0]; + +export async function _disableAppsWithAddonsCallback( + deps: { Apps: typeof Apps; sendMessagesToAdmins: typeof sendMessagesToAdmins }, + { module, external, valid }: OnModuleCallbackParameter, +) { + if (!external || valid) return; + + const enabledApps = await deps.Apps.installedApps({ enabled: true }); + + if (!enabledApps) return; + + const affectedApps: string[] = []; + + await Promise.all( + enabledApps.map(async (app) => { + if (app.getInfo().addon !== module) return; + + affectedApps.push(app.getName()); + + return deps.Apps.getManager()?.disable(app.getID(), AppStatus.DISABLED, false); + }), + ); + + if (!affectedApps.length) return; + + await deps.sendMessagesToAdmins({ + msgs: async ({ adminUser }) => ({ + msg: i18n.t('App_has_been_disabled_addon_message', { + lng: adminUser.language || 'en', + count: affectedApps.length, + appNames: affectedApps, + }), + }), + }); +} + +export const disableAppsWithAddonsCallback = (ctx: OnModuleCallbackParameter) => + _disableAppsWithAddonsCallback({ Apps, sendMessagesToAdmins }, ctx); diff --git a/apps/meteor/ee/server/startup/apps.ts b/apps/meteor/ee/server/startup/apps.ts new file mode 100644 index 000000000000..9cfe0b98f151 --- /dev/null +++ b/apps/meteor/ee/server/startup/apps.ts @@ -0,0 +1,20 @@ +import { License } from '@rocket.chat/license'; +import { Meteor } from 'meteor/meteor'; + +import { Apps } from '../apps'; +import { disableAppsWithAddonsCallback } from '../lib/apps/disableAppsWithAddonsCallback'; + +Meteor.startup(() => { + async function migratePrivateAppsCallback() { + if (!Apps.isInitialized) return; + + void Apps.migratePrivateApps(); + void Apps.disableMarketplaceApps(); + } + + License.onInvalidateLicense(migratePrivateAppsCallback); + License.onRemoveLicense(migratePrivateAppsCallback); + + // Disable apps that depend on add-ons (external modules) if they are invalidated + License.onModule(disableAppsWithAddonsCallback); +}); diff --git a/apps/meteor/ee/server/startup/apps/index.ts b/apps/meteor/ee/server/startup/apps/index.ts deleted file mode 100644 index 389658ee535c..000000000000 --- a/apps/meteor/ee/server/startup/apps/index.ts +++ /dev/null @@ -1 +0,0 @@ -import './trialExpiration'; diff --git a/apps/meteor/ee/server/startup/apps/trialExpiration.ts b/apps/meteor/ee/server/startup/apps/trialExpiration.ts deleted file mode 100644 index 7874c80c81f1..000000000000 --- a/apps/meteor/ee/server/startup/apps/trialExpiration.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { License } from '@rocket.chat/license'; -import { Meteor } from 'meteor/meteor'; - -import { Apps } from '../../apps'; - -Meteor.startup(async () => { - const updateAppsCallback = async () => { - if (!Apps.isInitialized) return; - - void Apps.migratePrivateApps(); - void Apps.disableMarketplaceApps(); - }; - - License.onInvalidateLicense(updateAppsCallback); - License.onRemoveLicense(updateAppsCallback); -}); diff --git a/apps/meteor/tests/mocks/data.ts b/apps/meteor/tests/mocks/data.ts index fd6a0ce91123..777fe1967837 100644 --- a/apps/meteor/tests/mocks/data.ts +++ b/apps/meteor/tests/mocks/data.ts @@ -205,6 +205,7 @@ export const createFakeLicenseInfo = (partial: Partial undefined }, + '../../../../server/lib/i18n': { + i18n: { t: () => undefined }, + }, + }); + +/** + * I've used named "empty" functions to spy on as it is easier to + * troubleshoot if the assertion fails. + * If we use `spy()` instead, there is no clear indication on the + * error message which of the spy assertions failed + */ + +describe('disableAppsWithAddonsCallback', () => { + function sendMessagesToAdmins(): any { + return undefined; + } + + it('should not execute anything if not external module', async () => { + function installedApps() { + return []; + } + + function getManagerDisable() { + return undefined; + } + + const mockManager = { + disable: spy(getManagerDisable), + }; + + const AppsMock = { + installedApps: spy(installedApps), + getManager: () => mockManager, + } as unknown as AppServerOrchestrator; + + await _disableAppsWithAddonsCallback({ Apps: AppsMock, sendMessagesToAdmins }, { module: 'auditing', external: false, valid: true }); + + expect(AppsMock.installedApps).to.not.have.been.called(); + expect(AppsMock.getManager()?.disable).to.not.have.been.called(); + }); + + it('should not execute anything if module is external and valid', async () => { + function installedApps() { + return []; + } + + function getManagerDisable() { + return undefined; + } + + const mockManager = { + disable: spy(getManagerDisable), + }; + + const AppsMock = { + installedApps: spy(installedApps), + getManager: () => mockManager, + } as unknown as AppServerOrchestrator; + + await _disableAppsWithAddonsCallback({ Apps: AppsMock, sendMessagesToAdmins }, { module: 'auditing', external: true, valid: true }); + + expect(AppsMock.installedApps).to.not.have.been.called(); + expect(AppsMock.getManager()?.disable).to.not.have.been.called(); + }); + + it('should not throw if there are no apps installed that are enabled', async () => { + function installedApps() { + return []; + } + + function getManagerDisable() { + return undefined; + } + + const mockManager = { + disable: spy(getManagerDisable), + }; + + const AppsMock = { + installedApps: spy(installedApps), + getManager: () => mockManager, + } as unknown as AppServerOrchestrator; + + await expect( + _disableAppsWithAddonsCallback({ Apps: AppsMock, sendMessagesToAdmins }, { module: 'auditing', external: true, valid: false }), + ).to.not.eventually.be.rejected; + + expect(AppsMock.installedApps).to.have.been.called(); + expect(AppsMock.getManager()?.disable).to.not.have.been.called(); + }); + + it('should only disable apps that require addons', async () => { + function installedApps() { + return [ + { + getInfo: () => ({}), + getName: () => 'Test App Without Addon', + getID() { + return 'test-app-without-addon'; + }, + }, + { + getInfo: () => ({ addon: 'chat.rocket.test-addon' }), + getName: () => 'Test App WITH Addon', + getID() { + return 'test-app-with-addon'; + }, + }, + ]; + } + + function getManagerDisable() { + return undefined; + } + + const mockManager = { + disable: spy(getManagerDisable), + }; + + const AppsMock = { + installedApps: spy(installedApps), + getManager: () => mockManager, + } as unknown as AppServerOrchestrator; + + await expect( + _disableAppsWithAddonsCallback( + { Apps: AppsMock, sendMessagesToAdmins }, + { module: 'chat.rocket.test-addon', external: true, valid: false }, + ), + ).to.not.eventually.be.rejected; + + expect(AppsMock.installedApps).to.have.been.called(); + expect(AppsMock.getManager()?.disable).to.have.been.called.once; + expect(AppsMock.getManager()?.disable).to.have.been.called.with('test-app-with-addon'); + }); + + it('should not send messages to admins if no app was disabled', async () => { + function installedApps() { + return [ + { + getInfo: () => ({}), + getName: () => 'Test App Without Addon', + getID() { + return 'test-app-without-addon'; + }, + }, + ]; + } + + function getManagerDisable() { + return undefined; + } + + const mockManager = { + disable: spy(getManagerDisable), + }; + + const AppsMock = { + installedApps: spy(installedApps), + getManager: () => mockManager, + } as unknown as AppServerOrchestrator; + + const sendMessagesToAdminsSpy = spy(sendMessagesToAdmins); + + await expect( + _disableAppsWithAddonsCallback( + { Apps: AppsMock, sendMessagesToAdmins: sendMessagesToAdminsSpy }, + { module: 'chat.rocket.test-addon', external: true, valid: false }, + ), + ).to.not.eventually.be.rejected; + + expect(AppsMock.installedApps).to.have.been.called(); + expect(AppsMock.getManager()?.disable).to.not.have.been.called(); + expect(sendMessagesToAdminsSpy).to.not.have.been.called(); + }); + + it('should send messages to admins if some app has been disabled', async () => { + function installedApps() { + return [ + { + getInfo: () => ({}), + getName: () => 'Test App Without Addon', + getID() { + return 'test-app-without-addon'; + }, + }, + { + getInfo: () => ({ addon: 'chat.rocket.test-addon' }), + getName: () => 'Test App WITH Addon', + getID() { + return 'test-app-with-addon'; + }, + }, + ]; + } + + function getManagerDisable() { + return undefined; + } + + const mockManager = { + disable: spy(getManagerDisable), + }; + + const AppsMock = { + installedApps: spy(installedApps), + getManager: () => mockManager, + } as unknown as AppServerOrchestrator; + + const sendMessagesToAdminsSpy = spy(sendMessagesToAdmins); + + await expect( + _disableAppsWithAddonsCallback( + { Apps: AppsMock, sendMessagesToAdmins: sendMessagesToAdminsSpy }, + { module: 'chat.rocket.test-addon', external: true, valid: false }, + ), + ).to.not.eventually.be.rejected; + + expect(AppsMock.installedApps).to.have.been.called(); + expect(AppsMock.getManager()?.disable).to.have.been.called.once; + expect(sendMessagesToAdminsSpy).to.have.been.called(); + }); +}); diff --git a/apps/meteor/tests/unit/app/license/server/canEnableApp.spec.ts b/apps/meteor/tests/unit/app/license/server/canEnableApp.spec.ts new file mode 100644 index 000000000000..8e5b2c0bc3ab --- /dev/null +++ b/apps/meteor/tests/unit/app/license/server/canEnableApp.spec.ts @@ -0,0 +1,129 @@ +import { AppStatus } from '@rocket.chat/apps-engine/definition/AppStatus'; +import type { IMarketplaceInfo } from '@rocket.chat/apps-engine/server/marketplace'; +import { AppInstallationSource, type IAppStorageItem } from '@rocket.chat/apps-engine/server/storage'; +import type { Apps } from '@rocket.chat/core-services'; +import type { LicenseImp } from '@rocket.chat/license'; +import { expect } from 'chai'; + +import { _canEnableApp } from '../../../../../ee/app/license/server/canEnableApp'; + +const getDefaultApp = (): IAppStorageItem => ({ + _id: '6706d9258e0ca97c2f0cc885', + id: '2e14ff6e-b4d5-4c4c-b12b-b1b1d15ec630', + info: { + id: '2e14ff6e-b4d5-4c4c-b12b-b1b1d15ec630', + version: '0.0.1', + requiredApiVersion: '^1.19.0', + iconFile: 'icon.png', + author: { name: 'a', homepage: 'a', support: 'a' }, + name: 'Add-on test', + nameSlug: 'add-on-test', + classFile: 'AddOnTestApp.js', + description: 'a', + implements: [], + iconFileContent: '', + }, + status: AppStatus.UNKNOWN, + settings: {}, + implemented: {}, + installationSource: AppInstallationSource.PRIVATE, + languageContent: {}, + sourcePath: 'GridFS:/6706d9258e0ca97c2f0cc880', + signature: '', + createdAt: new Date('2024-10-09T19:27:33.923Z'), + updatedAt: new Date('2024-10-09T19:27:33.923Z'), +}); + +// We will be passing promises to the `expect` function +/* eslint-disable @typescript-eslint/no-floating-promises */ + +describe('canEnableApp', () => { + it('should throw the message "apps-engine-not-initialized" when appropriate', () => { + const AppsMock = { + isInitialized() { + return false; + }, + } as unknown as typeof Apps; + + const LicenseMock = {} as unknown as LicenseImp; + + const deps = { Apps: AppsMock, License: LicenseMock }; + + return expect(_canEnableApp(deps, getDefaultApp())).to.eventually.be.rejectedWith('apps-engine-not-initialized'); + }); + + const AppsMock = { + isInitialized() { + return true; + }, + } as unknown as typeof Apps; + + const LicenseMock = { + hasModule() { + return false; + }, + shouldPreventAction() { + return true; + }, + hasValidLicense() { + return false; + }, + } as unknown as LicenseImp; + + const deps = { Apps: AppsMock, License: LicenseMock }; + + it('should throw the message "app-addon-not-valid" when appropriate', () => { + const app = getDefaultApp(); + app.info.addon = 'chat.rocket.test-addon'; + + return expect(_canEnableApp(deps, app)).to.eventually.be.rejectedWith('app-addon-not-valid'); + }); + + it('should throw the message "license-prevented" when appropriate', () => { + const privateApp = getDefaultApp(); + const marketplaceApp = getDefaultApp(); + marketplaceApp.installationSource = AppInstallationSource.MARKETPLACE; + + return Promise.all([ + expect(_canEnableApp(deps, privateApp)).to.eventually.be.rejectedWith('license-prevented'), + expect(_canEnableApp(deps, marketplaceApp)).to.eventually.be.rejectedWith('license-prevented'), + ]); + }); + + it('should throw the message "invalid-license" when appropriate', () => { + const License = { ...LicenseMock, shouldPreventAction: () => false } as unknown as LicenseImp; + + const app = getDefaultApp(); + app.installationSource = AppInstallationSource.MARKETPLACE; + app.marketplaceInfo = { isEnterpriseOnly: true } as IMarketplaceInfo; + + const deps = { Apps: AppsMock, License }; + + return expect(_canEnableApp(deps, app)).to.eventually.be.rejectedWith('invalid-license'); + }); + + it('should not throw if app is migrated', () => { + const app = getDefaultApp(); + app.migrated = true; + + return expect(_canEnableApp(deps, app)).to.not.eventually.be.rejected; + }); + + it('should not throw if license allows it', () => { + const License = { + hasModule() { + return true; + }, + shouldPreventAction() { + return false; + }, + hasValidLicense() { + return true; + }, + } as unknown as LicenseImp; + + const deps = { Apps: AppsMock, License }; + + return expect(_canEnableApp(deps, getDefaultApp())).to.not.eventually.be.rejected; + }); +}); diff --git a/ee/packages/license/__tests__/setLicense.spec.ts b/ee/packages/license/__tests__/setLicense.spec.ts index 1caf8cafa2cd..41785c756cc0 100644 --- a/ee/packages/license/__tests__/setLicense.spec.ts +++ b/ee/packages/license/__tests__/setLicense.spec.ts @@ -140,16 +140,18 @@ describe('License set license procedures', () => { const mocked = new MockedLicenseBuilder(); const oldToken = await mocked.sign(); - const newToken = await mocked.withGratedModules(['livechat-enterprise']).sign(); + const newToken = await mocked.withGratedModules(['livechat-enterprise', 'chat.rocket.test-addon']).sign(); await expect(license.setLicense(oldToken)).resolves.toBe(true); expect(license.hasValidLicense()).toBe(true); expect(license.hasModule('livechat-enterprise')).toBe(false); + expect(license.hasModule('chat.rocket.test-addon')).toBe(false); await expect(license.setLicense(newToken)).resolves.toBe(true); expect(license.hasValidLicense()).toBe(true); expect(license.hasModule('livechat-enterprise')).toBe(true); + expect(license.hasModule('chat.rocket.test-addon')).toBe(true); }); it('should call a validated event after set a valid license', async () => { diff --git a/ee/packages/license/src/MockedLicenseBuilder.ts b/ee/packages/license/src/MockedLicenseBuilder.ts index 4c3cab7b660f..d9def5b6b0d5 100644 --- a/ee/packages/license/src/MockedLicenseBuilder.ts +++ b/ee/packages/license/src/MockedLicenseBuilder.ts @@ -1,4 +1,14 @@ -import type { ILicenseTag, ILicenseV3, LicenseLimit, LicenseModule, LicensePeriod, Timestamp } from '@rocket.chat/core-typings'; +import type { InternalModuleName } from '@rocket.chat/core-typings'; +import { + CoreModules, + type GrantedModules, + type ILicenseTag, + type ILicenseV3, + type LicenseLimit, + type LicenseModule, + type LicensePeriod, + type Timestamp, +} from '@rocket.chat/core-typings'; import { encrypt } from './token'; @@ -163,9 +173,7 @@ export class MockedLicenseBuilder { return this; } - grantedModules: { - module: LicenseModule; - }[] = []; + grantedModules: GrantedModules = []; limits: { activeUsers?: LicenseLimit[]; @@ -192,7 +200,9 @@ export class MockedLicenseBuilder { public withGratedModules(modules: LicenseModule[]): this { this.grantedModules = this.grantedModules ?? []; - this.grantedModules.push(...modules.map((module) => ({ module }))); + this.grantedModules.push( + ...(modules.map((module) => ({ module, external: !CoreModules.includes(module as InternalModuleName) })) as GrantedModules), + ); return this; } diff --git a/ee/packages/license/src/events/emitter.ts b/ee/packages/license/src/events/emitter.ts index b4406abaa81b..fee830618eb8 100644 --- a/ee/packages/license/src/events/emitter.ts +++ b/ee/packages/license/src/events/emitter.ts @@ -2,13 +2,17 @@ import type { BehaviorWithContext, LicenseModule } from '@rocket.chat/core-typin import type { LicenseManager } from '../license'; import { logger } from '../logger'; +import { isInternalModuleName } from '../modules'; export function moduleValidated(this: LicenseManager, module: LicenseModule) { try { - this.emit('module', { module, valid: true }); + const external = !isInternalModuleName(module); + + this.emit('module', { module, external, valid: true }); } catch (error) { logger.error({ msg: `Error running module (valid: true) event: ${module}`, error }); } + try { this.emit(`valid:${module}`); } catch (error) { @@ -18,10 +22,13 @@ export function moduleValidated(this: LicenseManager, module: LicenseModule) { export function moduleRemoved(this: LicenseManager, module: LicenseModule) { try { - this.emit('module', { module, valid: false }); + const external = !isInternalModuleName(module); + + this.emit('module', { module, external, valid: false }); } catch (error) { logger.error({ msg: `Error running module (valid: false) event: ${module}`, error }); } + try { this.emit(`invalid:${module}`); } catch (error) { diff --git a/ee/packages/license/src/events/listeners.ts b/ee/packages/license/src/events/listeners.ts index 7b7eaf0baed9..97e57afbcc6d 100644 --- a/ee/packages/license/src/events/listeners.ts +++ b/ee/packages/license/src/events/listeners.ts @@ -83,7 +83,7 @@ export function onToggledFeature( }; } -export function onModule(this: LicenseManager, cb: (data: { module: LicenseModule; valid: boolean }) => void) { +export function onModule(this: LicenseManager, cb: (data: { module: LicenseModule; external: boolean; valid: boolean }) => void) { this.on('module', cb); } diff --git a/ee/packages/license/src/license.spec.ts b/ee/packages/license/src/license.spec.ts index 229b7e709780..b4a7f2c08902 100644 --- a/ee/packages/license/src/license.spec.ts +++ b/ee/packages/license/src/license.spec.ts @@ -421,7 +421,7 @@ describe('License.setLicense', () => { }); describe('License.removeLicense', () => { - it('should trigger the sync event even if the module callback throws an error', async () => { + it('should trigger the removed event', async () => { const licenseManager = await getReadyLicenseManager(); const removeLicense = jest.fn(); @@ -431,7 +431,7 @@ describe('License.removeLicense', () => { licenseManager.onModule(moduleCallback); - const license = await new MockedLicenseBuilder().withGratedModules(['auditing']).withLimits('activeUsers', [ + const license = await new MockedLicenseBuilder().withGratedModules(['auditing', 'chat.rocket.test-addon']).withLimits('activeUsers', [ { max: 10, behavior: 'disable_modules', @@ -440,22 +440,34 @@ describe('License.removeLicense', () => { ]); await expect(licenseManager.setLicense(await license.sign(), true)).resolves.toBe(true); - await expect(removeLicense).toHaveBeenCalledTimes(0); - await expect(moduleCallback).toHaveBeenNthCalledWith(1, { + expect(removeLicense).toHaveBeenCalledTimes(0); + expect(moduleCallback).toHaveBeenNthCalledWith(1, { module: 'auditing', valid: true, + external: false, + }); + expect(moduleCallback).toHaveBeenNthCalledWith(2, { + module: 'chat.rocket.test-addon', + valid: true, + external: true, }); removeLicense.mockClear(); moduleCallback.mockClear(); - await licenseManager.remove(); + licenseManager.remove(); - await expect(removeLicense).toHaveBeenCalledTimes(1); - await expect(moduleCallback).toHaveBeenNthCalledWith(1, { + expect(removeLicense).toHaveBeenCalledTimes(1); + expect(moduleCallback).toHaveBeenNthCalledWith(1, { module: 'auditing', valid: false, + external: false, + }); + expect(moduleCallback).toHaveBeenNthCalledWith(2, { + module: 'chat.rocket.test-addon', + valid: false, + external: true, }); - await expect(licenseManager.hasValidLicense()).toBe(false); + expect(licenseManager.hasValidLicense()).toBe(false); }); }); diff --git a/ee/packages/license/src/license.ts b/ee/packages/license/src/license.ts index a8d8f1bca510..beb13a63e83d 100644 --- a/ee/packages/license/src/license.ts +++ b/ee/packages/license/src/license.ts @@ -7,9 +7,9 @@ import type { BehaviorWithContext, LicenseBehavior, LicenseInfo, - LicenseModule, LicenseValidationOptions, LimitContext, + LicenseModule, } from '@rocket.chat/core-typings'; import { Emitter } from '@rocket.chat/emitter'; @@ -19,7 +19,7 @@ import { InvalidLicenseError } from './errors/InvalidLicenseError'; import { NotReadyForValidation } from './errors/NotReadyForValidation'; import { behaviorTriggered, behaviorTriggeredToggled, licenseInvalidated, licenseValidated } from './events/emitter'; import { logger } from './logger'; -import { getModules, invalidateAll, replaceModules } from './modules'; +import { getExternalModules, getModules, invalidateAll, replaceModules } from './modules'; import { applyPendingLicense, clearPendingLicense, hasPendingLicense, isPendingLicense, setPendingLicense } from './pendingLicense'; import { replaceTags } from './tags'; import { decrypt } from './token'; @@ -472,6 +472,7 @@ export class LicenseManager extends Emitter { license: boolean; }): Promise { const activeModules = getModules.call(this); + const externalModules = getExternalModules.call(this); const license = this.getLicense(); // Get all limits present in the license and their current value @@ -496,6 +497,7 @@ export class LicenseManager extends Emitter { return { license: (includeLicense && license) || undefined, activeModules, + externalModules, preventedActions: await this.shouldPreventActionResultsMap(), limits: limits as Record, tags: license?.information.tags || [], diff --git a/ee/packages/license/src/licenseImp.ts b/ee/packages/license/src/licenseImp.ts index f3946c7e1c8e..830566553650 100644 --- a/ee/packages/license/src/licenseImp.ts +++ b/ee/packages/license/src/licenseImp.ts @@ -20,7 +20,7 @@ import { import { overwriteClassOnLicense } from './events/overwriteClassOnLicense'; import { LicenseManager } from './license'; import { logger } from './logger'; -import { getModules, hasModule } from './modules'; +import { getExternalModules, getModuleDefinition, getModules, hasModule } from './modules'; import { showLicense } from './showLicense'; import { getTags } from './tags'; import { getCurrentValueForLicenseLimit, setLicenseLimitCounter } from './validation/getCurrentValueForLicenseLimit'; @@ -31,6 +31,8 @@ interface License { validateFormat: typeof validateFormat; hasModule: typeof hasModule; getModules: typeof getModules; + getModuleDefinition: typeof getModuleDefinition; + getExternalModules: typeof getExternalModules; getTags: typeof getTags; overwriteClassOnLicense: typeof overwriteClassOnLicense; setLicenseLimitCounter: typeof setLicenseLimitCounter; @@ -90,6 +92,10 @@ export class LicenseImp extends LicenseManager implements License { getModules = getModules; + getModuleDefinition = getModuleDefinition; + + getExternalModules = getExternalModules; + getTags = getTags; overwriteClassOnLicense = overwriteClassOnLicense; diff --git a/ee/packages/license/src/modules.spec.ts b/ee/packages/license/src/modules.spec.ts new file mode 100644 index 000000000000..07e1fe6d9170 --- /dev/null +++ b/ee/packages/license/src/modules.spec.ts @@ -0,0 +1,101 @@ +import type { LicenseModule } from '@rocket.chat/core-typings'; + +import { MockedLicenseBuilder, getReadyLicenseManager } from '../__tests__/MockedLicenseBuilder'; + +describe('getModules', () => { + it('should return internal and external', async () => { + const licenseManager = await getReadyLicenseManager(); + + const modules = ['auditing', 'livechat-enterprise', 'ldap-enterprise', 'chat.rocket.test-addon'] as LicenseModule[]; + + const license = await new MockedLicenseBuilder().withGratedModules(modules).sign(); + + await expect(licenseManager.setLicense(license)).resolves.toBe(true); + + expect(licenseManager.getModules()).toContain('auditing'); + expect(licenseManager.getModules()).toContain('livechat-enterprise'); + expect(licenseManager.getModules()).toContain('ldap-enterprise'); + expect(licenseManager.getModules()).toContain('chat.rocket.test-addon'); + }); +}); + +describe('getModuleDefinition', () => { + it('should not return `external` property for an internal module', async () => { + const licenseManager = await getReadyLicenseManager(); + + const license = await new MockedLicenseBuilder().withGratedModules(['auditing', 'chat.rocket.test-addon']).sign(); + + await licenseManager.setLicense(license); + + const module = licenseManager.getModuleDefinition('auditing'); + + expect(module).toMatchObject({ module: 'auditing' }); + }); + + it('should return `undefined` for a non-existing module', async () => { + const licenseManager = await getReadyLicenseManager(); + + const license = await new MockedLicenseBuilder().withGratedModules(['auditing', 'chat.rocket.test-addon']).sign(); + + await licenseManager.setLicense(license); + + const module = licenseManager.getModuleDefinition('livechat-enterprise'); + + expect(module).toBeUndefined(); + }); + + it('should return `undefined` if there is no license available', async () => { + const licenseManager = await getReadyLicenseManager(); + + const module = licenseManager.getModuleDefinition('livechat-enterprise'); + + expect(module).toBeUndefined(); + }); + + it('should return `external` property for an external module', async () => { + const licenseManager = await getReadyLicenseManager(); + + const license = await new MockedLicenseBuilder().withGratedModules(['auditing', 'chat.rocket.test-addon']).sign(); + + await licenseManager.setLicense(license); + + const module = licenseManager.getModuleDefinition('chat.rocket.test-addon'); + + expect(module).toMatchObject({ module: 'chat.rocket.test-addon', external: true }); + }); +}); + +describe('getExternalModules', () => { + it('should return only external modules', async () => { + const licenseManager = await getReadyLicenseManager(); + + const license = await new MockedLicenseBuilder().withGratedModules(['auditing', 'chat.rocket.test-addon']).sign(); + + await licenseManager.setLicense(license); + + const modules = licenseManager.getExternalModules(); + + expect(modules).toHaveLength(1); + expect(modules[0]).toMatchObject({ external: true, module: 'chat.rocket.test-addon' }); + }); + + it('should return empty array if no external module is present', async () => { + const licenseManager = await getReadyLicenseManager(); + + const license = await new MockedLicenseBuilder().withGratedModules(['auditing', 'livechat-enterprise']).sign(); + + await licenseManager.setLicense(license); + + const modules = licenseManager.getExternalModules(); + + expect(modules).toHaveLength(0); + }); + + it('should return empty array if license is not available', async () => { + const licenseManager = await getReadyLicenseManager(); + + const modules = licenseManager.getExternalModules(); + + expect(modules).toHaveLength(0); + }); +}); diff --git a/ee/packages/license/src/modules.ts b/ee/packages/license/src/modules.ts index 7a6f41c07ec1..15cebdcde548 100644 --- a/ee/packages/license/src/modules.ts +++ b/ee/packages/license/src/modules.ts @@ -1,8 +1,13 @@ -import type { LicenseModule } from '@rocket.chat/core-typings'; +import type { LicenseModule, InternalModuleName, ExternalModule } from '@rocket.chat/core-typings'; +import { CoreModules } from '@rocket.chat/core-typings'; import { moduleRemoved, moduleValidated } from './events/emitter'; import type { LicenseManager } from './license'; +export function isInternalModuleName(module: string): module is InternalModuleName { + return CoreModules.includes(module as InternalModuleName); +} + export function notifyValidatedModules(this: LicenseManager, licenseModules: LicenseModule[]) { licenseModules.forEach((module) => { this.modules.add(module); @@ -26,6 +31,28 @@ export function getModules(this: LicenseManager) { return [...this.modules]; } +export function getModuleDefinition(this: LicenseManager, moduleName: LicenseModule) { + const license = this.getLicense(); + + if (!license) { + return; + } + + const moduleDefinition = license.grantedModules.find(({ module }) => module === moduleName); + + return moduleDefinition; +} + +export function getExternalModules(this: LicenseManager): ExternalModule[] { + const license = this.getLicense(); + + if (!license) { + return []; + } + + return [...license.grantedModules.filter((value): value is ExternalModule => !isInternalModuleName(value.module))]; +} + export function hasModule(this: LicenseManager, module: LicenseModule) { return this.modules.has(module); } diff --git a/ee/packages/license/src/v2/bundles.ts b/ee/packages/license/src/v2/bundles.ts index 3aad7c9ed71c..518fbe41e1b3 100644 --- a/ee/packages/license/src/v2/bundles.ts +++ b/ee/packages/license/src/v2/bundles.ts @@ -1,35 +1,11 @@ -import type { LicenseModule } from '@rocket.chat/core-typings'; +import { CoreModules, type LicenseModule } from '@rocket.chat/core-typings'; interface IBundle { - [key: string]: LicenseModule[]; + [key: string]: readonly LicenseModule[]; } const bundles: IBundle = { - enterprise: [ - 'auditing', - 'canned-responses', - 'ldap-enterprise', - 'livechat-enterprise', - 'voip-enterprise', - 'omnichannel-mobile-enterprise', - 'engagement-dashboard', - 'push-privacy', - 'scalability', - 'saml-enterprise', - 'oauth-enterprise', - 'device-management', - 'federation', - 'videoconference-enterprise', - 'message-read-receipt', - 'outlook-calendar', - 'teams-mention', - 'hide-watermark', - 'custom-roles', - 'accessibility-certification', - 'unlimited-presence', - 'contact-id-verification', - 'teams-voip', - ], + enterprise: CoreModules, pro: [], }; @@ -55,7 +31,7 @@ export function isBundle(moduleName: string): boolean { return true; } -export function getBundleModules(moduleName: string): string[] { +export function getBundleModules(moduleName: string): readonly string[] { if (moduleName === '*') { return Object.keys(bundles).reduce((modules, bundle) => modules.concat(bundles[bundle]), []); } diff --git a/ee/packages/license/src/v2/convertToV3.ts b/ee/packages/license/src/v2/convertToV3.ts index 7c38881b79a1..539bf10062e3 100644 --- a/ee/packages/license/src/v2/convertToV3.ts +++ b/ee/packages/license/src/v2/convertToV3.ts @@ -3,7 +3,7 @@ * Transform a License V2 into a V3 representation. */ -import type { ILicenseV2, ILicenseV3, LicenseModule } from '@rocket.chat/core-typings'; +import type { ILicenseV2, ILicenseV3, InternalModuleName } from '@rocket.chat/core-typings'; import { isBundle, getBundleFromModule, getBundleModules } from './bundles'; import { getTagColor } from './getTagColor'; @@ -53,7 +53,7 @@ export const convertToV3 = (v2: ILicenseV2): ILicenseV3 => { ['teams-voip', 'contact-id-verification', 'hide-watermark', ...v2.modules] .map((licenseModule) => (isBundle(licenseModule) ? getBundleModules(licenseModule) : [licenseModule])) .reduce((prev, curr) => [...prev, ...curr], []) - .map((licenseModule) => ({ module: licenseModule as LicenseModule })), + .map((licenseModule) => ({ module: licenseModule as InternalModuleName })), ), ], limits: { diff --git a/packages/apps-engine/src/definition/metadata/IAppInfo.ts b/packages/apps-engine/src/definition/metadata/IAppInfo.ts index af1d0d62fd11..6cb7515ea6d4 100644 --- a/packages/apps-engine/src/definition/metadata/IAppInfo.ts +++ b/packages/apps-engine/src/definition/metadata/IAppInfo.ts @@ -17,4 +17,5 @@ export interface IAppInfo { iconFileContent?: string; essentials?: Array; permissions?: Array; + addon?: string; } diff --git a/packages/core-typings/src/Apps.ts b/packages/core-typings/src/Apps.ts index 3d0afce9d12c..d1b0ddac4a56 100644 --- a/packages/core-typings/src/Apps.ts +++ b/packages/core-typings/src/Apps.ts @@ -1,5 +1,7 @@ import type { AppStatus } from '@rocket.chat/apps-engine/definition/AppStatus'; +import type { ExternalModuleName } from './license'; + export type AppScreenshot = { id: string; appId: string; @@ -92,6 +94,7 @@ export type App = { categories: string[]; version: string; versionIncompatible?: boolean; + addon?: ExternalModuleName; price: number; purchaseType: PurchaseType; pricingPlans: AppPricingPlan[]; diff --git a/packages/core-typings/src/license/ILicenseV3.ts b/packages/core-typings/src/license/ILicenseV3.ts index d99f80c71bfc..b6f6fae00eb7 100644 --- a/packages/core-typings/src/license/ILicenseV3.ts +++ b/packages/core-typings/src/license/ILicenseV3.ts @@ -1,8 +1,13 @@ import type { ILicenseTag } from './ILicenseTag'; import type { LicenseLimit } from './LicenseLimit'; -import type { LicenseModule } from './LicenseModule'; +import type { ExternalModuleName, InternalModuleName } from './LicenseModule'; import type { LicensePeriod, Timestamp } from './LicensePeriod'; +export type InternalModule = { module: InternalModuleName; external?: false }; +export type ExternalModule = { module: ExternalModuleName; external: true }; + +export type GrantedModules = (InternalModule | ExternalModule)[]; + export interface ILicenseV3 { version: '3.0'; information: { @@ -48,9 +53,7 @@ export interface ILicenseV3 { allowedStaleInDays?: number; }; }; - grantedModules: { - module: LicenseModule; - }[]; + grantedModules: GrantedModules; limits: { activeUsers?: LicenseLimit[]; guestUsers?: LicenseLimit[]; diff --git a/packages/core-typings/src/license/LicenseInfo.ts b/packages/core-typings/src/license/LicenseInfo.ts index 019d1b9e1ca0..85e7c3fba506 100644 --- a/packages/core-typings/src/license/LicenseInfo.ts +++ b/packages/core-typings/src/license/LicenseInfo.ts @@ -1,10 +1,11 @@ import type { ILicenseTag } from './ILicenseTag'; -import type { ILicenseV3, LicenseLimitKind } from './ILicenseV3'; +import type { ExternalModule, ILicenseV3, LicenseLimitKind } from './ILicenseV3'; import type { LicenseModule } from './LicenseModule'; export type LicenseInfo = { license?: ILicenseV3; activeModules: LicenseModule[]; + externalModules: ExternalModule[]; preventedActions: Record; limits: Record; tags: ILicenseTag[]; diff --git a/packages/core-typings/src/license/LicenseModule.ts b/packages/core-typings/src/license/LicenseModule.ts index a1be9663afec..faf3332d5f45 100644 --- a/packages/core-typings/src/license/LicenseModule.ts +++ b/packages/core-typings/src/license/LicenseModule.ts @@ -1,24 +1,29 @@ -export type LicenseModule = - | 'auditing' - | 'canned-responses' - | 'ldap-enterprise' - | 'livechat-enterprise' - | 'voip-enterprise' - | 'omnichannel-mobile-enterprise' - | 'engagement-dashboard' - | 'push-privacy' - | 'scalability' - | 'teams-mention' - | 'saml-enterprise' - | 'oauth-enterprise' - | 'device-management' - | 'federation' - | 'videoconference-enterprise' - | 'message-read-receipt' - | 'outlook-calendar' - | 'hide-watermark' - | 'custom-roles' - | 'accessibility-certification' - | 'unlimited-presence' - | 'contact-id-verification' - | 'teams-voip'; +export const CoreModules = [ + 'auditing', + 'canned-responses', + 'ldap-enterprise', + 'livechat-enterprise', + 'voip-enterprise', + 'omnichannel-mobile-enterprise', + 'engagement-dashboard', + 'push-privacy', + 'scalability', + 'teams-mention', + 'saml-enterprise', + 'oauth-enterprise', + 'device-management', + 'federation', + 'videoconference-enterprise', + 'message-read-receipt', + 'outlook-calendar', + 'hide-watermark', + 'custom-roles', + 'accessibility-certification', + 'unlimited-presence', + 'contact-id-verification', + 'teams-voip', +] as const; + +export type InternalModuleName = (typeof CoreModules)[number]; +export type ExternalModuleName = `${string}.${string}`; +export type LicenseModule = InternalModuleName | ExternalModuleName; diff --git a/packages/core-typings/src/license/events.ts b/packages/core-typings/src/license/events.ts index 2156c298849c..487d31ae6698 100644 --- a/packages/core-typings/src/license/events.ts +++ b/packages/core-typings/src/license/events.ts @@ -19,6 +19,6 @@ export type LicenseEvents = ModuleValidation & removed: undefined; validate: undefined; invalidate: undefined; - module: { module: LicenseModule; valid: boolean }; + module: { module: LicenseModule; external: boolean; valid: boolean }; sync: undefined; }; diff --git a/packages/i18n/src/locales/en.i18n.json b/packages/i18n/src/locales/en.i18n.json index b9494e65046c..42422ef477ac 100644 --- a/packages/i18n/src/locales/en.i18n.json +++ b/packages/i18n/src/locales/en.i18n.json @@ -529,6 +529,8 @@ "App_Info": "App Info", "App_Information": "App Information", "Apps_context_enterprise": "Enterprise", + "App_has_been_disabled_addon_message_one": "The app {{appNames}} has been disabled because of an invalid add-on. A valid add-on subscription is required to re-enable it", + "App_has_been_disabled_addon_message_other": "The apps {{appNames}} have been disabled because of invalid add-ons. A valid add-on subscription is required to re-enable them", "App_Installation": "App Installation", "App_Installation_Deprecation_Title": "Deprecation Warning", "App_Installation_Deprecation": "Install apps from URL is deprecated and will be removed in the next major release.", From 687f1efd5f6fa18aff3dea22eb665bc278f9f015 Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo Date: Fri, 18 Oct 2024 16:01:44 -0300 Subject: [PATCH 11/25] feat(ui-kit): added accessory to CalloutBlock (#33528) --- .changeset/giant-spiders-train.md | 5 +++ .../src/Payload/actionBlock/BlocksTree.ts | 6 ++- .../src/Payload/callout/index.ts | 24 +++++++++++ .../src/blocks/CalloutBlock.tsx | 13 +++++- .../src/stories/Modal.stories.tsx | 4 ++ .../src/stories/payloads/callout.ts | 41 +++++++++++++++++++ .../src/stories/payloads/index.ts | 1 + .../ui-kit/src/blocks/layout/CalloutBlock.ts | 3 ++ 8 files changed, 95 insertions(+), 2 deletions(-) create mode 100644 .changeset/giant-spiders-train.md create mode 100644 packages/fuselage-ui-kit/src/stories/payloads/callout.ts diff --git a/.changeset/giant-spiders-train.md b/.changeset/giant-spiders-train.md new file mode 100644 index 000000000000..c05bb03779ba --- /dev/null +++ b/.changeset/giant-spiders-train.md @@ -0,0 +1,5 @@ +--- + +--- + +Adds `accessory` properties to `CalloutBlock` diff --git a/apps/uikit-playground/src/Payload/actionBlock/BlocksTree.ts b/apps/uikit-playground/src/Payload/actionBlock/BlocksTree.ts index 700c5505b6fb..e68d9761736c 100644 --- a/apps/uikit-playground/src/Payload/actionBlock/BlocksTree.ts +++ b/apps/uikit-playground/src/Payload/actionBlock/BlocksTree.ts @@ -1,5 +1,5 @@ import type { Item } from '../../Components/DropDown/types'; -import { callout } from '../callout'; +import { callout, calloutWithAction } from '../callout'; import { actionWithButtonDefault, actionWithButtonPrimary, @@ -324,6 +324,10 @@ const BlocksTree: Item = [ label: 'Plain', payload: callout, }, + { + label: 'With Action', + payload: calloutWithAction, + }, ], }, { diff --git a/apps/uikit-playground/src/Payload/callout/index.ts b/apps/uikit-playground/src/Payload/callout/index.ts index 29077031bd3a..3a5ef8fb5b62 100644 --- a/apps/uikit-playground/src/Payload/callout/index.ts +++ b/apps/uikit-playground/src/Payload/callout/index.ts @@ -13,3 +13,27 @@ export const callout: readonly LayoutBlock[] = [ }, }, ]; + +export const calloutWithAction: readonly LayoutBlock[] = [ + { + type: 'callout', + title: { + type: 'plain_text', + text: 'Callout Title', + }, + text: { + type: 'plain_text', + text: 'Callout Text', + }, + accessory: { + type: 'button', + text: { + type: 'plain_text', + text: 'Callout Action', + }, + actionId: 'callout-action', + blockId: 'callout-action', + appId: 'A', + }, + }, +]; diff --git a/packages/fuselage-ui-kit/src/blocks/CalloutBlock.tsx b/packages/fuselage-ui-kit/src/blocks/CalloutBlock.tsx index 91ef7f32da43..a8b634a1739c 100644 --- a/packages/fuselage-ui-kit/src/blocks/CalloutBlock.tsx +++ b/packages/fuselage-ui-kit/src/blocks/CalloutBlock.tsx @@ -11,7 +11,18 @@ const CalloutBlock = ({ surfaceRenderer, }: CalloutBlockProps): ReactElement => { return ( - + {surfaceRenderer.renderTextObject(block.text, 0, UiKit.BlockContext.NONE)} ); diff --git a/packages/fuselage-ui-kit/src/stories/Modal.stories.tsx b/packages/fuselage-ui-kit/src/stories/Modal.stories.tsx index ed1267961781..2be71ef91bd8 100644 --- a/packages/fuselage-ui-kit/src/stories/Modal.stories.tsx +++ b/packages/fuselage-ui-kit/src/stories/Modal.stories.tsx @@ -190,3 +190,7 @@ export const InputWithLinearScale = createStory(payloads.inputWithLinearScale, { }); export const Conditional = createStory(payloads.conditional); + +export const Callout = createStory(payloads.callout); + +export const CalloutWithAction = createStory(payloads.calloutWithAction); diff --git a/packages/fuselage-ui-kit/src/stories/payloads/callout.ts b/packages/fuselage-ui-kit/src/stories/payloads/callout.ts new file mode 100644 index 000000000000..0c0a5036f754 --- /dev/null +++ b/packages/fuselage-ui-kit/src/stories/payloads/callout.ts @@ -0,0 +1,41 @@ +import type * as UiKit from '@rocket.chat/ui-kit'; + +export const callout: readonly UiKit.LayoutBlock[] = [ + { + type: 'callout', + title: { + type: 'plain_text', + text: 'Callout', + }, + text: { + type: 'plain_text', + text: 'This is a callout', + }, + variant: 'info', + }, +] as const; + +export const calloutWithAction: readonly UiKit.LayoutBlock[] = [ + { + type: 'callout', + title: { + type: 'plain_text', + text: 'Callout', + }, + text: { + type: 'plain_text', + text: 'This is a callout', + }, + variant: 'info', + accessory: { + appId: 'dummy-app-id', + blockId: 'dummy-block-id', + actionId: 'dummy-action-id', + type: 'button', + text: { + type: 'plain_text', + text: 'Action', + }, + }, + }, +]; diff --git a/packages/fuselage-ui-kit/src/stories/payloads/index.ts b/packages/fuselage-ui-kit/src/stories/payloads/index.ts index 2de52ceac429..45fe2dbb1a91 100644 --- a/packages/fuselage-ui-kit/src/stories/payloads/index.ts +++ b/packages/fuselage-ui-kit/src/stories/payloads/index.ts @@ -1,5 +1,6 @@ export * from './actions'; export * from './conditional'; +export * from './callout'; export * from './context'; export * from './divider'; export * from './image'; diff --git a/packages/ui-kit/src/blocks/layout/CalloutBlock.ts b/packages/ui-kit/src/blocks/layout/CalloutBlock.ts index fbb3e9279248..4732443a0a91 100644 --- a/packages/ui-kit/src/blocks/layout/CalloutBlock.ts +++ b/packages/ui-kit/src/blocks/layout/CalloutBlock.ts @@ -1,9 +1,12 @@ import type { LayoutBlockish } from '../LayoutBlockish'; import type { TextObject } from '../TextObject'; +import type { ButtonElement } from '../elements/ButtonElement'; +import type { OverflowElement } from '../elements/OverflowElement'; export type CalloutBlock = LayoutBlockish<{ type: 'callout'; title?: TextObject; text: TextObject; variant?: 'info' | 'danger' | 'warning' | 'success'; + accessory?: ButtonElement | OverflowElement; }>; From 019a69a6d5d6188d017ff80ead5b932e7cce44bc Mon Sep 17 00:00:00 2001 From: Hugo Costa Date: Fri, 18 Oct 2024 17:04:23 -0300 Subject: [PATCH 12/25] fix: message toolbar e2ee not decrypted (#33652) --- .changeset/tame-dolls-know.md | 5 +++++ .../client/components/message/variants/RoomMessage.tsx | 2 +- apps/meteor/tests/e2e/e2e-encryption.spec.ts | 3 +++ 3 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 .changeset/tame-dolls-know.md diff --git a/.changeset/tame-dolls-know.md b/.changeset/tame-dolls-know.md new file mode 100644 index 000000000000..def199bd02af --- /dev/null +++ b/.changeset/tame-dolls-know.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +Fixes message toolbar showing in End-to-end encrypted room when messages are not decrypted diff --git a/apps/meteor/client/components/message/variants/RoomMessage.tsx b/apps/meteor/client/components/message/variants/RoomMessage.tsx index dd36dfa3eaff..bf5c12a9a6dd 100644 --- a/apps/meteor/client/components/message/variants/RoomMessage.tsx +++ b/apps/meteor/client/components/message/variants/RoomMessage.tsx @@ -110,7 +110,7 @@ const RoomMessage = ({ )} - {!message.private && } + {!message.private && message?.e2e !== 'pending' && } ); }; diff --git a/apps/meteor/tests/e2e/e2e-encryption.spec.ts b/apps/meteor/tests/e2e/e2e-encryption.spec.ts index d92ddad1d5f3..4446422d295a 100644 --- a/apps/meteor/tests/e2e/e2e-encryption.spec.ts +++ b/apps/meteor/tests/e2e/e2e-encryption.spec.ts @@ -420,6 +420,9 @@ test.describe.serial('e2e-encryption', () => { 'This message is end-to-end encrypted. To view it, you must enter your encryption key in your account settings.', ); await expect(poHomeChannel.content.lastUserMessage.locator('.rcx-icon--name-key')).toBeVisible(); + + await poHomeChannel.content.lastUserMessage.hover(); + await expect(page.locator('[role=toolbar][aria-label="Message actions"]')).not.toBeVisible(); }); test('expect placeholder text in place of encrypted file description, when E2EE is not setup and non-encrypted files upload in disabled e2ee room', async ({ From 15582b10267d5f0ec02b5b885f24901b152456f3 Mon Sep 17 00:00:00 2001 From: Ricardo Garim Date: Fri, 18 Oct 2024 17:06:05 -0300 Subject: [PATCH 13/25] chore!: remove query field on online groups listing (#33647) --- apps/meteor/app/api/server/v1/groups.ts | 18 ++++++++++++------ apps/meteor/tests/end-to-end/api/groups.ts | 12 +++++++++--- .../src/v1/groups/GroupsOnlineProps.ts | 12 +++++++++--- 3 files changed, 30 insertions(+), 12 deletions(-) diff --git a/apps/meteor/app/api/server/v1/groups.ts b/apps/meteor/app/api/server/v1/groups.ts index f2339b0a79c3..64ad8e41150d 100644 --- a/apps/meteor/app/api/server/v1/groups.ts +++ b/apps/meteor/app/api/server/v1/groups.ts @@ -1,6 +1,7 @@ import { Team, isMeteorError } from '@rocket.chat/core-services'; import type { IIntegration, IUser, IRoom, RoomType } from '@rocket.chat/core-typings'; import { Integrations, Messages, Rooms, Subscriptions, Uploads, Users } from '@rocket.chat/models'; +import { isGroupsOnlineProps } from '@rocket.chat/rest-typings'; import { check, Match } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; import type { Filter } from 'mongodb'; @@ -779,23 +780,28 @@ API.v1.addRoute( // TODO: CACHE: same as channels.online API.v1.addRoute( 'groups.online', - { authRequired: true }, + { authRequired: true, validateParams: isGroupsOnlineProps }, { async get() { const { query } = await this.parseJsonQuery(); - if (!query || Object.keys(query).length === 0) { + const { _id } = this.queryParams; + + if ((!query || Object.keys(query).length === 0) && !_id) { return API.v1.failure('Invalid query'); } - const ourQuery = Object.assign({}, query, { t: 'p' }); - - const room = await Rooms.findOne(ourQuery as Record); + const filter = { + ...query, + ...(_id ? { _id } : {}), + t: 'p', + }; + const room = await Rooms.findOne(filter as Record); if (!room) { return API.v1.failure('Group does not exists'); } - const user = await getLoggedInUser(this.request); + const user = await getLoggedInUser(this.request); if (!user) { return API.v1.failure('User does not exists'); } diff --git a/apps/meteor/tests/end-to-end/api/groups.ts b/apps/meteor/tests/end-to-end/api/groups.ts index 3e4ed0d86c26..9e7630bc5a0e 100644 --- a/apps/meteor/tests/end-to-end/api/groups.ts +++ b/apps/meteor/tests/end-to-end/api/groups.ts @@ -1119,7 +1119,9 @@ describe('[Groups]', () => { it('should return an array with online members', async () => { const { testUser, testUserCredentials, room } = await createUserAndChannel(); - const response = await request.get(api('groups.online')).set(testUserCredentials).query(`query={"_id": "${room._id}"}`); + const response = await request.get(api('groups.online')).set(testUserCredentials).query({ + _id: room._id, + }); const { body } = response; @@ -1134,7 +1136,9 @@ describe('[Groups]', () => { it('should return an empty array if members are offline', async () => { const { testUserCredentials, room } = await createUserAndChannel(false); - const response = await request.get(api('groups.online')).set(testUserCredentials).query(`query={"_id": "${room._id}"}`); + const response = await request.get(api('groups.online')).set(testUserCredentials).query({ + _id: room._id, + }); const { body } = response; @@ -1150,7 +1154,9 @@ describe('[Groups]', () => { await request .get(api('groups.online')) .set(outsiderCredentials) - .query(`query={"_id": "${room._id}"}`) + .query({ + _id: room._id, + }) .expect(400) .expect((res) => { expect(res.body).to.have.property('success', false); diff --git a/packages/rest-typings/src/v1/groups/GroupsOnlineProps.ts b/packages/rest-typings/src/v1/groups/GroupsOnlineProps.ts index 158840e35b36..a3021330cc72 100644 --- a/packages/rest-typings/src/v1/groups/GroupsOnlineProps.ts +++ b/packages/rest-typings/src/v1/groups/GroupsOnlineProps.ts @@ -4,10 +4,15 @@ const ajv = new Ajv({ coerceTypes: true, }); -export type GroupsOnlineProps = { query?: Record }; -const groupsOnlyPropsSchema = { +export type GroupsOnlineProps = { _id?: string; query?: Record }; + +const groupsOnlinePropsSchema = { type: 'object', properties: { + _id: { + type: 'string', + nullable: true, + }, query: { type: 'string', nullable: true, @@ -16,4 +21,5 @@ const groupsOnlyPropsSchema = { required: [], additionalProperties: false, }; -export const isGroupsOnlineProps = ajv.compile(groupsOnlyPropsSchema); + +export const isGroupsOnlineProps = ajv.compile(groupsOnlinePropsSchema); From bd64aedba8b1f21b56b40cfdc6c2303cb238672a Mon Sep 17 00:00:00 2001 From: Pierre Lehnen <55164754+pierre-lehnen-rc@users.noreply.github.com> Date: Fri, 18 Oct 2024 17:43:36 -0300 Subject: [PATCH 14/25] chore: use a separate license module for team collab VoIP and omnichannel VoIP (#33604) --- .../meteor/ee/app/api-enterprise/server/index.ts | 2 +- .../app/api-enterprise/server/voip-freeswitch.ts | 16 ++++++++++++++++ apps/meteor/ee/server/configuration/voip.ts | 2 +- apps/meteor/ee/server/settings/voip.ts | 2 +- packages/i18n/src/locales/en.i18n.json | 1 + 5 files changed, 20 insertions(+), 3 deletions(-) diff --git a/apps/meteor/ee/app/api-enterprise/server/index.ts b/apps/meteor/ee/app/api-enterprise/server/index.ts index 1c48d592d33e..af62899e1f3a 100644 --- a/apps/meteor/ee/app/api-enterprise/server/index.ts +++ b/apps/meteor/ee/app/api-enterprise/server/index.ts @@ -4,6 +4,6 @@ await License.onLicense('canned-responses', async () => { await import('./canned-responses'); }); -await License.onLicense('voip-enterprise', async () => { +License.onValidateLicense(async () => { await import('./voip-freeswitch'); }); diff --git a/apps/meteor/ee/app/api-enterprise/server/voip-freeswitch.ts b/apps/meteor/ee/app/api-enterprise/server/voip-freeswitch.ts index 6cadec3c6dba..fdcfcc2ec6e6 100644 --- a/apps/meteor/ee/app/api-enterprise/server/voip-freeswitch.ts +++ b/apps/meteor/ee/app/api-enterprise/server/voip-freeswitch.ts @@ -16,6 +16,10 @@ API.v1.addRoute( { authRequired: true, permissionsRequired: ['manage-voip-extensions'], validateParams: isVoipFreeSwitchExtensionListProps }, { async get() { + if (!settings.get('VoIP_TeamCollab_Enabled')) { + throw new Error('error-voip-disabled'); + } + const { username, type = 'all' } = this.queryParams; const extensions = await wrapExceptions(() => VoipFreeSwitch.getExtensionList()).catch(() => { @@ -58,6 +62,10 @@ API.v1.addRoute( { authRequired: true, permissionsRequired: ['manage-voip-extensions'], validateParams: isVoipFreeSwitchExtensionAssignProps }, { async post() { + if (!settings.get('VoIP_TeamCollab_Enabled')) { + throw new Error('error-voip-disabled'); + } + const { extension, username } = this.bodyParams; if (!username) { @@ -89,6 +97,10 @@ API.v1.addRoute( { authRequired: true, permissionsRequired: ['view-voip-extension-details'], validateParams: isVoipFreeSwitchExtensionGetDetailsProps }, { async get() { + if (!settings.get('VoIP_TeamCollab_Enabled')) { + throw new Error('error-voip-disabled'); + } + const { extension, group } = this.queryParams; if (!extension) { @@ -115,6 +127,10 @@ API.v1.addRoute( { authRequired: true, permissionsRequired: ['view-user-voip-extension'], validateParams: isVoipFreeSwitchExtensionGetInfoProps }, { async get() { + if (!settings.get('VoIP_TeamCollab_Enabled')) { + throw new Error('error-voip-disabled'); + } + const { userId } = this.queryParams; if (!userId) { diff --git a/apps/meteor/ee/server/configuration/voip.ts b/apps/meteor/ee/server/configuration/voip.ts index b265ca900cdb..1d736d8217e2 100644 --- a/apps/meteor/ee/server/configuration/voip.ts +++ b/apps/meteor/ee/server/configuration/voip.ts @@ -4,7 +4,7 @@ import { Meteor } from 'meteor/meteor'; import { addSettings } from '../settings/voip'; Meteor.startup(async () => { - await License.onLicense('voip-enterprise', async () => { + License.onValidateLicense(async () => { await addSettings(); }); }); diff --git a/apps/meteor/ee/server/settings/voip.ts b/apps/meteor/ee/server/settings/voip.ts index e3fbb90003c6..f773200b91d2 100644 --- a/apps/meteor/ee/server/settings/voip.ts +++ b/apps/meteor/ee/server/settings/voip.ts @@ -5,7 +5,7 @@ export function addSettings(): Promise { await this.with( { enterprise: true, - modules: ['voip-enterprise'], + modules: ['teams-voip'], }, async function () { await this.add('VoIP_TeamCollab_Enabled', false, { diff --git a/packages/i18n/src/locales/en.i18n.json b/packages/i18n/src/locales/en.i18n.json index 42422ef477ac..92056badc0b3 100644 --- a/packages/i18n/src/locales/en.i18n.json +++ b/packages/i18n/src/locales/en.i18n.json @@ -2244,6 +2244,7 @@ "error-registration-not-found": "Registration information not found", "error-extension-not-available": "Extension not available", "error-user-not-found": "User not found", + "error-voip-disaled": "Team voice calls (VoIP) is disabled", "error-extension-not-assigned": "Extension not assigned", "Workspace_exceeded_MAC_limit_disclaimer": "The workspace has exceeded the monthly limit of active contacts. Talk to your workspace admin to address this issue.", "You_do_not_have_permission_to_do_this": "You do not have permission to do this", From 9274cf4586f6978a554bf93615816ee41fb92595 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlia=20Jaeger=20Foresti?= <60678893+juliajforesti@users.noreply.github.com> Date: Fri, 18 Oct 2024 17:43:52 -0300 Subject: [PATCH 15/25] feat: collapse sidebar groups (#33592) --- .changeset/khaki-boxes-suffer.md | 14 ++ .../client/sidebarv2/RoomList/RoomList.tsx | 46 +++--- .../sidebarv2/hooks/useCollapsedGroups.ts | 19 +++ .../client/sidebarv2/hooks/useRoomList.ts | 134 +++++++++++------- apps/meteor/package.json | 2 +- apps/uikit-playground/package.json | 2 +- ee/packages/ui-theming/package.json | 2 +- packages/fuselage-ui-kit/package.json | 2 +- packages/gazzodown/package.json | 2 +- packages/ui-avatar/package.json | 2 +- packages/ui-client/package.json | 2 +- packages/ui-composer/package.json | 2 +- packages/ui-video-conf/package.json | 2 +- packages/ui-voip/package.json | 2 +- yarn.lock | 28 ++-- 15 files changed, 155 insertions(+), 106 deletions(-) create mode 100644 .changeset/khaki-boxes-suffer.md create mode 100644 apps/meteor/client/sidebarv2/hooks/useCollapsedGroups.ts diff --git a/.changeset/khaki-boxes-suffer.md b/.changeset/khaki-boxes-suffer.md new file mode 100644 index 000000000000..844768235ec0 --- /dev/null +++ b/.changeset/khaki-boxes-suffer.md @@ -0,0 +1,14 @@ +--- +'@rocket.chat/fuselage-ui-kit': minor +'@rocket.chat/ui-theming': minor +'@rocket.chat/ui-video-conf': minor +'@rocket.chat/uikit-playground': minor +'@rocket.chat/ui-composer': minor +'@rocket.chat/gazzodown': minor +'@rocket.chat/ui-avatar': minor +'@rocket.chat/ui-client': minor +'@rocket.chat/ui-voip': minor +'@rocket.chat/meteor': minor +--- + +Adds ability to collapse/expand sidebar groups diff --git a/apps/meteor/client/sidebarv2/RoomList/RoomList.tsx b/apps/meteor/client/sidebarv2/RoomList/RoomList.tsx index 89a4aeacef40..26d41a1307e3 100644 --- a/apps/meteor/client/sidebarv2/RoomList/RoomList.tsx +++ b/apps/meteor/client/sidebarv2/RoomList/RoomList.tsx @@ -1,8 +1,6 @@ /* eslint-disable react/no-multi-comp */ -import type { ISubscription, IRoom } from '@rocket.chat/core-typings'; -import { Box, SidebarV2GroupTitle } from '@rocket.chat/fuselage'; +import { Box, SidebarV2CollapseGroup } from '@rocket.chat/fuselage'; import { useResizeObserver } from '@rocket.chat/fuselage-hooks'; -import type { TranslationKey } from '@rocket.chat/ui-contexts'; import { useUserPreference, useUserId } from '@rocket.chat/ui-contexts'; import React, { useMemo } from 'react'; import { useTranslation } from 'react-i18next'; @@ -11,6 +9,7 @@ import { GroupedVirtuoso } from 'react-virtuoso'; import { VirtuosoScrollbars } from '../../components/CustomScrollbars'; import { useOpenedRoom } from '../../lib/RoomManager'; import { useAvatarTemplate } from '../hooks/useAvatarTemplate'; +import { useCollapsedGroups } from '../hooks/useCollapsedGroups'; import { usePreventDefault } from '../hooks/usePreventDefault'; import { useRoomList } from '../hooks/useRoomList'; import { useShortcutOpenMenu } from '../hooks/useShortcutOpenMenu'; @@ -19,30 +18,12 @@ import RoomListRow from './RoomListRow'; import RoomListRowWrapper from './RoomListRowWrapper'; import RoomListWrapper from './RoomListWrapper'; -const getRoomsByGroup = (rooms: (ISubscription & IRoom)[]) => { - const groupCounts = rooms - .reduce((acc, item, index) => { - if (typeof item === 'string') { - acc.push(index); - } - return acc; - }, [] as number[]) - .map((item, index, arr) => (arr[index + 1] ? arr[index + 1] : rooms.length) - item - 1); - - const groupList = rooms.filter((item) => typeof item === 'string') as unknown as TranslationKey[]; - const roomList = rooms.filter((item) => typeof item !== 'string'); - - return { - groupCounts, - groupList, - roomList, - }; -}; - const RoomList = () => { const { t } = useTranslation(); const isAnonymous = !useUserId(); - const roomsList = useRoomList(); + + const { collapsedGroups, handleCollapsedGroups } = useCollapsedGroups(); + const { groupsCount, groupsList, roomList } = useRoomList({ collapsedGroups }); const avatarTemplate = useAvatarTemplate(); const sideBarItemTemplate = useTemplateByViewMode(); const { ref } = useResizeObserver({ debounceDelay: 100 }); @@ -66,14 +47,21 @@ const RoomList = () => { usePreventDefault(ref); useShortcutOpenMenu(ref); - const { groupCounts, groupList, roomList } = getRoomsByGroup(roomsList); - return ( } - itemContent={(index) => } + groupCounts={groupsCount} + groupContent={(index) => ( + handleCollapsedGroups(groupsList[index])} + onKeyDown={() => handleCollapsedGroups(groupsList[index])} + expanded={!collapsedGroups.includes(groupsList[index])} + /> + )} + {...(roomList.length > 0 && { + itemContent: (index) => roomList[index] && , + })} components={{ Item: RoomListRowWrapper, List: RoomListWrapper, Scroller: VirtuosoScrollbars }} /> diff --git a/apps/meteor/client/sidebarv2/hooks/useCollapsedGroups.ts b/apps/meteor/client/sidebarv2/hooks/useCollapsedGroups.ts new file mode 100644 index 000000000000..6aac9f157277 --- /dev/null +++ b/apps/meteor/client/sidebarv2/hooks/useCollapsedGroups.ts @@ -0,0 +1,19 @@ +import { useLocalStorage } from '@rocket.chat/fuselage-hooks'; +import { useCallback } from 'react'; + +export const useCollapsedGroups = () => { + const [collapsedGroups, setCollapsedGroups] = useLocalStorage('sidebarGroups', []); + + const handleCollapsedGroups = useCallback( + (group: string) => { + if (collapsedGroups.includes(group)) { + setCollapsedGroups(collapsedGroups.filter((item) => item !== group)); + } else { + setCollapsedGroups([...collapsedGroups, group]); + } + }, + [collapsedGroups, setCollapsedGroups], + ); + + return { collapsedGroups, handleCollapsedGroups }; +}; diff --git a/apps/meteor/client/sidebarv2/hooks/useRoomList.ts b/apps/meteor/client/sidebarv2/hooks/useRoomList.ts index afdc57086dc4..af6e2029fa9b 100644 --- a/apps/meteor/client/sidebarv2/hooks/useRoomList.ts +++ b/apps/meteor/client/sidebarv2/hooks/useRoomList.ts @@ -1,7 +1,8 @@ import type { ILivechatInquiryRecord, IRoom, ISubscription } from '@rocket.chat/core-typings'; -import { useDebouncedState } from '@rocket.chat/fuselage-hooks'; +import { useDebouncedValue } from '@rocket.chat/fuselage-hooks'; +import type { TranslationKey } from '@rocket.chat/ui-contexts'; import { useUserPreference, useUserSubscriptions, useSetting } from '@rocket.chat/ui-contexts'; -import { useEffect } from 'react'; +import { useMemo } from 'react'; import { useVideoConfIncomingCalls } from '../../contexts/VideoConfContext'; import { useOmnichannelEnabled } from '../../hooks/omnichannel/useOmnichannelEnabled'; @@ -12,19 +13,7 @@ const query = { open: { $ne: false } }; const emptyQueue: ILivechatInquiryRecord[] = []; -const order: ( - | 'Incoming_Calls' - | 'Incoming_Livechats' - | 'Open_Livechats' - | 'On_Hold_Chats' - | 'Unread' - | 'Favorites' - | 'Teams' - | 'Discussions' - | 'Channels' - | 'Direct_Messages' - | 'Conversations' -)[] = [ +const order = [ 'Incoming_Calls', 'Incoming_Livechats', 'Open_Livechats', @@ -36,11 +25,17 @@ const order: ( 'Channels', 'Direct_Messages', 'Conversations', -]; - -export const useRoomList = (): Array => { - const [roomList, setRoomList] = useDebouncedState<(ISubscription & IRoom)[]>([], 150); - +] as const; + +export const useRoomList = ({ + collapsedGroups, +}: { + collapsedGroups?: string[]; +}): { + roomList: Array; + groupsCount: number[]; + groupsList: TranslationKey[]; +} => { const showOmnichannel = useOmnichannelEnabled(); const sidebarGroupByType = useUserPreference('sidebarGroupByType'); const favoritesEnabled = useUserPreference('sidebarShowFavorites'); @@ -56,13 +51,12 @@ export const useRoomList = (): Array => { const incomingCalls = useVideoConfIncomingCalls(); - let queue = emptyQueue; - if (inquiries.enabled) { - queue = inquiries.queue; - } + const queue = inquiries.enabled ? inquiries.queue : emptyQueue; + + const { groupsCount, groupsList, roomList } = useDebouncedValue( + useMemo(() => { + const isCollapsed = (groupTitle: string) => collapsedGroups?.includes(groupTitle); - useEffect(() => { - setRoomList(() => { const incomingCall = new Set(); const favorite = new Set(); const team = new Set(); @@ -83,7 +77,7 @@ export const useRoomList = (): Array => { return incomingCall.add(room); } - if (sidebarShowUnread && (room.alert || room.unread) && !room.hideUnreadStatus) { + if (sidebarShowUnread && (room.alert || room.unread)) { return unread.add(room); } @@ -118,42 +112,76 @@ export const useRoomList = (): Array => { conversation.add(room); }); - const groups = new Map(); + const groups = new Map>(); incomingCall.size && groups.set('Incoming_Calls', incomingCall); - showOmnichannel && inquiries.enabled && queue.length && groups.set('Incoming_Livechats', queue); + + showOmnichannel && inquiries.enabled && queue.length && groups.set('Incoming_Livechats', new Set(queue)); showOmnichannel && omnichannel.size && groups.set('Open_Livechats', omnichannel); showOmnichannel && onHold.size && groups.set('On_Hold_Chats', onHold); + sidebarShowUnread && unread.size && groups.set('Unread', unread); + favoritesEnabled && favorite.size && groups.set('Favorites', favorite); + sidebarGroupByType && team.size && groups.set('Teams', team); + sidebarGroupByType && isDiscussionEnabled && discussion.size && groups.set('Discussions', discussion); + sidebarGroupByType && channels.size && groups.set('Channels', channels); + sidebarGroupByType && direct.size && groups.set('Direct_Messages', direct); + !sidebarGroupByType && groups.set('Conversations', conversation); - return sidebarOrder - .map((key) => { - const group = groups.get(key); - if (!group) { - return []; + + const { groupsCount, groupsList, roomList } = sidebarOrder.reduce( + (acc, key) => { + const value = groups.get(key); + + if (!value) { + return acc; + } + + acc.groupsList.push(key as TranslationKey); + if (isCollapsed(key)) { + acc.groupsCount.push(0); + return acc; } - return [key, ...group]; - }) - .flat(); - }); - }, [ - rooms, - showOmnichannel, - incomingCalls, - inquiries.enabled, - queue, - sidebarShowUnread, - favoritesEnabled, - sidebarGroupByType, - setRoomList, - isDiscussionEnabled, - sidebarOrder, - ]); - - return roomList; + acc.groupsCount.push(value.size); + acc.roomList.push(...value); + return acc; + }, + { + groupsCount: [], + groupsList: [], + roomList: [], + } as { + groupsCount: number[]; + groupsList: TranslationKey[]; + roomList: Array; + }, + ); + + return { groupsCount, groupsList, roomList }; + }, [ + rooms, + showOmnichannel, + inquiries.enabled, + queue, + sidebarShowUnread, + favoritesEnabled, + sidebarGroupByType, + isDiscussionEnabled, + sidebarOrder, + collapsedGroups, + incomingCalls, + ]), + 50, + ); + + return { + roomList, + groupsCount, + groupsList, + }; }; diff --git a/apps/meteor/package.json b/apps/meteor/package.json index c2f5ffbcc819..ff9061ab8c0a 100644 --- a/apps/meteor/package.json +++ b/apps/meteor/package.json @@ -244,7 +244,7 @@ "@rocket.chat/forked-matrix-appservice-bridge": "^4.0.2", "@rocket.chat/forked-matrix-bot-sdk": "^0.6.0-beta.3", "@rocket.chat/freeswitch": "workspace:^", - "@rocket.chat/fuselage": "^0.59.1", + "@rocket.chat/fuselage": "^0.59.3", "@rocket.chat/fuselage-hooks": "^0.33.1", "@rocket.chat/fuselage-polyfills": "~0.31.25", "@rocket.chat/fuselage-toastbar": "^0.33.0", diff --git a/apps/uikit-playground/package.json b/apps/uikit-playground/package.json index 0877784db09c..f7e649d8b2b3 100644 --- a/apps/uikit-playground/package.json +++ b/apps/uikit-playground/package.json @@ -16,7 +16,7 @@ "@lezer/highlight": "^1.1.6", "@rocket.chat/core-typings": "workspace:^", "@rocket.chat/css-in-js": "~0.31.25", - "@rocket.chat/fuselage": "^0.59.1", + "@rocket.chat/fuselage": "^0.59.3", "@rocket.chat/fuselage-hooks": "^0.33.1", "@rocket.chat/fuselage-polyfills": "~0.31.25", "@rocket.chat/fuselage-toastbar": "^0.33.0", diff --git a/ee/packages/ui-theming/package.json b/ee/packages/ui-theming/package.json index 4cb281f188c9..0495a92ea034 100644 --- a/ee/packages/ui-theming/package.json +++ b/ee/packages/ui-theming/package.json @@ -4,7 +4,7 @@ "private": true, "devDependencies": { "@rocket.chat/css-in-js": "~0.31.25", - "@rocket.chat/fuselage": "^0.59.1", + "@rocket.chat/fuselage": "^0.59.3", "@rocket.chat/fuselage-hooks": "^0.33.1", "@rocket.chat/icons": "~0.38.0", "@rocket.chat/ui-contexts": "workspace:~", diff --git a/packages/fuselage-ui-kit/package.json b/packages/fuselage-ui-kit/package.json index 13aa830cd6f1..d9b716238c56 100644 --- a/packages/fuselage-ui-kit/package.json +++ b/packages/fuselage-ui-kit/package.json @@ -52,7 +52,7 @@ "@rocket.chat/apps-engine": "workspace:^", "@rocket.chat/core-typings": "workspace:^", "@rocket.chat/eslint-config": "workspace:^", - "@rocket.chat/fuselage": "^0.59.1", + "@rocket.chat/fuselage": "^0.59.3", "@rocket.chat/fuselage-hooks": "^0.33.1", "@rocket.chat/fuselage-polyfills": "~0.31.25", "@rocket.chat/icons": "~0.38.0", diff --git a/packages/gazzodown/package.json b/packages/gazzodown/package.json index 792695fa890e..c7d11418084c 100644 --- a/packages/gazzodown/package.json +++ b/packages/gazzodown/package.json @@ -30,7 +30,7 @@ "@babel/core": "~7.25.8", "@rocket.chat/core-typings": "workspace:^", "@rocket.chat/css-in-js": "~0.31.25", - "@rocket.chat/fuselage": "^0.59.1", + "@rocket.chat/fuselage": "^0.59.3", "@rocket.chat/fuselage-tokens": "^0.33.1", "@rocket.chat/jest-presets": "workspace:~", "@rocket.chat/message-parser": "workspace:^", diff --git a/packages/ui-avatar/package.json b/packages/ui-avatar/package.json index 058cd096df7d..15d947c6a819 100644 --- a/packages/ui-avatar/package.json +++ b/packages/ui-avatar/package.json @@ -4,7 +4,7 @@ "private": true, "devDependencies": { "@babel/core": "~7.25.8", - "@rocket.chat/fuselage": "^0.59.1", + "@rocket.chat/fuselage": "^0.59.3", "@rocket.chat/ui-contexts": "workspace:^", "@types/react": "~17.0.80", "@types/react-dom": "~17.0.25", diff --git a/packages/ui-client/package.json b/packages/ui-client/package.json index c40e5e2cc29c..72ea44034243 100644 --- a/packages/ui-client/package.json +++ b/packages/ui-client/package.json @@ -21,7 +21,7 @@ "@babel/core": "~7.25.8", "@react-aria/toolbar": "^3.0.0-beta.1", "@rocket.chat/css-in-js": "~0.31.25", - "@rocket.chat/fuselage": "^0.59.1", + "@rocket.chat/fuselage": "^0.59.3", "@rocket.chat/fuselage-hooks": "^0.33.1", "@rocket.chat/icons": "~0.38.0", "@rocket.chat/jest-presets": "workspace:~", diff --git a/packages/ui-composer/package.json b/packages/ui-composer/package.json index ed11fd3012cf..35549b7df460 100644 --- a/packages/ui-composer/package.json +++ b/packages/ui-composer/package.json @@ -21,7 +21,7 @@ "@babel/core": "~7.25.8", "@react-aria/toolbar": "^3.0.0-beta.1", "@rocket.chat/eslint-config": "workspace:^", - "@rocket.chat/fuselage": "^0.59.1", + "@rocket.chat/fuselage": "^0.59.3", "@rocket.chat/icons": "~0.38.0", "@storybook/addon-actions": "^8.3.5", "@storybook/addon-docs": "^8.3.5", diff --git a/packages/ui-video-conf/package.json b/packages/ui-video-conf/package.json index 6940eb991dec..f26e43571606 100644 --- a/packages/ui-video-conf/package.json +++ b/packages/ui-video-conf/package.json @@ -24,7 +24,7 @@ "@babel/core": "~7.25.8", "@rocket.chat/css-in-js": "~0.31.25", "@rocket.chat/eslint-config": "workspace:^", - "@rocket.chat/fuselage": "^0.59.1", + "@rocket.chat/fuselage": "^0.59.3", "@rocket.chat/fuselage-hooks": "^0.33.1", "@rocket.chat/icons": "~0.38.0", "@rocket.chat/jest-presets": "workspace:~", diff --git a/packages/ui-voip/package.json b/packages/ui-voip/package.json index 3654e21aa468..307da263043a 100644 --- a/packages/ui-voip/package.json +++ b/packages/ui-voip/package.json @@ -28,7 +28,7 @@ "@faker-js/faker": "~8.0.2", "@rocket.chat/css-in-js": "~0.31.25", "@rocket.chat/eslint-config": "workspace:^", - "@rocket.chat/fuselage": "^0.59.1", + "@rocket.chat/fuselage": "^0.59.3", "@rocket.chat/fuselage-hooks": "^0.33.1", "@rocket.chat/icons": "~0.38.0", "@rocket.chat/jest-presets": "workspace:~", diff --git a/yarn.lock b/yarn.lock index c54c20875ca0..8c6ee6df2ea1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8092,7 +8092,7 @@ __metadata: "@rocket.chat/apps-engine": "workspace:^" "@rocket.chat/core-typings": "workspace:^" "@rocket.chat/eslint-config": "workspace:^" - "@rocket.chat/fuselage": "npm:^0.59.1" + "@rocket.chat/fuselage": "npm:^0.59.3" "@rocket.chat/fuselage-hooks": "npm:^0.33.1" "@rocket.chat/fuselage-polyfills": "npm:~0.31.25" "@rocket.chat/gazzodown": "workspace:^" @@ -8151,9 +8151,9 @@ __metadata: languageName: unknown linkType: soft -"@rocket.chat/fuselage@npm:^0.59.1": - version: 0.59.1 - resolution: "@rocket.chat/fuselage@npm:0.59.1" +"@rocket.chat/fuselage@npm:^0.59.3": + version: 0.59.3 + resolution: "@rocket.chat/fuselage@npm:0.59.3" dependencies: "@rocket.chat/css-in-js": "npm:^0.31.25" "@rocket.chat/css-supports": "npm:^0.31.25" @@ -8171,7 +8171,7 @@ __metadata: react: ^17.0.2 react-dom: ^17.0.2 react-virtuoso: 1.2.4 - checksum: 10/8451ad891a2731d310581e9dae96cee197d34db940c3933d5d875e9710d66e643b19164472af4727470e84bf35442f292d0d9dc11421b34aecdb0ecc83be1ccf + checksum: 10/b3677f4b7b8fc161757ff93d3619ed599ff2474b9243c8955863acbeb9b41a539029e4598be471b03e0be0219e985ddb9d15f42e306bcb060288c35f9752c5af languageName: node linkType: hard @@ -8182,7 +8182,7 @@ __metadata: "@babel/core": "npm:~7.25.8" "@rocket.chat/core-typings": "workspace:^" "@rocket.chat/css-in-js": "npm:~0.31.25" - "@rocket.chat/fuselage": "npm:^0.59.1" + "@rocket.chat/fuselage": "npm:^0.59.3" "@rocket.chat/fuselage-tokens": "npm:^0.33.1" "@rocket.chat/jest-presets": "workspace:~" "@rocket.chat/message-parser": "workspace:^" @@ -8553,7 +8553,7 @@ __metadata: "@rocket.chat/forked-matrix-appservice-bridge": "npm:^4.0.2" "@rocket.chat/forked-matrix-bot-sdk": "npm:^0.6.0-beta.3" "@rocket.chat/freeswitch": "workspace:^" - "@rocket.chat/fuselage": "npm:^0.59.1" + "@rocket.chat/fuselage": "npm:^0.59.3" "@rocket.chat/fuselage-hooks": "npm:^0.33.1" "@rocket.chat/fuselage-polyfills": "npm:~0.31.25" "@rocket.chat/fuselage-toastbar": "npm:^0.33.0" @@ -9445,7 +9445,7 @@ __metadata: resolution: "@rocket.chat/ui-avatar@workspace:packages/ui-avatar" dependencies: "@babel/core": "npm:~7.25.8" - "@rocket.chat/fuselage": "npm:^0.59.1" + "@rocket.chat/fuselage": "npm:^0.59.3" "@rocket.chat/ui-contexts": "workspace:^" "@types/react": "npm:~17.0.80" "@types/react-dom": "npm:~17.0.25" @@ -9470,7 +9470,7 @@ __metadata: "@babel/core": "npm:~7.25.8" "@react-aria/toolbar": "npm:^3.0.0-beta.1" "@rocket.chat/css-in-js": "npm:~0.31.25" - "@rocket.chat/fuselage": "npm:^0.59.1" + "@rocket.chat/fuselage": "npm:^0.59.3" "@rocket.chat/fuselage-hooks": "npm:^0.33.1" "@rocket.chat/icons": "npm:~0.38.0" "@rocket.chat/jest-presets": "workspace:~" @@ -9522,7 +9522,7 @@ __metadata: "@babel/core": "npm:~7.25.8" "@react-aria/toolbar": "npm:^3.0.0-beta.1" "@rocket.chat/eslint-config": "workspace:^" - "@rocket.chat/fuselage": "npm:^0.59.1" + "@rocket.chat/fuselage": "npm:^0.59.3" "@rocket.chat/icons": "npm:~0.38.0" "@storybook/addon-actions": "npm:^8.3.5" "@storybook/addon-docs": "npm:^8.3.5" @@ -9616,7 +9616,7 @@ __metadata: resolution: "@rocket.chat/ui-theming@workspace:ee/packages/ui-theming" dependencies: "@rocket.chat/css-in-js": "npm:~0.31.25" - "@rocket.chat/fuselage": "npm:^0.59.1" + "@rocket.chat/fuselage": "npm:^0.59.3" "@rocket.chat/fuselage-hooks": "npm:^0.33.1" "@rocket.chat/icons": "npm:~0.38.0" "@rocket.chat/ui-contexts": "workspace:~" @@ -9646,7 +9646,7 @@ __metadata: "@rocket.chat/css-in-js": "npm:~0.31.25" "@rocket.chat/emitter": "npm:~0.31.25" "@rocket.chat/eslint-config": "workspace:^" - "@rocket.chat/fuselage": "npm:^0.59.1" + "@rocket.chat/fuselage": "npm:^0.59.3" "@rocket.chat/fuselage-hooks": "npm:^0.33.1" "@rocket.chat/icons": "npm:~0.38.0" "@rocket.chat/jest-presets": "workspace:~" @@ -9695,7 +9695,7 @@ __metadata: "@rocket.chat/css-in-js": "npm:~0.31.25" "@rocket.chat/emitter": "npm:~0.31.25" "@rocket.chat/eslint-config": "workspace:^" - "@rocket.chat/fuselage": "npm:^0.59.1" + "@rocket.chat/fuselage": "npm:^0.59.3" "@rocket.chat/fuselage-hooks": "npm:^0.33.1" "@rocket.chat/icons": "npm:~0.38.0" "@rocket.chat/jest-presets": "workspace:~" @@ -9752,7 +9752,7 @@ __metadata: "@lezer/highlight": "npm:^1.1.6" "@rocket.chat/core-typings": "workspace:^" "@rocket.chat/css-in-js": "npm:~0.31.25" - "@rocket.chat/fuselage": "npm:^0.59.1" + "@rocket.chat/fuselage": "npm:^0.59.3" "@rocket.chat/fuselage-hooks": "npm:^0.33.1" "@rocket.chat/fuselage-polyfills": "npm:~0.31.25" "@rocket.chat/fuselage-toastbar": "npm:^0.33.0" From 4692150878d57a31536c3a06a55a6097adbed928 Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo Date: Fri, 18 Oct 2024 17:45:27 -0300 Subject: [PATCH 16/25] chore: move to `matrix-*` (#33525) Co-authored-by: Debdut Chakraborty Co-authored-by: Debdut Chakraborty --- .github/actions/build-docker/action.yml | 6 + .github/workflows/ci.yml | 44 +- apps/meteor/.docker/Dockerfile.alpine | 4 + apps/meteor/package.json | 5 +- .../AbstractFederationApplicationService.ts | 5 +- .../federation/domain/IFederationBridge.ts | 2 +- .../infrastructure/matrix/Bridge.ts | 10 +- .../room/to-internal-parser-formatter.ts | 10 +- apps/meteor/tests/data/permissions.helper.ts | 15 +- .../meteor/tests/end-to-end/api/federation.ts | 22 +- package.json | 1 - yarn.lock | 680 +++++++++++------- 12 files changed, 532 insertions(+), 272 deletions(-) diff --git a/.github/actions/build-docker/action.yml b/.github/actions/build-docker/action.yml index d0e9d3ce4f52..1c1132d30daf 100644 --- a/.github/actions/build-docker/action.yml +++ b/.github/actions/build-docker/action.yml @@ -85,6 +85,12 @@ runs: if: inputs.setup == 'true' shell: bash + - if: ${{ matrix.platform == 'alpine' }} + uses: actions/download-artifact@v4 + with: + name: napi-binary + path: /tmp/build/matrix-sdk-crypto.linux-x64-musl.node + - name: Build Docker images shell: bash run: | diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 32d09c1fb076..98fe8a059543 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -280,9 +280,46 @@ jobs: deno-version: ${{ needs.release-versions.outputs.deno-version }} coverage: ${{ github.event_name != 'release' }} + # TODO: this should go away once upstream builds are fixed + build-matrix-rust-bindings-for-alpine: + name: Builds matrix rust bindings against alpine + runs-on: ubuntu-latest + steps: + - run: sudo apt-get install -y musl-tools libunwind-dev && find /usr/include -name stdarg.h 2>/dev/null || true + - uses: actions/checkout@v4 + with: + repository: matrix-org/matrix-rust-sdk-crypto-nodejs + ref: v0.2.0-beta.1 # https://github.com/element-hq/matrix-bot-sdk/blob/e72a4c498e00c6c339a791630c45d00a351f56a8/package.json#L58 + + - uses: actions/setup-node@v4 + with: + node-version: '20' + + - uses: dtolnay/rust-toolchain@stable + with: + toolchain: stable + targets: x86_64-unknown-linux-musl + + - name: Install ziglang + uses: mlugg/setup-zig@v1 + with: + version: 0.13.0 + + - name: Build + run: | + npm install --ignore-scripts + npx napi build --release --target x86_64-unknown-linux-musl --platform --zig + + - name: Upload bin + uses: actions/upload-artifact@v4 + with: + path: matrix-sdk-crypto.linux-x64-musl.node + name: napi-binary + if-no-files-found: error + build-gh-docker-coverage: name: 🚢 Build Docker Images for Testing - needs: [build, release-versions] + needs: [build, release-versions, build-matrix-rust-bindings-for-alpine] runs-on: ubuntu-20.04 env: @@ -311,6 +348,11 @@ jobs: build-containers: ${{ matrix.platform == 'alpine' && 'authorization-service account-service ddp-streamer-service presence-service stream-hub-service queue-worker-service omnichannel-transcript-service' || '' }} NPM_TOKEN: ${{ secrets.NPM_TOKEN }} + - name: Make sure matrix bindings load + if: ${{ matrix.platform == 'alpine' }} + run: | + docker run --rm -w /app/bundle/programs/server/npm/node_modules/matrix-appservice-bridge ghcr.io/rocketchat/rocket.chat:$RC_DOCKER_TAG -e 'require(".")' + build-gh-docker: name: 🚢 Build Docker Images for Production needs: [build-prod, release-versions] diff --git a/apps/meteor/.docker/Dockerfile.alpine b/apps/meteor/.docker/Dockerfile.alpine index e225594ec44f..7138711c3b11 100644 --- a/apps/meteor/.docker/Dockerfile.alpine +++ b/apps/meteor/.docker/Dockerfile.alpine @@ -36,6 +36,10 @@ RUN set -x \ && npm cache clear --force \ && apk del .fetch-deps +# TODO: remove hack once upstream builds are fixed +COPY matrix-sdk-crypto.linux-x64-musl.node /app/bundle/programs/server/npm/node_modules/@matrix-org/matrix-sdk-crypto-nodejs +COPY matrix-sdk-crypto.linux-x64-musl.node /app/bundle/programs/server/npm/node_modules/@vector-im/matrix-bot-sdk/node_modules/@matrix-org/matrix-sdk-crypto-nodejs + VOLUME /app/uploads WORKDIR /app/bundle diff --git a/apps/meteor/package.json b/apps/meteor/package.json index ff9061ab8c0a..fafecd90266b 100644 --- a/apps/meteor/package.json +++ b/apps/meteor/package.json @@ -241,8 +241,6 @@ "@rocket.chat/css-in-js": "~0.31.25", "@rocket.chat/emitter": "~0.31.25", "@rocket.chat/favicon": "workspace:^", - "@rocket.chat/forked-matrix-appservice-bridge": "^4.0.2", - "@rocket.chat/forked-matrix-bot-sdk": "^0.6.0-beta.3", "@rocket.chat/freeswitch": "workspace:^", "@rocket.chat/fuselage": "^0.59.3", "@rocket.chat/fuselage-hooks": "^0.33.1", @@ -292,6 +290,7 @@ "@slack/bolt": "^3.14.0", "@slack/rtm-api": "^6.0.0", "@tanstack/react-query": "^4.16.1", + "@vector-im/matrix-bot-sdk": "0.7.1-element.6", "@xmldom/xmldom": "^0.8.10", "adm-zip": "0.5.16", "ajv": "^8.11.0", @@ -370,6 +369,8 @@ "lodash.get": "^4.4.2", "mailparser": "^3.4.0", "marked": "^4.2.5", + "matrix-appservice": "^2.0.0", + "matrix-appservice-bridge": "^10.3.1", "mem": "^8.1.1", "meteor-node-stubs": "^1.2.10", "mime-db": "^1.52.0", diff --git a/apps/meteor/server/services/federation/application/AbstractFederationApplicationService.ts b/apps/meteor/server/services/federation/application/AbstractFederationApplicationService.ts index 91111e6dd2e8..0bda7529ebe4 100644 --- a/apps/meteor/server/services/federation/application/AbstractFederationApplicationService.ts +++ b/apps/meteor/server/services/federation/application/AbstractFederationApplicationService.ts @@ -54,7 +54,10 @@ export abstract class AbstractFederationApplicationService { return; } if (federatedUser.shouldUpdateFederationAvatar(avatarUrl)) { - await this.internalUserAdapter.setAvatar(federatedUser, this.bridge.convertMatrixUrlToHttp(federatedUser.getExternalId(), avatarUrl)); + await this.internalUserAdapter.setAvatar( + federatedUser, + await this.bridge.convertMatrixUrlToHttp(federatedUser.getExternalId(), avatarUrl), + ); await this.internalUserAdapter.updateFederationAvatar(federatedUser.getInternalId(), avatarUrl); } } diff --git a/apps/meteor/server/services/federation/domain/IFederationBridge.ts b/apps/meteor/server/services/federation/domain/IFederationBridge.ts index 1076888f511d..f5ba4aa0bddc 100644 --- a/apps/meteor/server/services/federation/domain/IFederationBridge.ts +++ b/apps/meteor/server/services/federation/domain/IFederationBridge.ts @@ -82,7 +82,7 @@ export interface IFederationBridge { fileDetails: { filename: string; fileSize: number; mimeType: string; metadata?: { width?: number; height?: number; format?: string } }, ): Promise; uploadContent(externalSenderId: string, content: Buffer, options?: { name?: string; type?: string }): Promise; - convertMatrixUrlToHttp(externalUserId: string, matrixUrl: string): string; + convertMatrixUrlToHttp(externalUserId: string, matrixUrl: string): Promise; sendReplyToMessage( externalRoomId: string, externalUserId: string, diff --git a/apps/meteor/server/services/federation/infrastructure/matrix/Bridge.ts b/apps/meteor/server/services/federation/infrastructure/matrix/Bridge.ts index 31c101bbfdac..bd30f23ed095 100644 --- a/apps/meteor/server/services/federation/infrastructure/matrix/Bridge.ts +++ b/apps/meteor/server/services/federation/infrastructure/matrix/Bridge.ts @@ -1,6 +1,6 @@ import type { IMessage } from '@rocket.chat/core-typings'; -import type { AppServiceOutput, Bridge } from '@rocket.chat/forked-matrix-appservice-bridge'; import { serverFetch as fetch } from '@rocket.chat/server-fetch'; +import type { AppServiceOutput, Bridge } from 'matrix-appservice-bridge'; import type { IExternalUserProfileInformation, IFederationBridge, IFederationBridgeRegistrationFile } from '../../domain/IFederationBridge'; import type { RocketChatSettingsAdapter } from '../rocket-chat/adapters/Settings'; @@ -50,7 +50,7 @@ export class MatrixBridge implements IFederationBridge { this.bridgeInstance.addAppServicePath({ method: 'POST', path: '/_matrix/app/v1/ping', - checkToken: true, + authenticate: true, handler: (_req, res, _next) => { /* * https://spec.matrix.org/v1.11/application-service-api/#post_matrixappv1ping @@ -489,7 +489,7 @@ export class MatrixBridge implements IFederationBridge { } public async getReadStreamForFileFromUrl(externalUserId: string, fileUrl: string): Promise { - const response = await fetch(this.convertMatrixUrlToHttp(externalUserId, fileUrl)); + const response = await fetch(await this.convertMatrixUrlToHttp(externalUserId, fileUrl)); if (!response.body) { throw new Error('Not able to download the file'); } @@ -736,7 +736,7 @@ export class MatrixBridge implements IFederationBridge { await this.bridgeInstance.getIntent(externalUserId).setRoomTopic(externalRoomId, roomTopic); } - public convertMatrixUrlToHttp(externalUserId: string, matrixUrl: string): string { + public convertMatrixUrlToHttp(externalUserId: string, matrixUrl: string): Promise { return this.bridgeInstance.getIntent(externalUserId).matrixClient.mxcToHttp(matrixUrl); } @@ -744,7 +744,7 @@ export class MatrixBridge implements IFederationBridge { federationBridgeLogger.info('Performing Dynamic Import of matrix-appservice-bridge'); // Dynamic import to prevent Rocket.Chat from loading the module until needed and then handle if that fails - const { Bridge, AppServiceRegistration, MatrixUser } = await import('@rocket.chat/forked-matrix-appservice-bridge'); + const { Bridge, AppServiceRegistration, MatrixUser } = await import('matrix-appservice-bridge'); MatrixUserInstance = MatrixUser; const registrationFile = this.internalSettings.getAppServiceRegistrationObject(); diff --git a/apps/meteor/server/services/federation/infrastructure/matrix/converters/room/to-internal-parser-formatter.ts b/apps/meteor/server/services/federation/infrastructure/matrix/converters/room/to-internal-parser-formatter.ts index a1f42e9b2ab3..f5e3f199a8a3 100644 --- a/apps/meteor/server/services/federation/infrastructure/matrix/converters/room/to-internal-parser-formatter.ts +++ b/apps/meteor/server/services/federation/infrastructure/matrix/converters/room/to-internal-parser-formatter.ts @@ -1,4 +1,4 @@ -import type { MentionPill as MentionPillType } from '@rocket.chat/forked-matrix-bot-sdk'; +import type { MentionPill as MentionPillType } from '@vector-im/matrix-bot-sdk'; import { marked } from 'marked'; const INTERNAL_MENTIONS_FOR_EXTERNAL_USERS_REGEX = /@([0-9a-zA-Z-_.]+(@([0-9a-zA-Z-_.]+))?):+([0-9a-zA-Z-_.]+)(?=[^<>]*(?:<\w|$))/gm; // @username:server.com excluding any tags @@ -20,7 +20,7 @@ const replaceMessageMentions = async ( }; const replaceMentionsFromLocalExternalUsersForExternalFormat = async (message: string): Promise => { - const { MentionPill } = await import('@rocket.chat/forked-matrix-bot-sdk'); + const { MentionPill } = await import('@vector-im/matrix-bot-sdk'); return replaceMessageMentions(message, INTERNAL_MENTIONS_FOR_EXTERNAL_USERS_REGEX, (match: string) => MentionPill.forUser(match.trimStart()), @@ -28,7 +28,7 @@ const replaceMentionsFromLocalExternalUsersForExternalFormat = async (message: s }; const replaceInternalUsersMentionsForExternalFormat = async (message: string, homeServerDomain: string): Promise => { - const { MentionPill } = await import('@rocket.chat/forked-matrix-bot-sdk'); + const { MentionPill } = await import('@vector-im/matrix-bot-sdk'); return replaceMessageMentions(message, INTERNAL_MENTIONS_FOR_INTERNAL_USERS_REGEX, (match: string) => MentionPill.forUser(`${match.trimStart()}:${homeServerDomain}`), @@ -36,7 +36,7 @@ const replaceInternalUsersMentionsForExternalFormat = async (message: string, ho }; const replaceInternalGeneralMentionsForExternalFormat = async (message: string, externalRoomId: string): Promise => { - const { MentionPill } = await import('@rocket.chat/forked-matrix-bot-sdk'); + const { MentionPill } = await import('@vector-im/matrix-bot-sdk'); return replaceMessageMentions(message, INTERNAL_GENERAL_REGEX, () => MentionPill.forRoom(externalRoomId)); }; @@ -79,7 +79,7 @@ export const toExternalQuoteMessageFormat = async ({ message: string; homeServerDomain: string; }): Promise<{ message: string; formattedMessage: string }> => { - const { RichReply } = await import('@rocket.chat/forked-matrix-bot-sdk'); + const { RichReply } = await import('@vector-im/matrix-bot-sdk'); const formattedMessage = convertMarkdownToHTML(message); const finalFormattedMessage = convertMarkdownToHTML( diff --git a/apps/meteor/tests/data/permissions.helper.ts b/apps/meteor/tests/data/permissions.helper.ts index 33e9af1693ad..535c1d404b6e 100644 --- a/apps/meteor/tests/data/permissions.helper.ts +++ b/apps/meteor/tests/data/permissions.helper.ts @@ -30,7 +30,7 @@ const updateManyPermissions = (permissions: { [key: string]: string[] }): Promis .end((err?: Error) => setTimeout(() => (!err && resolve()) || reject(err), 100)); }); -export const updateSetting = (setting: string, value: ISetting['value']): Promise => +export const updateSetting = (setting: string, value: ISetting['value'], debounce = true): Promise => new Promise((resolve, reject) => { void request .post(`/api/v1/settings/${setting}`) @@ -38,7 +38,18 @@ export const updateSetting = (setting: string, value: ISetting['value']): Promis .send({ value }) .expect('Content-Type', 'application/json') .expect(200) - .end((err?: Error) => setTimeout(() => (!err && resolve()) || reject(err), 100)); + .end((err?: Error) => { + if (err) { + return reject(err); + } + + if (debounce) { + setTimeout(resolve, 100); + return; + } + + resolve(); + }); }); export const getSettingValueById = async (setting: string): Promise => { diff --git a/apps/meteor/tests/end-to-end/api/federation.ts b/apps/meteor/tests/end-to-end/api/federation.ts index a1bfd92f1d29..5af7f71c1c74 100644 --- a/apps/meteor/tests/end-to-end/api/federation.ts +++ b/apps/meteor/tests/end-to-end/api/federation.ts @@ -7,15 +7,17 @@ import { updateSetting } from '../../data/permissions.helper'; describe('federation', () => { before((done) => getCredentials(done)); + // FIXME: why debouncing is causing timeouts here on the hooks? + // Since we don't care about the watchers on this setting, not debouncing is fine. describe('well-known', () => { describe('when matrix disabled', () => { before(async () => { - await updateSetting('Federation_Matrix_enabled', false); - await updateSetting('Federation_Matrix_serve_well_known', true); + await updateSetting('Federation_Matrix_enabled', false, false); + await updateSetting('Federation_Matrix_serve_well_known', true, false); }); after(async () => { - await updateSetting('Federation_Matrix_serve_well_known', false); + await updateSetting('Federation_Matrix_serve_well_known', false, false); }); it('should return 404 not found', async () => { @@ -27,12 +29,12 @@ describe('federation', () => { describe('when matrix enabled but well-known disabled', () => { before(async () => { - await updateSetting('Federation_Matrix_enabled', true); - await updateSetting('Federation_Matrix_serve_well_known', false); + await updateSetting('Federation_Matrix_enabled', true, false); + await updateSetting('Federation_Matrix_serve_well_known', false, false); }); after(async () => { - await updateSetting('Federation_Matrix_enabled', false); + await updateSetting('Federation_Matrix_enabled', false, false); }); it('should return 404 not found', async () => { @@ -44,13 +46,13 @@ describe('federation', () => { describe('when enabled', () => { before(async () => { - await updateSetting('Federation_Matrix_enabled', true); - await updateSetting('Federation_Matrix_serve_well_known', true); + await updateSetting('Federation_Matrix_enabled', true, false); + await updateSetting('Federation_Matrix_serve_well_known', true, false); }); after(async () => { - await updateSetting('Federation_Matrix_enabled', false); - await updateSetting('Federation_Matrix_serve_well_known', false); + await updateSetting('Federation_Matrix_enabled', false, false); + await updateSetting('Federation_Matrix_serve_well_known', false, false); }); it('should return matrix information', async () => { diff --git a/package.json b/package.json index f7f22c694fa9..26ea7ee794f4 100644 --- a/package.json +++ b/package.json @@ -63,7 +63,6 @@ "underscore": "1.13.7", "lodash": "4.17.21", "mongodb@^4.17.1": "patch:mongodb@npm:4.17.1#.yarn/patches/mongodb-npm-4.17.1-a2fe811ff1.patch", - "@rocket.chat/forked-matrix-sdk-crypto-nodejs": "0.1.0-beta.13", "typia@~6.9.0": "patch:typia@npm%3A6.9.0#./.yarn/patches/typia-npm-6.9.0-2fd4d85f25.patch", "moleculer@^0.14.34": "patch:moleculer@npm%3A0.14.34#./.yarn/patches/moleculer-npm-0.14.34-440e26767d.patch", "mongodb@npm:^4.3.1": "patch:mongodb@npm%3A4.17.2#~/.yarn/patches/mongodb-npm-4.17.2-40d1286d70.patch" diff --git a/yarn.lock b/yarn.lock index 8c6ee6df2ea1..bdb87b964721 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3166,6 +3166,13 @@ __metadata: languageName: node linkType: hard +"@colors/colors@npm:1.6.0, @colors/colors@npm:^1.6.0": + version: 1.6.0 + resolution: "@colors/colors@npm:1.6.0" + checksum: 10/66d00284a3a9a21e5e853b256942e17edbb295f4bd7b9aa7ef06bbb603568d5173eb41b0f64c1e51748bc29d382a23a67d99956e57e7431c64e47e74324182d9 + languageName: node + linkType: hard + "@cspotcode/source-map-support@npm:^0.8.0": version: 0.8.1 resolution: "@cspotcode/source-map-support@npm:0.8.1" @@ -4635,6 +4642,16 @@ __metadata: languageName: node linkType: hard +"@matrix-org/matrix-sdk-crypto-nodejs@npm:0.2.0-beta.1": + version: 0.2.0-beta.1 + resolution: "@matrix-org/matrix-sdk-crypto-nodejs@npm:0.2.0-beta.1" + dependencies: + https-proxy-agent: "npm:^5.0.1" + node-downloader-helper: "npm:^2.1.5" + checksum: 10/5460d306055d16358ecaed2716d0ed31b4b418e0fef00627fae3fc48c55d7f601eee2fc3e1100e476d9a771487df63bd8018d5ccab4c868283d2caa2f3cd73d6 + languageName: node + linkType: hard + "@mdx-js/react@npm:^3.0.0": version: 3.0.1 resolution: "@mdx-js/react@npm:3.0.1" @@ -4683,15 +4700,6 @@ __metadata: languageName: node linkType: hard -"@napi-rs/cli@npm:^2.2.0": - version: 2.6.2 - resolution: "@napi-rs/cli@npm:2.6.2" - bin: - napi: scripts/index.js - checksum: 10/0f5ff1b32548a3f7b3ab2495867996a87c3e371b4cd94c590233a56794a8e982e3e3d1a9070e968bd616b44f845a8f16191a1063f3d03f89a6fff8382558cf52 - languageName: node - linkType: hard - "@napi-rs/pinyin-android-arm-eabi@npm:1.7.0": version: 1.7.0 resolution: "@napi-rs/pinyin-android-arm-eabi@npm:1.7.0" @@ -5287,6 +5295,13 @@ __metadata: languageName: node linkType: hard +"@opentelemetry/api@npm:^1.4.0": + version: 1.9.0 + resolution: "@opentelemetry/api@npm:1.9.0" + checksum: 10/a607f0eef971893c4f2ee2a4c2069aade6ec3e84e2a1f5c2aac19f65c5d9eeea41aa72db917c1029faafdd71789a1a040bdc18f40d63690e22ccae5d7070f194 + languageName: node + linkType: hard + "@parcel/watcher-android-arm64@npm:2.4.1": version: 2.4.1 resolution: "@parcel/watcher-android-arm64@npm:2.4.1" @@ -7956,66 +7971,6 @@ __metadata: languageName: unknown linkType: soft -"@rocket.chat/forked-matrix-appservice-bridge@npm:^4.0.2": - version: 4.0.2 - resolution: "@rocket.chat/forked-matrix-appservice-bridge@npm:4.0.2" - dependencies: - "@alloc/quick-lru": "npm:^5.2.0" - "@rocket.chat/forked-matrix-bot-sdk": "npm:^0.6.0-beta.2" - axios: "npm:^0.23.0" - chalk: "npm:^4.1.0" - express-rate-limit: "npm:^6.2.0" - extend: "npm:^3.0.2" - ip-cidr: "npm:^3.0.4" - is-my-json-valid: "npm:^2.20.5" - js-yaml: "npm:^4.0.0" - matrix-appservice: "npm:^0.10.0" - matrix-js-sdk: "npm:^12.4.1" - nedb: "npm:^1.8.0" - nopt: "npm:^5.0.0" - p-queue: "npm:^6.6.2" - prom-client: "npm:^14.0.0" - uuid: "npm:^8.3.2" - winston: "npm:^3.3.3" - winston-daily-rotate-file: "npm:^4.5.1" - checksum: 10/a6149f35d9230c8fd560dc3995753dc413cfa273f263de3f5728bd6058fb7cf71e4fc2c58f529498c87f43a612b8228d8ed531eef5d788be40cffeb2702883cc - languageName: node - linkType: hard - -"@rocket.chat/forked-matrix-bot-sdk@npm:^0.6.0-beta.2, @rocket.chat/forked-matrix-bot-sdk@npm:^0.6.0-beta.3": - version: 0.6.0-beta.3 - resolution: "@rocket.chat/forked-matrix-bot-sdk@npm:0.6.0-beta.3" - dependencies: - "@rocket.chat/forked-matrix-sdk-crypto-nodejs": "npm:^0.1.0-beta.12" - "@types/express": "npm:^4.17.13" - another-json: "npm:^0.2.0" - chalk: "npm:^4" - express: "npm:^4.17.2" - glob-to-regexp: "npm:^0.4.1" - hash.js: "npm:^1.1.7" - html-to-text: "npm:^8.1.0" - htmlencode: "npm:^0.0.4" - lowdb: "npm:^1" - lru-cache: "npm:^6.0.0" - mkdirp: "npm:^1.0.4" - morgan: "npm:^1.10.0" - request: "npm:^2.88.2" - request-promise: "npm:^4.2.6" - sanitize-html: "npm:^2.6.1" - checksum: 10/c180be729e11f5f7297458b093c6c4f6c4d9d3e91bfacc3bf217fb8d37f8f7d18f5a74fecccc145fb1667feef75d8f6bdf1cbae5728ed54e5658c5ccdb7434db - languageName: node - linkType: hard - -"@rocket.chat/forked-matrix-sdk-crypto-nodejs@npm:0.1.0-beta.13": - version: 0.1.0-beta.13 - resolution: "@rocket.chat/forked-matrix-sdk-crypto-nodejs@npm:0.1.0-beta.13" - dependencies: - "@napi-rs/cli": "npm:^2.2.0" - shelljs: "npm:^0.8.4" - checksum: 10/440e66a60603cffe9b528699294bce7bc78635ccf728dd3f140336686182f260b0dca487faaefb055df0ec62274d6fe97a729d71ec854f6b06328d40616a5c05 - languageName: node - linkType: hard - "@rocket.chat/freeswitch@workspace:^, @rocket.chat/freeswitch@workspace:packages/freeswitch": version: 0.0.0-use.local resolution: "@rocket.chat/freeswitch@workspace:packages/freeswitch" @@ -8550,8 +8505,6 @@ __metadata: "@rocket.chat/emitter": "npm:~0.31.25" "@rocket.chat/eslint-config": "workspace:^" "@rocket.chat/favicon": "workspace:^" - "@rocket.chat/forked-matrix-appservice-bridge": "npm:^4.0.2" - "@rocket.chat/forked-matrix-bot-sdk": "npm:^0.6.0-beta.3" "@rocket.chat/freeswitch": "workspace:^" "@rocket.chat/fuselage": "npm:^0.59.3" "@rocket.chat/fuselage-hooks": "npm:^0.33.1" @@ -8689,6 +8642,7 @@ __metadata: "@types/xml-encryption": "npm:~1.2.4" "@typescript-eslint/eslint-plugin": "npm:~5.60.1" "@typescript-eslint/parser": "npm:~5.60.1" + "@vector-im/matrix-bot-sdk": "npm:0.7.1-element.6" "@xmldom/xmldom": "npm:^0.8.10" adm-zip: "npm:0.5.16" ajv: "npm:^8.11.0" @@ -8794,6 +8748,8 @@ __metadata: lodash.get: "npm:^4.4.2" mailparser: "npm:^3.4.0" marked: "npm:^4.2.5" + matrix-appservice: "npm:^2.0.0" + matrix-appservice-bridge: "npm:^10.3.1" mem: "npm:^8.1.1" meteor-node-stubs: "npm:^1.2.10" mime-db: "npm:^1.52.0" @@ -9840,6 +9796,16 @@ __metadata: languageName: node linkType: hard +"@selderee/plugin-htmlparser2@npm:^0.11.0": + version: 0.11.0 + resolution: "@selderee/plugin-htmlparser2@npm:0.11.0" + dependencies: + domhandler: "npm:^5.0.3" + selderee: "npm:^0.11.0" + checksum: 10/7550108d270e6ea2be4850d55cbf4d58d5a90c109a15b874c3c7c622a1399bd8015359ef3672983a86118432ca8325a6aca1fe79d961b01278fdaeaea8895c5f + languageName: node + linkType: hard + "@selderee/plugin-htmlparser2@npm:^0.6.0": version: 0.6.0 resolution: "@selderee/plugin-htmlparser2@npm:0.6.0" @@ -11740,7 +11706,7 @@ __metadata: languageName: node linkType: hard -"@types/express@npm:*, @types/express@npm:^4.16.1, @types/express@npm:^4.17.13, @types/express@npm:^4.17.8": +"@types/express@npm:*, @types/express@npm:^4.16.1": version: 4.17.20 resolution: "@types/express@npm:4.17.20" dependencies: @@ -12266,7 +12232,7 @@ __metadata: languageName: node linkType: hard -"@types/nedb@npm:^1.8.12": +"@types/nedb@npm:^1.8.12, @types/nedb@npm:^1.8.16": version: 1.8.16 resolution: "@types/nedb@npm:1.8.16" dependencies: @@ -12827,6 +12793,13 @@ __metadata: languageName: node linkType: hard +"@types/triple-beam@npm:^1.3.2": + version: 1.3.5 + resolution: "@types/triple-beam@npm:1.3.5" + checksum: 10/519b6a1b30d4571965c9706ad5400a200b94e4050feca3e7856e3ea7ac00ec9903e32e9a10e2762d0f7e472d5d03e5f4b29c16c0bd8c1f77c8876c683b2231f1 + languageName: node + linkType: hard + "@types/trouter@npm:*": version: 3.1.1 resolution: "@types/trouter@npm:3.1.1" @@ -13268,6 +13241,32 @@ __metadata: languageName: node linkType: hard +"@vector-im/matrix-bot-sdk@npm:0.7.1-element.6, @vector-im/matrix-bot-sdk@npm:^0.7.1-element.6": + version: 0.7.1-element.6 + resolution: "@vector-im/matrix-bot-sdk@npm:0.7.1-element.6" + dependencies: + "@matrix-org/matrix-sdk-crypto-nodejs": "npm:0.2.0-beta.1" + "@types/express": "npm:^4.17.21" + another-json: "npm:^0.2.0" + async-lock: "npm:^1.4.0" + chalk: "npm:4" + express: "npm:^4.18.2" + glob-to-regexp: "npm:^0.4.1" + hash.js: "npm:^1.1.7" + html-to-text: "npm:^9.0.5" + htmlencode: "npm:^0.0.4" + lowdb: "npm:1" + lru-cache: "npm:^10.0.1" + mkdirp: "npm:^3.0.1" + morgan: "npm:^1.10.0" + postgres: "npm:^3.4.1" + request: "npm:^2.88.2" + request-promise: "npm:^4.2.6" + sanitize-html: "npm:^2.11.0" + checksum: 10/782203fbc63df3ff087fd23f529e34122fffcd8ce2751c2403a25f5cd84b0ed9b998dc3139b6a9495db3e4d75f0f628425e92ec2ed49f7364be9e75db93fb6c4 + languageName: node + linkType: hard + "@vitejs/plugin-react@npm:^4.0.0": version: 4.0.0 resolution: "@vitejs/plugin-react@npm:4.0.0" @@ -14591,6 +14590,13 @@ __metadata: languageName: node linkType: hard +"async-lock@npm:^1.4.0": + version: 1.4.1 + resolution: "async-lock@npm:1.4.1" + checksum: 10/80d55ac95f920e880a865968b799963014f6d987dd790dd08173fae6e1af509d8cd0ab45a25daaca82e3ef8e7c939f5d128cd1facfcc5c647da8ac2409e20ef9 + languageName: node + linkType: hard + "async-retry@npm:^1.3.3": version: 1.3.3 resolution: "async-retry@npm:1.3.3" @@ -14761,15 +14767,6 @@ __metadata: languageName: node linkType: hard -"axios@npm:^0.23.0": - version: 0.23.0 - resolution: "axios@npm:0.23.0" - dependencies: - follow-redirects: "npm:^1.14.4" - checksum: 10/27d6c89629c971386a08786e5349a46584eb09bd026f241c9a9827364ad01ffbf15b1a96543f35794a0c965dd6af52cc031996516b5aea246448e1b092c74605 - languageName: node - linkType: hard - "axios@npm:^0.25.0": version: 0.25.0 resolution: "axios@npm:0.25.0" @@ -15107,15 +15104,6 @@ __metadata: languageName: node linkType: hard -"base-x@npm:^3.0.2": - version: 3.0.9 - resolution: "base-x@npm:3.0.9" - dependencies: - safe-buffer: "npm:^5.0.1" - checksum: 10/957101d6fd09e1903e846fd8f69fd7e5e3e50254383e61ab667c725866bec54e5ece5ba49ce385128ae48f9ec93a26567d1d5ebb91f4d56ef4a9cc0d5a5481e8 - languageName: node - linkType: hard - "base32.js@npm:0.0.1": version: 0.0.1 resolution: "base32.js@npm:0.0.1" @@ -15366,6 +15354,26 @@ __metadata: languageName: node linkType: hard +"body-parser@npm:1.20.1": + version: 1.20.1 + resolution: "body-parser@npm:1.20.1" + dependencies: + bytes: "npm:3.1.2" + content-type: "npm:~1.0.4" + debug: "npm:2.6.9" + depd: "npm:2.0.0" + destroy: "npm:1.2.0" + http-errors: "npm:2.0.0" + iconv-lite: "npm:0.4.24" + on-finished: "npm:2.4.1" + qs: "npm:6.11.0" + raw-body: "npm:2.5.1" + type-is: "npm:~1.6.18" + unpipe: "npm:1.0.0" + checksum: 10/5f8d128022a2fb8b6e7990d30878a0182f300b70e46b3f9d358a9433ad6275f0de46add6d63206da3637c01c3b38b6111a7480f7e7ac2e9f7b989f6133fe5510 + languageName: node + linkType: hard + "body-parser@npm:1.20.3, body-parser@npm:^1.20.3": version: 1.20.3 resolution: "body-parser@npm:1.20.3" @@ -15535,13 +15543,6 @@ __metadata: languageName: node linkType: hard -"browser-request@npm:^0.3.3": - version: 0.3.3 - resolution: "browser-request@npm:0.3.3" - checksum: 10/5cecb8f5fbc94a29b758d73458d284e8702810b78f2c5b48120a7cd29ad4675444ca623d6afa1662ef8980fac19b440bd8cb461e8faac494430cf5d22b5ba6e2 - languageName: node - linkType: hard - "browser-resolve@npm:^2.0.0": version: 2.0.0 resolution: "browser-resolve@npm:2.0.0" @@ -15727,15 +15728,6 @@ __metadata: languageName: node linkType: hard -"bs58@npm:^4.0.1": - version: 4.0.1 - resolution: "bs58@npm:4.0.1" - dependencies: - base-x: "npm:^3.0.2" - checksum: 10/b3c5365bb9e0c561e1a82f1a2d809a1a692059fae016be233a6127ad2f50a6b986467c3a50669ce4c18929dcccb297c5909314dd347a25a68c21b68eb3e95ac2 - languageName: node - linkType: hard - "bser@npm:2.1.1": version: 2.1.1 resolution: "bser@npm:2.1.1" @@ -15827,6 +15819,16 @@ __metadata: languageName: node linkType: hard +"buffer@npm:^6.0.3, buffer@npm:~6.0.3": + version: 6.0.3 + resolution: "buffer@npm:6.0.3" + dependencies: + base64-js: "npm:^1.3.1" + ieee754: "npm:^1.2.1" + checksum: 10/b6bc68237ebf29bdacae48ce60e5e28fc53ae886301f2ad9496618efac49427ed79096750033e7eab1897a4f26ae374ace49106a5758f38fb70c78c9fda2c3b1 + languageName: node + linkType: hard + "buffer@npm:~5.2.1": version: 5.2.1 resolution: "buffer@npm:5.2.1" @@ -15837,16 +15839,6 @@ __metadata: languageName: node linkType: hard -"buffer@npm:~6.0.3": - version: 6.0.3 - resolution: "buffer@npm:6.0.3" - dependencies: - base64-js: "npm:^1.3.1" - ieee754: "npm:^1.2.1" - checksum: 10/b6bc68237ebf29bdacae48ce60e5e28fc53ae886301f2ad9496618efac49427ed79096750033e7eab1897a4f26ae374ace49106a5758f38fb70c78c9fda2c3b1 - languageName: node - linkType: hard - "bufrw@npm:^1.3.0": version: 1.3.0 resolution: "bufrw@npm:1.3.0" @@ -16316,7 +16308,7 @@ __metadata: languageName: node linkType: hard -"chalk@npm:4.1.2, chalk@npm:^4, chalk@npm:^4.0.0, chalk@npm:^4.1.0, chalk@npm:^4.1.1, chalk@npm:^4.1.2": +"chalk@npm:4, chalk@npm:4.1.2, chalk@npm:^4.0.0, chalk@npm:^4.1.0, chalk@npm:^4.1.1, chalk@npm:^4.1.2": version: 4.1.2 resolution: "chalk@npm:4.1.2" dependencies: @@ -17233,7 +17225,7 @@ __metadata: languageName: node linkType: hard -"content-type@npm:^1.0.4, content-type@npm:~1.0.4, content-type@npm:~1.0.5": +"content-type@npm:~1.0.4, content-type@npm:~1.0.5": version: 1.0.5 resolution: "content-type@npm:1.0.5" checksum: 10/585847d98dc7fb8035c02ae2cb76c7a9bd7b25f84c447e5ed55c45c2175e83617c8813871b4ee22f368126af6b2b167df655829007b21aa10302873ea9c62662 @@ -17280,6 +17272,13 @@ __metadata: languageName: node linkType: hard +"cookie@npm:0.5.0, cookie@npm:^0.5.0": + version: 0.5.0 + resolution: "cookie@npm:0.5.0" + checksum: 10/aae7911ddc5f444a9025fbd979ad1b5d60191011339bce48e555cb83343d0f98b865ff5c4d71fecdfb8555a5cafdc65632f6fce172f32aaf6936830a883a0380 + languageName: node + linkType: hard + "cookie@npm:0.7.1": version: 0.7.1 resolution: "cookie@npm:0.7.1" @@ -17294,13 +17293,6 @@ __metadata: languageName: node linkType: hard -"cookie@npm:^0.5.0": - version: 0.5.0 - resolution: "cookie@npm:0.5.0" - checksum: 10/aae7911ddc5f444a9025fbd979ad1b5d60191011339bce48e555cb83343d0f98b865ff5c4d71fecdfb8555a5cafdc65632f6fce172f32aaf6936830a883a0380 - languageName: node - linkType: hard - "cookiejar@npm:^2.1.3": version: 2.1.3 resolution: "cookiejar@npm:2.1.3" @@ -18581,6 +18573,13 @@ __metadata: languageName: node linkType: hard +"deepmerge@npm:^4.3.1": + version: 4.3.1 + resolution: "deepmerge@npm:4.3.1" + checksum: 10/058d9e1b0ff1a154468bf3837aea436abcfea1ba1d165ddaaf48ca93765fdd01a30d33c36173da8fbbed951dd0a267602bc782fe288b0fc4b7e1e7091afc4529 + languageName: node + linkType: hard + "default-browser-id@npm:^5.0.0": version: 5.0.0 resolution: "default-browser-id@npm:5.0.0" @@ -20812,16 +20811,55 @@ __metadata: languageName: node linkType: hard -"express-rate-limit@npm:^6.2.0": - version: 6.4.0 - resolution: "express-rate-limit@npm:6.4.0" +"express-rate-limit@npm:^7.1.5": + version: 7.4.1 + resolution: "express-rate-limit@npm:7.4.1" peerDependencies: - express: ^4 || ^5 - checksum: 10/07a22f43e0a0f3598f153fd981a9e784a59c1d3f3e05d5ecc57088339060e4291e685aa1454bcfd07594dacd93733f2d3171f0387482803faa2b8846768c0268 + express: 4 || 5 || ^5.0.0-beta.1 + checksum: 10/230cebc90d9a6baf0b471fa9039b5bf3d82f0a29dc7b304adee38eaa4803493266584108ca3d79d21993bdd45f9497c0b4eac9db8037cd3f10b19c529a9bdf66 + languageName: node + linkType: hard + +"express@npm:^4.16.4, express@npm:^4.17.3": + version: 4.18.2 + resolution: "express@npm:4.18.2" + dependencies: + accepts: "npm:~1.3.8" + array-flatten: "npm:1.1.1" + body-parser: "npm:1.20.1" + content-disposition: "npm:0.5.4" + content-type: "npm:~1.0.4" + cookie: "npm:0.5.0" + cookie-signature: "npm:1.0.6" + debug: "npm:2.6.9" + depd: "npm:2.0.0" + encodeurl: "npm:~1.0.2" + escape-html: "npm:~1.0.3" + etag: "npm:~1.8.1" + finalhandler: "npm:1.2.0" + fresh: "npm:0.5.2" + http-errors: "npm:2.0.0" + merge-descriptors: "npm:1.0.1" + methods: "npm:~1.1.2" + on-finished: "npm:2.4.1" + parseurl: "npm:~1.3.3" + path-to-regexp: "npm:0.1.7" + proxy-addr: "npm:~2.0.7" + qs: "npm:6.11.0" + range-parser: "npm:~1.2.1" + safe-buffer: "npm:5.2.1" + send: "npm:0.18.0" + serve-static: "npm:1.15.0" + setprototypeof: "npm:1.2.0" + statuses: "npm:2.0.1" + type-is: "npm:~1.6.18" + utils-merge: "npm:1.0.1" + vary: "npm:~1.1.2" + checksum: 10/869ae89ed6ff4bed7b373079dc58e5dddcf2915a2669b36037ff78c99d675ae930e5fe052b35c24f56557d28a023bb1cbe3e2f2fb87eaab96a1cedd7e597809d languageName: node linkType: hard -"express@npm:^4.16.4, express@npm:^4.17.1, express@npm:^4.17.2, express@npm:^4.17.3, express@npm:^4.19.2": +"express@npm:^4.18.1, express@npm:^4.18.2, express@npm:^4.19.2": version: 4.21.1 resolution: "express@npm:4.21.1" dependencies: @@ -21313,6 +21351,21 @@ __metadata: languageName: node linkType: hard +"finalhandler@npm:1.2.0": + version: 1.2.0 + resolution: "finalhandler@npm:1.2.0" + dependencies: + debug: "npm:2.6.9" + encodeurl: "npm:~1.0.2" + escape-html: "npm:~1.0.3" + on-finished: "npm:2.4.1" + parseurl: "npm:~1.3.3" + statuses: "npm:2.0.1" + unpipe: "npm:~1.0.0" + checksum: 10/635718cb203c6d18e6b48dfbb6c54ccb08ea470e4f474ddcef38c47edcf3227feec316f886dd701235997d8af35240cae49856721ce18f539ad038665ebbf163 + languageName: node + linkType: hard + "finalhandler@npm:1.3.1": version: 1.3.1 resolution: "finalhandler@npm:1.3.1" @@ -21481,7 +21534,17 @@ __metadata: languageName: node linkType: hard -"follow-redirects@npm:^1.0.0, follow-redirects@npm:^1.14.0, follow-redirects@npm:^1.14.4, follow-redirects@npm:^1.14.7, follow-redirects@npm:^1.14.8, follow-redirects@npm:^1.14.9, follow-redirects@npm:^1.15.6": +"follow-redirects@npm:^1.0.0, follow-redirects@npm:^1.14.0, follow-redirects@npm:^1.14.7, follow-redirects@npm:^1.14.8, follow-redirects@npm:^1.14.9": + version: 1.15.4 + resolution: "follow-redirects@npm:1.15.4" + peerDependenciesMeta: + debug: + optional: true + checksum: 10/2e8f5f259a6b02dfa8dc199e08431848a7c3beed32eb4c19945966164a52c89f07b86c3afcc32ebe4279cf0a960520e45a63013d6350309c5ec90133c5d9351a + languageName: node + linkType: hard + +"follow-redirects@npm:^1.15.6": version: 1.15.9 resolution: "follow-redirects@npm:1.15.9" peerDependenciesMeta: @@ -22184,7 +22247,7 @@ __metadata: languageName: node linkType: hard -"glob@npm:^7.0.0, glob@npm:^7.0.3, glob@npm:^7.1.0, glob@npm:^7.1.3, glob@npm:^7.1.4, glob@npm:^7.1.6, glob@npm:^7.2.0": +"glob@npm:^7.0.3, glob@npm:^7.1.0, glob@npm:^7.1.3, glob@npm:^7.1.4, glob@npm:^7.1.6, glob@npm:^7.2.0": version: 7.2.3 resolution: "glob@npm:7.2.3" dependencies: @@ -22965,19 +23028,16 @@ __metadata: languageName: node linkType: hard -"html-to-text@npm:^8.1.0": - version: 8.2.0 - resolution: "html-to-text@npm:8.2.0" +"html-to-text@npm:^9.0.5": + version: 9.0.5 + resolution: "html-to-text@npm:9.0.5" dependencies: - "@selderee/plugin-htmlparser2": "npm:^0.6.0" - deepmerge: "npm:^4.2.2" - he: "npm:^1.2.0" - htmlparser2: "npm:^6.1.0" - minimist: "npm:^1.2.6" - selderee: "npm:^0.6.0" - bin: - html-to-text: bin/cli.js - checksum: 10/3aed60f9aa90932def41a26b2e8f394b493377d34ed76da705ad2e9048cdc5a48d8aa6c2ad8dcaa65a2654a25ee46548675cd88ca3b201c379ff3347af3c5b40 + "@selderee/plugin-htmlparser2": "npm:^0.11.0" + deepmerge: "npm:^4.3.1" + dom-serializer: "npm:^2.0.0" + htmlparser2: "npm:^8.0.2" + selderee: "npm:^0.11.0" + checksum: 10/e5991f9946dd0e5c91c4ed863c71a4feaef3d5ce85cd8684fb0f2fc175b1ccee323bb97a1773b6bebc47ac7963dbbfd1fc81b024adff705ae7c0e08992d1dba5 languageName: node linkType: hard @@ -23043,7 +23103,7 @@ __metadata: languageName: node linkType: hard -"htmlparser2@npm:^6.0.0, htmlparser2@npm:^6.1.0": +"htmlparser2@npm:^6.1.0": version: 6.1.0 resolution: "htmlparser2@npm:6.1.0" dependencies: @@ -23055,7 +23115,7 @@ __metadata: languageName: node linkType: hard -"htmlparser2@npm:^8.0.0": +"htmlparser2@npm:^8.0.0, htmlparser2@npm:^8.0.2": version: 8.0.2 resolution: "htmlparser2@npm:8.0.2" dependencies: @@ -23784,13 +23844,6 @@ __metadata: languageName: node linkType: hard -"interpret@npm:^1.0.0": - version: 1.4.0 - resolution: "interpret@npm:1.4.0" - checksum: 10/5beec568d3f60543d0f61f2c5969d44dffcb1a372fe5abcdb8013968114d4e4aaac06bc971a4c9f5bd52d150881d8ebad72a8c60686b1361f5f0522f39c0e1a3 - languageName: node - linkType: hard - "interpret@npm:^3.1.1": version: 3.1.1 resolution: "interpret@npm:3.1.1" @@ -23839,13 +23892,13 @@ __metadata: languageName: node linkType: hard -"ip-cidr@npm:^3.0.4": - version: 3.0.7 - resolution: "ip-cidr@npm:3.0.7" +"ip-cidr@npm:^3.0.0": + version: 3.1.0 + resolution: "ip-cidr@npm:3.1.0" dependencies: ip-address: "npm:^7.1.0" jsbn: "npm:^1.1.0" - checksum: 10/2304d34a1ab470bb0acd73fde7bab05ab49466b636db953511c95125662168d00fe6ff2a3dc9a40da4642ddb0c8db1eb9160b080bcead5bec8599d594efecd39 + checksum: 10/c1f4e8f6d781ea8e6fae9858a11a6ccce598d325783c6acf67f0f9350f215c5452a8cc79e678e164663b8ce30558300f58dbcc9c09ac5e9ce6e5d1f40dcc393a languageName: node linkType: hard @@ -26140,6 +26193,13 @@ __metadata: languageName: node linkType: hard +"leac@npm:^0.6.0": + version: 0.6.0 + resolution: "leac@npm:0.6.0" + checksum: 10/bfe6aa128ca98664f124096f65584778194a8e1ddebf77d315fd9681be849c1619b0a9d9f4743e67aea0298808bd69ef25bd74320cad50a80be6852714627870 + languageName: node + linkType: hard + "leven@npm:2.1.0": version: 2.1.0 resolution: "leven@npm:2.1.0" @@ -26580,7 +26640,7 @@ __metadata: languageName: node linkType: hard -"logform@npm:^2.3.2, logform@npm:^2.4.0": +"logform@npm:^2.3.2": version: 2.4.0 resolution: "logform@npm:2.4.0" dependencies: @@ -26593,10 +26653,17 @@ __metadata: languageName: node linkType: hard -"loglevel@npm:^1.7.1": - version: 1.8.0 - resolution: "loglevel@npm:1.8.0" - checksum: 10/72d700ea698b675f8d8d952539de5c3c04acdf95ea7990d991931c53c8731c682432d9c69ae96ac9c52193f59819f18e99b20f9c21ca1ac6535b002152fa783d +"logform@npm:^2.6.0, logform@npm:^2.6.1": + version: 2.6.1 + resolution: "logform@npm:2.6.1" + dependencies: + "@colors/colors": "npm:1.6.0" + "@types/triple-beam": "npm:^1.3.2" + fecha: "npm:^4.2.0" + ms: "npm:^2.1.1" + safe-stable-stringify: "npm:^2.3.1" + triple-beam: "npm:^1.3.0" + checksum: 10/e67f414787fbfe1e6a997f4c84300c7e06bee3d0bd579778af667e24b36db3ea200ed195d41b61311ff738dab7faabc615a07b174b22fe69e0b2f39e985be64b languageName: node linkType: hard @@ -26659,7 +26726,7 @@ __metadata: languageName: node linkType: hard -"lowdb@npm:^1": +"lowdb@npm:1": version: 1.0.0 resolution: "lowdb@npm:1.0.0" dependencies: @@ -26695,6 +26762,13 @@ __metadata: languageName: node linkType: hard +"lru-cache@npm:^10.0.1": + version: 10.4.3 + resolution: "lru-cache@npm:10.4.3" + checksum: 10/e6e90267360476720fa8e83cc168aa2bf0311f3f2eea20a6ba78b90a885ae72071d9db132f40fda4129c803e7dcec3a6b6a6fbb44ca90b081630b810b5d6a41a + languageName: node + linkType: hard + "lru-cache@npm:^11.0.0": version: 11.0.1 resolution: "lru-cache@npm:11.0.1" @@ -26954,34 +27028,43 @@ __metadata: languageName: node linkType: hard -"matrix-appservice@npm:^0.10.0": - version: 0.10.0 - resolution: "matrix-appservice@npm:0.10.0" +"matrix-appservice-bridge@npm:^10.3.1": + version: 10.3.1 + resolution: "matrix-appservice-bridge@npm:10.3.1" dependencies: - "@types/express": "npm:^4.17.8" - body-parser: "npm:^1.19.0" - express: "npm:^4.17.1" - js-yaml: "npm:^4.1.0" - morgan: "npm:^1.10.0" - checksum: 10/23002c2cf28cd789d6b998ea50c8cbacf39133d0f7014d5cc4344b2c2c7f353eb1264a95e6bc92a75cd371bb6ce6c7b01e37d2c94ca412040e189cff1db58ee3 + "@alloc/quick-lru": "npm:^5.2.0" + "@types/nedb": "npm:^1.8.16" + "@vector-im/matrix-bot-sdk": "npm:^0.7.1-element.6" + chalk: "npm:^4.1.0" + express: "npm:^4.18.2" + express-rate-limit: "npm:^7.1.5" + extend: "npm:^3.0.2" + ip-cidr: "npm:^3.0.0" + is-my-json-valid: "npm:^2.20.5" + js-yaml: "npm:^4.0.0" + matrix-appservice: "npm:^2.0.0" + nopt: "npm:^5.0.0" + p-queue: "npm:^6.6.2" + pkginfo: "npm:^0.4.1" + postgres: "npm:^3.4.3" + prom-client: "npm:^15.1.0" + winston: "npm:^3.11.0" + winston-daily-rotate-file: "npm:^4.5.1" + peerDependencies: + nedb: ^1.8.0 + checksum: 10/79d47223d1aa3a536017cd86b44eeb9a1e9c47e478d6c148d5aa13d3fb4f8ffc58ee26a2f32a4924b4c498fec97a090e60705380700d28b6544500b7145ba5c6 languageName: node linkType: hard -"matrix-js-sdk@npm:^12.4.1": - version: 12.5.0 - resolution: "matrix-js-sdk@npm:12.5.0" +"matrix-appservice@npm:^2.0.0": + version: 2.0.0 + resolution: "matrix-appservice@npm:2.0.0" dependencies: - "@babel/runtime": "npm:^7.12.5" - another-json: "npm:^0.2.0" - browser-request: "npm:^0.3.3" - bs58: "npm:^4.0.1" - content-type: "npm:^1.0.4" - loglevel: "npm:^1.7.1" - p-retry: "npm:^4.5.0" - qs: "npm:^6.9.6" - request: "npm:^2.88.2" - unhomoglyph: "npm:^1.0.6" - checksum: 10/de03988f798659afd26b6e63b4bae730db596a4c3f09fe59660db01d7ee6bdf99122807b50187aa5fa1b671c27c1ef47f84b0ee644d0423c1d7b3ccda76b9b36 + body-parser: "npm:^1.19.0" + express: "npm:^4.18.1" + js-yaml: "npm:^4.1.0" + morgan: "npm:^1.10.0" + checksum: 10/6c3bcc4f8ab6336c9c88193279eea27b0d2ff9c0f9a88aa309725cfa65818cd20c975ee5564b83ef318a5061873a831ec0636364f48b168e71b4c2e2d91a61e7 languageName: node linkType: hard @@ -27224,6 +27307,13 @@ __metadata: languageName: node linkType: hard +"merge-descriptors@npm:1.0.1": + version: 1.0.1 + resolution: "merge-descriptors@npm:1.0.1" + checksum: 10/5abc259d2ae25bb06d19ce2b94a21632583c74e2a9109ee1ba7fd147aa7362b380d971e0251069f8b3eb7d48c21ac839e21fa177b335e82c76ec172e30c31a26 + languageName: node + linkType: hard + "merge-descriptors@npm:1.0.3, merge-descriptors@npm:~1.0.0": version: 1.0.3 resolution: "merge-descriptors@npm:1.0.3" @@ -27686,6 +27776,15 @@ __metadata: languageName: node linkType: hard +"mkdirp@npm:^3.0.1": + version: 3.0.1 + resolution: "mkdirp@npm:3.0.1" + bin: + mkdirp: dist/cjs/src/bin.js + checksum: 10/16fd79c28645759505914561e249b9a1f5fe3362279ad95487a4501e4467abeb714fd35b95307326b8fd03f3c7719065ef11a6f97b7285d7888306d1bd2232ba + languageName: node + linkType: hard + "mocha@npm:^9.2.2": version: 9.2.2 resolution: "mocha@npm:9.2.2" @@ -28345,6 +28444,15 @@ __metadata: languageName: node linkType: hard +"node-downloader-helper@npm:^2.1.5": + version: 2.1.9 + resolution: "node-downloader-helper@npm:2.1.9" + bin: + ndh: bin/ndh + checksum: 10/c25f23a5a8b6c1be61b7b3fa8b075bc3e4bdd2a6bf9cc7927e7813942cf503614fcf7cd23025a334152b1a84b086b7c90fbf0f7af161929a1d61d3e51de3c337 + languageName: node + linkType: hard + "node-fetch@npm:2.6.7": version: 2.6.7 resolution: "node-fetch@npm:2.6.7" @@ -29380,7 +29488,7 @@ __metadata: languageName: node linkType: hard -"p-retry@npm:^4.0.0, p-retry@npm:^4.5.0": +"p-retry@npm:^4.0.0": version: 4.6.2 resolution: "p-retry@npm:4.6.2" dependencies: @@ -29609,6 +29717,16 @@ __metadata: languageName: node linkType: hard +"parseley@npm:^0.12.0": + version: 0.12.1 + resolution: "parseley@npm:0.12.1" + dependencies: + leac: "npm:^0.6.0" + peberminta: "npm:^0.9.0" + checksum: 10/64788dbe1fbbc231e0fef235357823e03ca3d915693b2109ad862293aad5d091e902fd7cf6f54763728e758a228d06497c787a9af0dfdacd6a941bc2dbf2019e + languageName: node + linkType: hard + "parseley@npm:^0.7.0": version: 0.7.0 resolution: "parseley@npm:0.7.0" @@ -29747,6 +29865,13 @@ __metadata: languageName: node linkType: hard +"path-to-regexp@npm:0.1.7": + version: 0.1.7 + resolution: "path-to-regexp@npm:0.1.7" + checksum: 10/701c99e1f08e3400bea4d701cf6f03517474bb1b608da71c78b1eb261415b645c5670dfae49808c89e12cea2dccd113b069f040a80de012da0400191c6dbd1c8 + languageName: node + linkType: hard + "path-to-regexp@npm:2.2.1": version: 2.2.1 resolution: "path-to-regexp@npm:2.2.1" @@ -29870,6 +29995,13 @@ __metadata: languageName: node linkType: hard +"peberminta@npm:^0.9.0": + version: 0.9.0 + resolution: "peberminta@npm:0.9.0" + checksum: 10/b396cf8bac836b3cfe9315e6c94747fa02bb68b252a4b176c0f7d6a3fcbdc5e9f23c3523480c91244b42f87be63f200d775587b039abe4b4731915480da2528d + languageName: node + linkType: hard + "peek-readable@npm:^4.1.0": version: 4.1.0 resolution: "peek-readable@npm:4.1.0" @@ -30091,6 +30223,13 @@ __metadata: languageName: node linkType: hard +"pkginfo@npm:^0.4.1": + version: 0.4.1 + resolution: "pkginfo@npm:0.4.1" + checksum: 10/e354d6f78a940da07f36cdc503705e78bf53044f1562f29f46f18d2a21d271b203d30eb738468cf7c68b5739cc1d7383646cd5930bd42bfe9a81b03a5a8e860c + languageName: node + linkType: hard + "playwright-core@npm:1.40.1": version: 1.40.1 resolution: "playwright-core@npm:1.40.1" @@ -30897,6 +31036,13 @@ __metadata: languageName: node linkType: hard +"postgres@npm:^3.4.1, postgres@npm:^3.4.3": + version: 3.4.4 + resolution: "postgres@npm:3.4.4" + checksum: 10/2b8c511f2dd679b91264bb3033c8d18d2ad10a5fc9aca2049eda13d6b68ae96fac45d47cfaeb0b66482ff18cf7175a5562e69a1cf259e892d063fb60d2178758 + languageName: node + linkType: hard + "postis@npm:^2.2.0": version: 2.2.0 resolution: "postis@npm:2.2.0" @@ -31102,7 +31248,7 @@ __metadata: languageName: node linkType: hard -"prom-client@npm:^14.0.0, prom-client@npm:^14.2.0": +"prom-client@npm:^14.2.0": version: 14.2.0 resolution: "prom-client@npm:14.2.0" dependencies: @@ -31111,6 +31257,16 @@ __metadata: languageName: node linkType: hard +"prom-client@npm:^15.1.0": + version: 15.1.3 + resolution: "prom-client@npm:15.1.3" + dependencies: + "@opentelemetry/api": "npm:^1.4.0" + tdigest: "npm:^0.1.1" + checksum: 10/eba75e15ab896845d39359e3a4d6f7913ea05339b3122d8dde8c8c374669ad1a1d1ab2694ab2101c420bd98086a564e4f2a18aa29018fc14a4732e57c1c19aec + languageName: node + linkType: hard + "prometheus-gc-stats@npm:^0.6.5": version: 0.6.5 resolution: "prometheus-gc-stats@npm:0.6.5" @@ -31352,7 +31508,7 @@ __metadata: languageName: node linkType: hard -"qs@npm:6.13.0, qs@npm:^6.10.3, qs@npm:^6.12.3, qs@npm:^6.9.4, qs@npm:^6.9.6": +"qs@npm:6.13.0, qs@npm:^6.10.3, qs@npm:^6.12.3, qs@npm:^6.9.4": version: 6.13.0 resolution: "qs@npm:6.13.0" dependencies: @@ -31628,6 +31784,18 @@ __metadata: languageName: node linkType: hard +"raw-body@npm:2.5.1": + version: 2.5.1 + resolution: "raw-body@npm:2.5.1" + dependencies: + bytes: "npm:3.1.2" + http-errors: "npm:2.0.0" + iconv-lite: "npm:0.4.24" + unpipe: "npm:1.0.0" + checksum: 10/280bedc12db3490ecd06f740bdcf66093a07535374b51331242382c0e130bb273ebb611b7bc4cba1b4b4e016cc7b1f4b05a6df885a6af39c2bc3b94c02291c84 + languageName: node + linkType: hard + "raw-body@npm:2.5.2, raw-body@npm:^2.3.3": version: 2.5.2 resolution: "raw-body@npm:2.5.2" @@ -32279,6 +32447,19 @@ __metadata: languageName: node linkType: hard +"readable-stream@npm:^4.5.2": + version: 4.5.2 + resolution: "readable-stream@npm:4.5.2" + dependencies: + abort-controller: "npm:^3.0.0" + buffer: "npm:^6.0.3" + events: "npm:^3.3.0" + process: "npm:^0.11.10" + string_decoder: "npm:^1.3.0" + checksum: 10/01b128a559c5fd76a898495f858cf0a8839f135e6a69e3409f986e88460134791657eb46a2ff16826f331682a3c4d0c5a75cef5e52ef259711021ba52b1c2e82 + languageName: node + linkType: hard + "readable-stream@npm:~2.0.5": version: 2.0.6 resolution: "readable-stream@npm:2.0.6" @@ -32338,15 +32519,6 @@ __metadata: languageName: node linkType: hard -"rechoir@npm:^0.6.2": - version: 0.6.2 - resolution: "rechoir@npm:0.6.2" - dependencies: - resolve: "npm:^1.1.6" - checksum: 10/fe76bf9c21875ac16e235defedd7cbd34f333c02a92546142b7911a0f7c7059d2e16f441fe6fb9ae203f459c05a31b2bcf26202896d89e390eda7514d5d2702b - languageName: node - linkType: hard - "rechoir@npm:^0.8.0": version: 0.8.0 resolution: "rechoir@npm:0.8.0" @@ -32818,7 +32990,7 @@ __metadata: languageName: node linkType: hard -"resolve@npm:^1.1.4, resolve@npm:^1.1.6, resolve@npm:^1.1.7, resolve@npm:^1.10.0, resolve@npm:^1.11.1, resolve@npm:^1.14.2, resolve@npm:^1.17.0, resolve@npm:^1.20.0, resolve@npm:^1.22.0, resolve@npm:^1.22.1, resolve@npm:^1.22.2, resolve@npm:^1.22.8, resolve@npm:^1.4.0": +"resolve@npm:^1.1.4, resolve@npm:^1.1.7, resolve@npm:^1.10.0, resolve@npm:^1.11.1, resolve@npm:^1.14.2, resolve@npm:^1.17.0, resolve@npm:^1.20.0, resolve@npm:^1.22.0, resolve@npm:^1.22.1, resolve@npm:^1.22.2, resolve@npm:^1.22.8, resolve@npm:^1.4.0": version: 1.22.8 resolution: "resolve@npm:1.22.8" dependencies: @@ -32844,7 +33016,7 @@ __metadata: languageName: node linkType: hard -"resolve@patch:resolve@npm%3A^1.1.4#optional!builtin, resolve@patch:resolve@npm%3A^1.1.6#optional!builtin, resolve@patch:resolve@npm%3A^1.1.7#optional!builtin, resolve@patch:resolve@npm%3A^1.10.0#optional!builtin, resolve@patch:resolve@npm%3A^1.11.1#optional!builtin, resolve@patch:resolve@npm%3A^1.14.2#optional!builtin, resolve@patch:resolve@npm%3A^1.17.0#optional!builtin, resolve@patch:resolve@npm%3A^1.20.0#optional!builtin, resolve@patch:resolve@npm%3A^1.22.0#optional!builtin, resolve@patch:resolve@npm%3A^1.22.1#optional!builtin, resolve@patch:resolve@npm%3A^1.22.2#optional!builtin, resolve@patch:resolve@npm%3A^1.22.8#optional!builtin, resolve@patch:resolve@npm%3A^1.4.0#optional!builtin": +"resolve@patch:resolve@npm%3A^1.1.4#optional!builtin, resolve@patch:resolve@npm%3A^1.1.7#optional!builtin, resolve@patch:resolve@npm%3A^1.10.0#optional!builtin, resolve@patch:resolve@npm%3A^1.11.1#optional!builtin, resolve@patch:resolve@npm%3A^1.14.2#optional!builtin, resolve@patch:resolve@npm%3A^1.17.0#optional!builtin, resolve@patch:resolve@npm%3A^1.20.0#optional!builtin, resolve@patch:resolve@npm%3A^1.22.0#optional!builtin, resolve@patch:resolve@npm%3A^1.22.1#optional!builtin, resolve@patch:resolve@npm%3A^1.22.2#optional!builtin, resolve@patch:resolve@npm%3A^1.22.8#optional!builtin, resolve@patch:resolve@npm%3A^1.4.0#optional!builtin": version: 1.22.8 resolution: "resolve@patch:resolve@npm%3A1.22.8#optional!builtin::version=1.22.8&hash=c3c19d" dependencies: @@ -33205,7 +33377,7 @@ __metadata: languageName: node linkType: hard -"sanitize-html@npm:^2.13.1": +"sanitize-html@npm:^2.11.0, sanitize-html@npm:^2.13.1": version: 2.13.1 resolution: "sanitize-html@npm:2.13.1" dependencies: @@ -33219,20 +33391,6 @@ __metadata: languageName: node linkType: hard -"sanitize-html@npm:^2.6.1": - version: 2.7.2 - resolution: "sanitize-html@npm:2.7.2" - dependencies: - deepmerge: "npm:^4.2.2" - escape-string-regexp: "npm:^4.0.0" - htmlparser2: "npm:^6.0.0" - is-plain-object: "npm:^5.0.0" - parse-srcset: "npm:^1.0.2" - postcss: "npm:^8.3.11" - checksum: 10/65545224c6ec293066890c7ba309019185c0d82846e939650edb44337f0ec3560e5561dde7272c531ee4df561318ccc93a23b02f0484efccab2e412e63f8bae5 - languageName: node - linkType: hard - "sass-loader@npm:~16.0.2": version: 16.0.2 resolution: "sass-loader@npm:16.0.2" @@ -33406,6 +33564,15 @@ __metadata: languageName: node linkType: hard +"selderee@npm:^0.11.0": + version: 0.11.0 + resolution: "selderee@npm:0.11.0" + dependencies: + parseley: "npm:^0.12.0" + checksum: 10/9f697a00b8270354777a8423e555fd3168abead1304b8d267412877a4b007830624d8aa562eb29a3ec2d9d2f7f977808d17b790b9c210a7d828c12ed9ef0f1f0 + languageName: node + linkType: hard + "selderee@npm:^0.6.0": version: 0.6.0 resolution: "selderee@npm:0.6.0" @@ -33507,6 +33674,27 @@ __metadata: languageName: node linkType: hard +"send@npm:0.18.0": + version: 0.18.0 + resolution: "send@npm:0.18.0" + dependencies: + debug: "npm:2.6.9" + depd: "npm:2.0.0" + destroy: "npm:1.2.0" + encodeurl: "npm:~1.0.2" + escape-html: "npm:~1.0.3" + etag: "npm:~1.8.1" + fresh: "npm:0.5.2" + http-errors: "npm:2.0.0" + mime: "npm:1.6.0" + ms: "npm:2.1.3" + on-finished: "npm:2.4.1" + range-parser: "npm:~1.2.1" + statuses: "npm:2.0.1" + checksum: 10/ec66c0ad109680ad8141d507677cfd8b4e40b9559de23191871803ed241718e99026faa46c398dcfb9250676076573bd6bfe5d0ec347f88f4b7b8533d1d391cb + languageName: node + linkType: hard + "send@npm:0.19.0": version: 0.19.0 resolution: "send@npm:0.19.0" @@ -33597,6 +33785,18 @@ __metadata: languageName: node linkType: hard +"serve-static@npm:1.15.0": + version: 1.15.0 + resolution: "serve-static@npm:1.15.0" + dependencies: + encodeurl: "npm:~1.0.2" + escape-html: "npm:~1.0.3" + parseurl: "npm:~1.3.3" + send: "npm:0.18.0" + checksum: 10/699b2d4c29807a51d9b5e0f24955346911437aebb0178b3c4833ad30d3eca93385ff9927254f5c16da345903cad39d9cd4a532198c95a5129cc4ed43911b15a4 + languageName: node + linkType: hard + "serve-static@npm:1.16.2": version: 1.16.2 resolution: "serve-static@npm:1.16.2" @@ -33787,19 +33987,6 @@ __metadata: languageName: node linkType: hard -"shelljs@npm:^0.8.4": - version: 0.8.5 - resolution: "shelljs@npm:0.8.5" - dependencies: - glob: "npm:^7.0.0" - interpret: "npm:^1.0.0" - rechoir: "npm:^0.6.2" - bin: - shjs: bin/shjs - checksum: 10/f2178274b97b44332bbe9ddb78161137054f55ecf701c7a99db9552cb5478fe279ad5f5131d8a7c2f0730e01ccf0c629d01094143f0541962ce1a3d0243d23f7 - languageName: node - linkType: hard - "shiki@npm:^0.14.1": version: 0.14.7 resolution: "shiki@npm:0.14.7" @@ -36866,13 +37053,6 @@ __metadata: languageName: node linkType: hard -"unhomoglyph@npm:^1.0.6": - version: 1.0.6 - resolution: "unhomoglyph@npm:1.0.6" - checksum: 10/96442934bd16b62e6261fbd9381d9baaa910e2720006ef6b6a270e810b3c867226436353f024e85e5d5270acf9cf9e51d2f7982a4b7c12392a5143bd5d798640 - languageName: node - linkType: hard - "unicode-canonical-property-names-ecmascript@npm:^2.0.0": version: 2.0.0 resolution: "unicode-canonical-property-names-ecmascript@npm:2.0.0" @@ -38304,7 +38484,7 @@ __metadata: languageName: node linkType: hard -"winston-transport@npm:^4.4.0, winston-transport@npm:^4.5.0": +"winston-transport@npm:^4.4.0": version: 4.5.0 resolution: "winston-transport@npm:4.5.0" dependencies: @@ -38315,21 +38495,33 @@ __metadata: languageName: node linkType: hard -"winston@npm:^3.3.3": - version: 3.7.2 - resolution: "winston@npm:3.7.2" +"winston-transport@npm:^4.7.0": + version: 4.8.0 + resolution: "winston-transport@npm:4.8.0" + dependencies: + logform: "npm:^2.6.1" + readable-stream: "npm:^4.5.2" + triple-beam: "npm:^1.3.0" + checksum: 10/930bdc0ec689d5c4f07a262721da80440336f64739d0ce33db801c7142b4fca5be8ef71b725b670bac609de8b6bce405e5c5f84d355f5176a611209b476cee18 + languageName: node + linkType: hard + +"winston@npm:^3.11.0": + version: 3.15.0 + resolution: "winston@npm:3.15.0" dependencies: + "@colors/colors": "npm:^1.6.0" "@dabh/diagnostics": "npm:^2.0.2" async: "npm:^3.2.3" is-stream: "npm:^2.0.0" - logform: "npm:^2.4.0" + logform: "npm:^2.6.0" one-time: "npm:^1.0.0" readable-stream: "npm:^3.4.0" safe-stable-stringify: "npm:^2.3.1" stack-trace: "npm:0.0.x" triple-beam: "npm:^1.3.0" - winston-transport: "npm:^4.5.0" - checksum: 10/3e2cfd0097f23e03fd6e60eaaabf54b8a1baeb3d6fae5a0bccd7e63e25d70d8d3f9879e7ea3bd1e5f774005543bb285b6e52d7223e8cea14e4835e2272a49f24 + winston-transport: "npm:^4.7.0" + checksum: 10/60e55eb3621e4de1a764a4e43ee1d242c71957d3e0eb359cb8f16fe2b9d9543fd4c31a8d3baf96fa7e43ef5df383c43c1a98aff4bd714ea0082303504b0e3cdc languageName: node linkType: hard From 10ee0653807243fa570455b3db4a653c36adf621 Mon Sep 17 00:00:00 2001 From: Yash Rajpal <58601732+yash-rajpal@users.noreply.github.com> Date: Sat, 19 Oct 2024 02:21:43 +0530 Subject: [PATCH 17/25] regression: Move WorkspaceCredentials models to CE (#33657) --- apps/meteor/ee/server/models/startup.ts | 1 - apps/meteor/{ee => }/server/models/WorkspaceCredentials.ts | 2 +- apps/meteor/{ee => }/server/models/raw/WorkspaceCredentials.ts | 2 +- apps/meteor/server/models/startup.ts | 1 + 4 files changed, 3 insertions(+), 3 deletions(-) rename apps/meteor/{ee => }/server/models/WorkspaceCredentials.ts (79%) rename apps/meteor/{ee => }/server/models/raw/WorkspaceCredentials.ts (95%) diff --git a/apps/meteor/ee/server/models/startup.ts b/apps/meteor/ee/server/models/startup.ts index 08605fe2c4d2..f77bcd1d7619 100644 --- a/apps/meteor/ee/server/models/startup.ts +++ b/apps/meteor/ee/server/models/startup.ts @@ -7,7 +7,6 @@ import('./LivechatPriority'); import('./OmnichannelServiceLevelAgreements'); import('./AuditLog'); import('./ReadReceipts'); -import('./WorkspaceCredentials'); void License.onLicense('livechat-enterprise', () => { import('./CannedResponse'); diff --git a/apps/meteor/ee/server/models/WorkspaceCredentials.ts b/apps/meteor/server/models/WorkspaceCredentials.ts similarity index 79% rename from apps/meteor/ee/server/models/WorkspaceCredentials.ts rename to apps/meteor/server/models/WorkspaceCredentials.ts index 26b1f015f067..7901f2bfef2d 100644 --- a/apps/meteor/ee/server/models/WorkspaceCredentials.ts +++ b/apps/meteor/server/models/WorkspaceCredentials.ts @@ -1,6 +1,6 @@ import { registerModel } from '@rocket.chat/models'; -import { db } from '../../../server/database/utils'; +import { db } from '../database/utils'; import { WorkspaceCredentialsRaw } from './raw/WorkspaceCredentials'; registerModel('IWorkspaceCredentialsModel', new WorkspaceCredentialsRaw(db)); diff --git a/apps/meteor/ee/server/models/raw/WorkspaceCredentials.ts b/apps/meteor/server/models/raw/WorkspaceCredentials.ts similarity index 95% rename from apps/meteor/ee/server/models/raw/WorkspaceCredentials.ts rename to apps/meteor/server/models/raw/WorkspaceCredentials.ts index f4141967814d..b989ace8c2a3 100644 --- a/apps/meteor/ee/server/models/raw/WorkspaceCredentials.ts +++ b/apps/meteor/server/models/raw/WorkspaceCredentials.ts @@ -2,7 +2,7 @@ import type { IWorkspaceCredentials } from '@rocket.chat/core-typings'; import type { IWorkspaceCredentialsModel } from '@rocket.chat/model-typings'; import type { Db, DeleteResult, Filter, IndexDescription, UpdateResult } from 'mongodb'; -import { BaseRaw } from '../../../../server/models/raw/BaseRaw'; +import { BaseRaw } from './BaseRaw'; export class WorkspaceCredentialsRaw extends BaseRaw implements IWorkspaceCredentialsModel { constructor(db: Db) { diff --git a/apps/meteor/server/models/startup.ts b/apps/meteor/server/models/startup.ts index eaca155674f5..c3ecc381f7f0 100644 --- a/apps/meteor/server/models/startup.ts +++ b/apps/meteor/server/models/startup.ts @@ -70,3 +70,4 @@ import './AppsTokens'; import './CronHistory'; import './Migrations'; import './ReadReceipts'; +import './WorkspaceCredentials'; From 34ed9ad64661dae0517c6fc51ec3719620c31548 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlia=20Jaeger=20Foresti?= <60678893+juliajforesti@users.noreply.github.com> Date: Fri, 18 Oct 2024 18:07:21 -0300 Subject: [PATCH 18/25] feat: show Recent chats on the new sidebar (#33489) Co-authored-by: Guilherme Gazzo --- .changeset/five-suns-tickle.md | 6 +++ .../client/sidebarv2/header/SearchList.tsx | 9 +++-- .../client/sidebarv2/header/SearchSection.tsx | 37 ++++++++++++++++--- packages/i18n/src/locales/en.i18n.json | 3 +- 4 files changed, 44 insertions(+), 11 deletions(-) create mode 100644 .changeset/five-suns-tickle.md diff --git a/.changeset/five-suns-tickle.md b/.changeset/five-suns-tickle.md new file mode 100644 index 000000000000..740fc3640604 --- /dev/null +++ b/.changeset/five-suns-tickle.md @@ -0,0 +1,6 @@ +--- +'@rocket.chat/i18n': minor +'@rocket.chat/meteor': minor +--- + +Adds `Recent` button on the new sidebar Search section to replicate the previous behavior of focusing the search bar - show recent chats. diff --git a/apps/meteor/client/sidebarv2/header/SearchList.tsx b/apps/meteor/client/sidebarv2/header/SearchList.tsx index b132af80f674..066b82ce7681 100644 --- a/apps/meteor/client/sidebarv2/header/SearchList.tsx +++ b/apps/meteor/client/sidebarv2/header/SearchList.tsx @@ -1,4 +1,4 @@ -import { Box } from '@rocket.chat/fuselage'; +import { Box, SidebarV2GroupTitle } from '@rocket.chat/fuselage'; import { useTranslation, useUserPreference, useSetting } from '@rocket.chat/ui-contexts'; import type { MouseEventHandler, ReactElement } from 'react'; import React, { useMemo, useRef } from 'react'; @@ -12,9 +12,9 @@ import { useTemplateByViewMode } from '../hooks/useTemplateByViewMode'; import Row from '../search/Row'; import { useSearchItems } from './hooks/useSearchItems'; -type SearchListProps = { filterText: string; onEscSearch: () => void }; +type SearchListProps = { filterText: string; onEscSearch: () => void; showRecentList?: boolean }; -const SearchList = ({ filterText, onEscSearch }: SearchListProps) => { +const SearchList = ({ filterText, onEscSearch, showRecentList }: SearchListProps) => { const t = useTranslation(); const boxRef = useRef(null); @@ -58,12 +58,13 @@ const SearchList = ({ filterText, onEscSearch }: SearchListProps) => { flexShrink={1} h='full' w='full' - pbs={8} + pbs={showRecentList ? 0 : 8} aria-live='polite' aria-atomic='true' aria-busy={isLoading} onClick={handleClick} > + {showRecentList && } { return '(Ctrl+K)'; })(); +const isRecentButton = (node: EventTarget) => (node as HTMLElement).title === 'Recent'; + const SearchSection = () => { const t = useTranslation(); + const focusManager = useFocusManager(); const user = useUser(); + const [recentButtonPressed, setRecentButtonPressed] = useState(false); const { formState: { isDirty }, @@ -62,12 +68,15 @@ const SearchSection = () => { const { filterText } = watch(); const { ref: filterRef, ...rest } = register('filterText'); + const showRecentList = Boolean(recentButtonPressed && !filterText); + const inputRef = useRef(null); const wrapperRef = useRef(null); const mergedRefs = useMergedRefs(filterRef, inputRef); const handleEscSearch = useCallback(() => { resetField('filterText'); + setRecentButtonPressed(false); inputRef.current?.blur(); }, [resetField]); @@ -83,6 +92,11 @@ const SearchSection = () => { event.preventDefault(); setFocus('filterText'); }, + 'Shift+$mod+K': (event) => { + event.preventDefault(); + setRecentButtonPressed(true); + focusManager.focusNext({ accept: (node) => isRecentButton(node) }); + }, 'Escape': (event) => { event.preventDefault(); handleEscSearch(); @@ -92,12 +106,12 @@ const SearchSection = () => { return (): void => { unsubscribe(); }; - }, [handleEscSearch, setFocus]); + }, [focusManager, handleEscSearch, setFocus]); const placeholder = [t('Search'), shortcut].filter(Boolean).join(' '); return ( - + { {user && !isDirty && ( <> - + setRecentButtonPressed(!recentButtonPressed)} + pressed={recentButtonPressed} + /> + {recentButtonPressed ? : } )} - {isDirty && } + {(isDirty || recentButtonPressed) && ( + + + + )} ); }; diff --git a/packages/i18n/src/locales/en.i18n.json b/packages/i18n/src/locales/en.i18n.json index 92056badc0b3..9273b890f260 100644 --- a/packages/i18n/src/locales/en.i18n.json +++ b/packages/i18n/src/locales/en.i18n.json @@ -6643,5 +6643,6 @@ "Sidepanel_navigation": "Secondary navigation for teams", "Sidepanel_navigation_description": "Display channels and/or discussions associated with teams by default. This allows team owners to customize communication methods to best meet their team’s needs. This is currently in feature preview and will be a premium capability once fully released.", "Show_channels_description": "Show team channels in second sidebar", - "Show_discussions_description": "Show team discussions in second sidebar" + "Show_discussions_description": "Show team discussions in second sidebar", + "Recent": "Recent" } From 42143e27db7aa3f4668a11f8048169ba5a696f87 Mon Sep 17 00:00:00 2001 From: Debdut Chakraborty Date: Sat, 19 Oct 2024 03:06:59 +0530 Subject: [PATCH 19/25] ci: don't run workflow when docs are updated (#33645) --- .github/workflows/ci.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 98fe8a059543..e70557bbd31d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,9 +5,13 @@ on: types: [published] pull_request: branches: '**' + paths-ignore: + - '**.md' push: branches: - develop + paths-ignore: + - '**.md' concurrency: group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} From e3dac4aab68ee553221c8cd0ee3bdc89b5373745 Mon Sep 17 00:00:00 2001 From: Tiago Evangelista Pinto Date: Fri, 18 Oct 2024 19:12:41 -0300 Subject: [PATCH 20/25] feat: marketplace add-on components (#33483) Co-authored-by: Douglas Gubert <1810309+d-gubert@users.noreply.github.com> Co-authored-by: Tasso Evangelista <2263066+tassoevan@users.noreply.github.com> --- .changeset/tidy-suns-move.md | 6 +++ .../client/hooks/useHasLicenseModule.ts | 4 +- .../views/admin/subscription/utils/links.ts | 1 + .../tabs/AppDetails/AppDetails.tsx | 35 ++++++++++---- .../tabs/AppStatus/AppStatus.tsx | 12 ++++- .../views/marketplace/AppsList/AddonChip.tsx | 24 ++++++++++ .../AppsList/AddonRequiredModal.tsx | 42 ++++++++++++++++ .../views/marketplace/AppsList/AppRow.tsx | 2 + .../views/marketplace/hooks/useAppMenu.tsx | 48 +++++++++++++++---- packages/i18n/src/locales/en.i18n.json | 6 +++ 10 files changed, 159 insertions(+), 21 deletions(-) create mode 100644 .changeset/tidy-suns-move.md create mode 100644 apps/meteor/client/views/marketplace/AppsList/AddonChip.tsx create mode 100644 apps/meteor/client/views/marketplace/AppsList/AddonRequiredModal.tsx diff --git a/.changeset/tidy-suns-move.md b/.changeset/tidy-suns-move.md new file mode 100644 index 000000000000..4d125d059b68 --- /dev/null +++ b/.changeset/tidy-suns-move.md @@ -0,0 +1,6 @@ +--- +"@rocket.chat/meteor": feat +"@rocket.chat/i18n": feat +--- + +Introduces new visual components into marketplace pages to inform an add-on necessity into the workspace. diff --git a/apps/meteor/client/hooks/useHasLicenseModule.ts b/apps/meteor/client/hooks/useHasLicenseModule.ts index ea4080730884..c28713019d4d 100644 --- a/apps/meteor/client/hooks/useHasLicenseModule.ts +++ b/apps/meteor/client/hooks/useHasLicenseModule.ts @@ -2,10 +2,10 @@ import type { LicenseModule } from '@rocket.chat/core-typings'; import { useLicenseBase } from './useLicense'; -export const useHasLicenseModule = (licenseName: LicenseModule): 'loading' | boolean => { +export const useHasLicenseModule = (licenseName: LicenseModule | undefined): 'loading' | boolean => { return ( useLicenseBase({ - select: (data) => data.license.activeModules.includes(licenseName), + select: (data) => !!licenseName && data.license.activeModules.includes(licenseName), }).data ?? 'loading' ); }; diff --git a/apps/meteor/client/views/admin/subscription/utils/links.ts b/apps/meteor/client/views/admin/subscription/utils/links.ts index 3b32bf163412..a4de2bfcdab6 100644 --- a/apps/meteor/client/views/admin/subscription/utils/links.ts +++ b/apps/meteor/client/views/admin/subscription/utils/links.ts @@ -2,3 +2,4 @@ export const CONTACT_SALES_LINK = 'https://go.rocket.chat/i/contact-sales-produc export const PRICING_LINK = 'https://go.rocket.chat/i/pricing-product'; export const DOWNGRADE_LINK = 'https://go.rocket.chat/i/docs-downgrade'; export const TRIAL_LINK = 'https://go.rocket.chat/i/docs-trial'; +export const GET_ADDONS_LINK = 'https://go.rocket.chat/i/get-addons'; diff --git a/apps/meteor/client/views/marketplace/AppDetailsPage/tabs/AppDetails/AppDetails.tsx b/apps/meteor/client/views/marketplace/AppDetailsPage/tabs/AppDetails/AppDetails.tsx index 5d20900078a4..a4178a4e27ae 100644 --- a/apps/meteor/client/views/marketplace/AppDetailsPage/tabs/AppDetails/AppDetails.tsx +++ b/apps/meteor/client/views/marketplace/AppDetailsPage/tabs/AppDetails/AppDetails.tsx @@ -1,10 +1,13 @@ -import { Box, Callout, Chip, Margins } from '@rocket.chat/fuselage'; +import { Box, Button, Callout, Chip, Margins } from '@rocket.chat/fuselage'; import { ExternalLink } from '@rocket.chat/ui-client'; import type { TranslationKey } from '@rocket.chat/ui-contexts'; import DOMPurify from 'dompurify'; import React from 'react'; import { useTranslation } from 'react-i18next'; +import { useExternalLink } from '../../../../../hooks/useExternalLink'; +import { useHasLicenseModule } from '../../../../../hooks/useHasLicenseModule'; +import { GET_ADDONS_LINK } from '../../../../admin/subscription/utils/links'; import ScreenshotCarouselAnchor from '../../../components/ScreenshotCarouselAnchor'; import type { AppInfo } from '../../../definitions/AppInfo'; import { purifyOptions } from '../../../lib/purifyOptions'; @@ -12,10 +15,7 @@ import AppDetailsAPIs from './AppDetailsAPIs'; import { normalizeUrl } from './normalizeUrl'; type AppDetailsProps = { - app: Omit & { - author?: Partial; - documentationUrl?: AppInfo['documentationUrl']; - }; + app: AppInfo; }; const AppDetails = ({ app }: AppDetailsProps) => { @@ -28,6 +28,7 @@ const AppDetails = ({ app }: AppDetailsProps) => { screenshots, apis, documentationUrl: documentation, + addon: appAddon, } = app; const isMarkdown = detailedDescription && Object.keys(detailedDescription).length !== 0 && detailedDescription.rendered; @@ -37,18 +38,36 @@ const AppDetails = ({ app }: AppDetailsProps) => { const normalizedSupportUrl = support ? normalizeUrl(support) : undefined; const normalizedDocumentationUrl = documentation ? normalizeUrl(documentation) : undefined; + const workspaceHasAddon = useHasLicenseModule(appAddon); + + const openExternalLink = useExternalLink(); + return ( - + + {appAddon && !workspaceHasAddon && ( + openExternalLink(GET_ADDONS_LINK)}> + {t('Contact_sales')} + + } + > + {t('App_cannot_be_enabled_without_add-on')} + + )} {app.licenseValidation && ( <> {Object.entries(app.licenseValidation.warnings).map(([key]) => ( - + {t(`Apps_License_Message_${key}` as TranslationKey)} ))} {Object.entries(app.licenseValidation.errors).map(([key]) => ( - + {t(`Apps_License_Message_${key}` as TranslationKey)} ))} diff --git a/apps/meteor/client/views/marketplace/AppDetailsPage/tabs/AppStatus/AppStatus.tsx b/apps/meteor/client/views/marketplace/AppDetailsPage/tabs/AppStatus/AppStatus.tsx index 643dfbd0215b..bda1bf7ef261 100644 --- a/apps/meteor/client/views/marketplace/AppDetailsPage/tabs/AppStatus/AppStatus.tsx +++ b/apps/meteor/client/views/marketplace/AppDetailsPage/tabs/AppStatus/AppStatus.tsx @@ -7,7 +7,9 @@ import type { ReactElement } from 'react'; import React, { useCallback, useState, memo } from 'react'; import semver from 'semver'; +import { useHasLicenseModule } from '../../../../../hooks/useHasLicenseModule'; import { useIsEnterprise } from '../../../../../hooks/useIsEnterprise'; +import AddonRequiredModal from '../../../AppsList/AddonRequiredModal'; import type { appStatusSpanResponseProps } from '../../../helpers'; import { appButtonProps, appMultiStatusProps } from '../../../helpers'; import type { AppInstallationHandlerParams } from '../../../hooks/useAppInstallationHandler'; @@ -41,6 +43,9 @@ const AppStatus = ({ app, showStatus = true, isAppDetailsPage, installed, ...pro const { data } = useIsEnterprise(); const isEnterprise = data?.isEnterprise ?? false; + const appAddon = app.addon; + const workspaceHasAddon = useHasLicenseModule(appAddon); + const statuses = appMultiStatusProps(app, isAppDetailsPage, context || '', isEnterprise); const totalSeenRequests = app?.appRequestStats?.totalSeen; @@ -81,8 +86,13 @@ const AppStatus = ({ app, showStatus = true, isAppDetailsPage, installed, ...pro const handleAcquireApp = useCallback(() => { setLoading(true); + + if (isAdminUser && appAddon && !workspaceHasAddon) { + return setModal(); + } + appInstallationHandler(); - }, [appInstallationHandler, setLoading]); + }, [appAddon, appInstallationHandler, cancelAction, isAdminUser, setLoading, setModal, workspaceHasAddon]); // @TODO we should refactor this to not use the label to determine the variant const getStatusVariant = (status: appStatusSpanResponseProps) => { diff --git a/apps/meteor/client/views/marketplace/AppsList/AddonChip.tsx b/apps/meteor/client/views/marketplace/AppsList/AddonChip.tsx new file mode 100644 index 000000000000..39b51f84fa78 --- /dev/null +++ b/apps/meteor/client/views/marketplace/AppsList/AddonChip.tsx @@ -0,0 +1,24 @@ +import type { App } from '@rocket.chat/core-typings'; +import { Tag } from '@rocket.chat/fuselage'; +import React from 'react'; +import { useTranslation } from 'react-i18next'; + +type AddonChipProps = { + app: App; +}; + +const AddonChip = ({ app }: AddonChipProps) => { + const { t } = useTranslation(); + + if (!app.addon) { + return null; + } + + return ( + + {t('Add-on')} + + ); +}; + +export default AddonChip; diff --git a/apps/meteor/client/views/marketplace/AppsList/AddonRequiredModal.tsx b/apps/meteor/client/views/marketplace/AppsList/AddonRequiredModal.tsx new file mode 100644 index 000000000000..30fc216266bb --- /dev/null +++ b/apps/meteor/client/views/marketplace/AppsList/AddonRequiredModal.tsx @@ -0,0 +1,42 @@ +import { Button, Modal } from '@rocket.chat/fuselage'; +import React from 'react'; +import { useTranslation } from 'react-i18next'; + +import { useExternalLink } from '../../../hooks/useExternalLink'; +import { GET_ADDONS_LINK } from '../../admin/subscription/utils/links'; + +export type AddonActionType = 'install' | 'enable'; + +type AddonRequiredModalProps = { + actionType: AddonActionType; + onDismiss: () => void; + onInstallAnyway: () => void; +}; + +const AddonRequiredModal = ({ actionType, onDismiss, onInstallAnyway }: AddonRequiredModalProps) => { + const { t } = useTranslation(); + + const handleOpenLink = useExternalLink(); + + return ( + + + + {t('Add-on_required')} + + + + {t('Add-on_required_modal_enable_content')} + + + {actionType === 'install' && } + + + + + ); +}; + +export default AddonRequiredModal; diff --git a/apps/meteor/client/views/marketplace/AppsList/AppRow.tsx b/apps/meteor/client/views/marketplace/AppsList/AppRow.tsx index 338828e3f47d..1a5911e526b9 100644 --- a/apps/meteor/client/views/marketplace/AppsList/AppRow.tsx +++ b/apps/meteor/client/views/marketplace/AppsList/AppRow.tsx @@ -9,6 +9,7 @@ import semver from 'semver'; import AppStatus from '../AppDetailsPage/tabs/AppStatus/AppStatus'; import AppMenu from '../AppMenu'; import BundleChips from '../BundleChips'; +import AddonChip from './AddonChip'; // TODO: org props const AppRow = ({ className, ...props }: App & { className?: string }): ReactElement => { @@ -68,6 +69,7 @@ const AppRow = ({ className, ...props }: App & { className?: string }): ReactEle {name} {Boolean(bundledIn?.length) && } + {shortDescription && {shortDescription}} diff --git a/apps/meteor/client/views/marketplace/hooks/useAppMenu.tsx b/apps/meteor/client/views/marketplace/hooks/useAppMenu.tsx index 7b06d2214be9..d32b2b8b80c3 100644 --- a/apps/meteor/client/views/marketplace/hooks/useAppMenu.tsx +++ b/apps/meteor/client/views/marketplace/hooks/useAppMenu.tsx @@ -15,7 +15,10 @@ import React, { useMemo, useCallback, useState } from 'react'; import semver from 'semver'; import WarningModal from '../../../components/WarningModal'; +import { useHasLicenseModule } from '../../../hooks/useHasLicenseModule'; import { useIsEnterprise } from '../../../hooks/useIsEnterprise'; +import type { AddonActionType } from '../AppsList/AddonRequiredModal'; +import AddonRequiredModal from '../AppsList/AddonRequiredModal'; import IframeModal from '../IframeModal'; import UninstallGrandfatheredAppModal from '../components/UninstallGrandfatheredAppModal/UninstallGrandfatheredAppModal'; import type { Actions } from '../helpers'; @@ -56,6 +59,9 @@ export const useAppMenu = (app: App, isAppDetailsPage: boolean) => { const { data } = useIsEnterprise(); const isEnterpriseLicense = !!data?.isEnterprise; + const appAddon = app.addon; + const workspaceHasAddon = useHasLicenseModule(appAddon); + const [isLoading, setLoading] = useState(false); const [requestedEndUser, setRequestedEndUser] = useState(app.requestedEndUser); const [isAppPurchased, setPurchased] = useState(app?.isPurchased); @@ -118,10 +124,30 @@ export const useAppMenu = (app: App, isAppDetailsPage: boolean) => { setIsPurchased: setPurchased, }); + // TODO: There is no necessity of all these callbacks being out of the above useMemo. + // My propose here is to refactor the hook to make it clearer and with less unnecessary caching. + const missingAddonHandler = useCallback( + (actionType: AddonActionType) => { + setModal(); + }, + [appInstallationHandler, closeModal, setModal], + ); + + const handleAddon = useCallback( + (actionType: AddonActionType, callback: () => void) => { + if (isAdminUser && appAddon && !workspaceHasAddon) { + return missingAddonHandler(actionType); + } + + callback(); + }, + [appAddon, isAdminUser, missingAddonHandler, workspaceHasAddon], + ); + const handleAcquireApp = useCallback(() => { setLoading(true); - appInstallationHandler(); - }, [appInstallationHandler, setLoading]); + handleAddon('install', appInstallationHandler); + }, [appInstallationHandler, handleAddon]); const handleSubscription = useCallback(async () => { if (app?.versionIncompatible && !isSubscribed) { @@ -181,14 +207,16 @@ export const useAppMenu = (app: App, isAppDetailsPage: boolean) => { ); }, [app.name, closeModal, setAppStatus, setModal, t]); - const handleEnable = useCallback(async () => { - try { - const { status } = await setAppStatus({ status: AppStatus.MANUALLY_ENABLED }); - warnEnableDisableApp(app.name, status, 'enable'); - } catch (error) { - handleAPIError(error); - } - }, [app.name, setAppStatus]); + const handleEnable = useCallback(() => { + handleAddon('enable', async () => { + try { + const { status } = await setAppStatus({ status: AppStatus.MANUALLY_ENABLED }); + warnEnableDisableApp(app.name, status, 'enable'); + } catch (error) { + handleAPIError(error); + } + }); + }, [app.name, handleAddon, setAppStatus]); const handleUninstall = useCallback(() => { const uninstall = async () => { diff --git a/packages/i18n/src/locales/en.i18n.json b/packages/i18n/src/locales/en.i18n.json index 9273b890f260..e199e8a1dcb6 100644 --- a/packages/i18n/src/locales/en.i18n.json +++ b/packages/i18n/src/locales/en.i18n.json @@ -380,6 +380,9 @@ "admin-video-conf-provider-not-configured": "**Conference call not enabled**: Configure conference calls in order to make it available on this workspace.", "admin-no-videoconf-provider-app": "**Conference call not enabled**: Conference call apps are available in the Rocket.Chat marketplace.", "Administration": "Administration", + "Add-on": "Add-on", + "Add-on_required": "Add-on required", + "Add-on_required_modal_enable_content": "App cannot be enabled without the required subscription add-on. Contact sales to get the add-on for this app.", "Address": "Address", "Adjustable_font_size": "Adjustable font size", "Adjustable_font_size_description": "Designed for those who prefer larger or smaller text for improved readability. This flexibility promotes inclusivity by empowering users to tailor the software interface to their specific needs.", @@ -4580,6 +4583,7 @@ "Require_any_token": "Require any token", "Require_password_change": "Require password change", "Require_Two_Factor_Authentication": "Require Two Factor Authentication", + "Requires_subscription_add-on": "Requires subscription add-on", "Resend_verification_email": "Resend verification email", "Resend_welcome_email": "Resend welcome email", "Reset": "Reset", @@ -6353,6 +6357,8 @@ "onboarding.form.standaloneServerForm.servicesUnavailable": "Some of the services will be unavailable or will require manual setup", "onboarding.form.standaloneServerForm.publishOwnApp": "In order to send push notitications you need to compile and publish your own app to Google Play and App Store", "onboarding.form.standaloneServerForm.manuallyIntegrate": "Need to manually integrate with external services", + "Subscription_add-on_required": "Subscription add-on required", + "App_cannot_be_enabled_without_add-on": "App cannot be enabled without add-on.", "subscription.callout.servicesDisruptionsMayOccur": "Services disruptions may occur", "subscription.callout.servicesDisruptionsOccurring": "Services disruptions occurring", "subscription.callout.capabilitiesDisabled": "Capabilities disabled", From 274a89b1380d7c2ac7bffc1782e2361f9bf77408 Mon Sep 17 00:00:00 2001 From: Diego Sampaio Date: Fri, 18 Oct 2024 19:12:55 -0300 Subject: [PATCH 21/25] chore!: Change default Docker flavor to Alpine (#28042) Co-authored-by: Guilherme Gazzo --- .changeset/six-horses-sin.md | 8 +++ .github/actions/build-docker-image/action.yml | 2 +- .github/workflows/ci-test-e2e.yml | 10 ++-- .github/workflows/ci.yml | 48 ++++++++-------- apps/meteor/.docker/Dockerfile | 56 +++++++----------- apps/meteor/.docker/Dockerfile.alpine | 49 ---------------- apps/meteor/.docker/Dockerfile.debian | 57 +++++++++++++++++++ 7 files changed, 117 insertions(+), 113 deletions(-) create mode 100644 .changeset/six-horses-sin.md delete mode 100644 apps/meteor/.docker/Dockerfile.alpine create mode 100644 apps/meteor/.docker/Dockerfile.debian diff --git a/.changeset/six-horses-sin.md b/.changeset/six-horses-sin.md new file mode 100644 index 000000000000..dcb75ef4ac6b --- /dev/null +++ b/.changeset/six-horses-sin.md @@ -0,0 +1,8 @@ +--- +"@rocket.chat/meteor": patch +--- + + + Changes the default base Docker image to Alpine. Previously we were shipping Alpine as an alternative flavor under the tag rocketchat/rocket.chat:{release}.alpine , we have been testing this for a while now so we're migrating to use the official one to Alpine. + +We'll still ship a Debian variant under the tag rocketchat/rocket.chat:{release}.debian. diff --git a/.github/actions/build-docker-image/action.yml b/.github/actions/build-docker-image/action.yml index 02a05d9605a7..fa0535d332c6 100644 --- a/.github/actions/build-docker-image/action.yml +++ b/.github/actions/build-docker-image/action.yml @@ -51,7 +51,7 @@ runs: fi; DOCKERFILE_PATH="${DOCKER_PATH}/Dockerfile" - if [[ '${{ inputs.release }}' = 'alpine' ]]; then + if [[ '${{ inputs.release }}' = 'debian' ]]; then DOCKERFILE_PATH="${DOCKERFILE_PATH}.${{ inputs.release }}" fi; diff --git a/.github/workflows/ci-test-e2e.yml b/.github/workflows/ci-test-e2e.yml index 7dc581450275..8f5d258ef165 100644 --- a/.github/workflows/ci-test-e2e.yml +++ b/.github/workflows/ci-test-e2e.yml @@ -18,10 +18,10 @@ on: rc-docker-tag: required: true type: string - rc-dockerfile-alpine: + rc-dockerfile-debian: required: true type: string - rc-docker-tag-alpine: + rc-docker-tag-debian: required: true type: string gh-docker-tag: @@ -83,8 +83,8 @@ jobs: test: runs-on: ubuntu-20.04 env: - RC_DOCKERFILE: ${{ matrix.mongodb-version == '7.0' && inputs.rc-dockerfile-alpine || inputs.rc-dockerfile }} - RC_DOCKER_TAG: ${{ matrix.mongodb-version == '7.0' && inputs.rc-docker-tag-alpine || inputs.rc-docker-tag }} + RC_DOCKERFILE: ${{ matrix.mongodb-version == '7.0' && inputs.rc-dockerfile-debian || inputs.rc-dockerfile }} + RC_DOCKER_TAG: ${{ matrix.mongodb-version == '7.0' && inputs.rc-docker-tag-debian || inputs.rc-docker-tag }} strategy: fail-fast: false @@ -92,7 +92,7 @@ jobs: mongodb-version: ${{ fromJSON(inputs.mongodb-version) }} shard: ${{ fromJSON(inputs.shard) }} - name: MongoDB ${{ matrix.mongodb-version }}${{ inputs.db-watcher-disabled == 'true' && ' [no watchers]' || '' }} (${{ matrix.shard }}/${{ inputs.total-shard }})${{ matrix.mongodb-version == '7.0' && ' - Alpine' || '' }} + name: MongoDB ${{ matrix.mongodb-version }}${{ inputs.db-watcher-disabled == 'true' && ' [no watchers]' || '' }} (${{ matrix.shard }}/${{ inputs.total-shard }}) - ${{ matrix.mongodb-version == '7.0' && 'Debian' || 'Alpine (Official)' }} steps: - name: Collect Workflow Telemetry diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e70557bbd31d..90b7d93f642a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -32,8 +32,8 @@ jobs: lowercase-repo: ${{ steps.var.outputs.lowercase-repo }} rc-dockerfile: '${{ github.workspace }}/apps/meteor/.docker/Dockerfile' rc-docker-tag: '${{ steps.docker.outputs.gh-docker-tag }}.official' - rc-dockerfile-alpine: '${{ github.workspace }}/apps/meteor/.docker/Dockerfile.alpine' - rc-docker-tag-alpine: '${{ steps.docker.outputs.gh-docker-tag }}.alpine' + rc-dockerfile-debian: '${{ github.workspace }}/apps/meteor/.docker/Dockerfile.debian' + rc-docker-tag-debian: '${{ steps.docker.outputs.gh-docker-tag }}.debian' node-version: ${{ steps.var.outputs.node-version }} deno-version: ${{ steps.var.outputs.deno-version }} # this is 100% intentional, secrets are not available for forks, so ee-tests will always fail @@ -327,15 +327,15 @@ jobs: runs-on: ubuntu-20.04 env: - RC_DOCKERFILE: ${{ matrix.platform == 'alpine' && needs.release-versions.outputs.rc-dockerfile-alpine || needs.release-versions.outputs.rc-dockerfile }} - RC_DOCKER_TAG: ${{ matrix.platform == 'alpine' && needs.release-versions.outputs.rc-docker-tag-alpine || needs.release-versions.outputs.rc-docker-tag }} + RC_DOCKERFILE: ${{ matrix.platform == 'debian' && needs.release-versions.outputs.rc-dockerfile-debian || needs.release-versions.outputs.rc-dockerfile }} + RC_DOCKER_TAG: ${{ matrix.platform == 'debian' && needs.release-versions.outputs.rc-docker-tag-debian || needs.release-versions.outputs.rc-docker-tag }} DOCKER_TAG: ${{ needs.release-versions.outputs.gh-docker-tag }} LOWERCASE_REPOSITORY: ${{ needs.release-versions.outputs.lowercase-repo }} strategy: fail-fast: false matrix: - platform: ['official', 'alpine'] + platform: ['official', 'debian'] steps: - uses: actions/checkout@v4 @@ -349,7 +349,7 @@ jobs: node-version: ${{ needs.release-versions.outputs.node-version }} deno-version: ${{ needs.release-versions.outputs.deno-version }} platform: ${{ matrix.platform }} - build-containers: ${{ matrix.platform == 'alpine' && 'authorization-service account-service ddp-streamer-service presence-service stream-hub-service queue-worker-service omnichannel-transcript-service' || '' }} + build-containers: ${{ matrix.platform == 'debian' && 'authorization-service account-service ddp-streamer-service presence-service stream-hub-service queue-worker-service omnichannel-transcript-service' || '' }} NPM_TOKEN: ${{ secrets.NPM_TOKEN }} - name: Make sure matrix bindings load @@ -363,15 +363,15 @@ jobs: runs-on: ubuntu-20.04 env: - RC_DOCKERFILE: ${{ matrix.platform == 'alpine' && needs.release-versions.outputs.rc-dockerfile-alpine || needs.release-versions.outputs.rc-dockerfile }} - RC_DOCKER_TAG: ${{ matrix.platform == 'alpine' && needs.release-versions.outputs.rc-docker-tag-alpine || needs.release-versions.outputs.rc-docker-tag }} + RC_DOCKERFILE: ${{ matrix.platform == 'debian' && needs.release-versions.outputs.rc-dockerfile-debian || needs.release-versions.outputs.rc-dockerfile }} + RC_DOCKER_TAG: ${{ matrix.platform == 'debian' && needs.release-versions.outputs.rc-docker-tag-debian || needs.release-versions.outputs.rc-docker-tag }} DOCKER_TAG: ${{ needs.release-versions.outputs.gh-docker-tag }} LOWERCASE_REPOSITORY: ${{ needs.release-versions.outputs.lowercase-repo }} strategy: fail-fast: false matrix: - platform: ['official', 'alpine'] + platform: ['official', 'debian'] steps: - uses: actions/checkout@v4 @@ -383,7 +383,7 @@ jobs: node-version: ${{ needs.release-versions.outputs.node-version }} deno-version: ${{ needs.release-versions.outputs.deno-version }} platform: ${{ matrix.platform }} - build-containers: ${{ matrix.platform == 'alpine' && 'authorization-service account-service ddp-streamer-service presence-service stream-hub-service queue-worker-service omnichannel-transcript-service' || '' }} + build-containers: ${{ matrix.platform == 'debian' && 'authorization-service account-service ddp-streamer-service presence-service stream-hub-service queue-worker-service omnichannel-transcript-service' || '' }} NPM_TOKEN: ${{ secrets.NPM_TOKEN }} - name: Rename official Docker tag to GitHub Container Registry @@ -429,8 +429,8 @@ jobs: lowercase-repo: ${{ needs.release-versions.outputs.lowercase-repo }} rc-dockerfile: ${{ needs.release-versions.outputs.rc-dockerfile }} rc-docker-tag: ${{ needs.release-versions.outputs.rc-docker-tag }} - rc-dockerfile-alpine: ${{ needs.release-versions.outputs.rc-dockerfile-alpine }} - rc-docker-tag-alpine: ${{ needs.release-versions.outputs.rc-docker-tag-alpine }} + rc-dockerfile-debian: ${{ needs.release-versions.outputs.rc-dockerfile-debian }} + rc-docker-tag-debian: ${{ needs.release-versions.outputs.rc-docker-tag-debian }} gh-docker-tag: ${{ needs.release-versions.outputs.gh-docker-tag }} secrets: CR_USER: ${{ secrets.CR_USER }} @@ -453,8 +453,8 @@ jobs: lowercase-repo: ${{ needs.release-versions.outputs.lowercase-repo }} rc-dockerfile: ${{ needs.release-versions.outputs.rc-dockerfile }} rc-docker-tag: ${{ needs.release-versions.outputs.rc-docker-tag }} - rc-dockerfile-alpine: ${{ needs.release-versions.outputs.rc-dockerfile-alpine }} - rc-docker-tag-alpine: ${{ needs.release-versions.outputs.rc-docker-tag-alpine }} + rc-dockerfile-debian: ${{ needs.release-versions.outputs.rc-dockerfile-debian }} + rc-docker-tag-debian: ${{ needs.release-versions.outputs.rc-docker-tag-debian }} gh-docker-tag: ${{ needs.release-versions.outputs.gh-docker-tag }} retries: ${{ (github.event_name == 'release' || github.ref == 'refs/heads/develop' || github.ref == 'refs/heads/master') && 2 || 0 }} secrets: @@ -481,8 +481,8 @@ jobs: lowercase-repo: ${{ needs.release-versions.outputs.lowercase-repo }} rc-dockerfile: ${{ needs.release-versions.outputs.rc-dockerfile }} rc-docker-tag: ${{ needs.release-versions.outputs.rc-docker-tag }} - rc-dockerfile-alpine: ${{ needs.release-versions.outputs.rc-dockerfile-alpine }} - rc-docker-tag-alpine: ${{ needs.release-versions.outputs.rc-docker-tag-alpine }} + rc-dockerfile-debian: ${{ needs.release-versions.outputs.rc-dockerfile-debian }} + rc-docker-tag-debian: ${{ needs.release-versions.outputs.rc-docker-tag-debian }} gh-docker-tag: ${{ needs.release-versions.outputs.gh-docker-tag }} secrets: CR_USER: ${{ secrets.CR_USER }} @@ -506,8 +506,8 @@ jobs: lowercase-repo: ${{ needs.release-versions.outputs.lowercase-repo }} rc-dockerfile: ${{ needs.release-versions.outputs.rc-dockerfile }} rc-docker-tag: ${{ needs.release-versions.outputs.rc-docker-tag }} - rc-dockerfile-alpine: ${{ needs.release-versions.outputs.rc-dockerfile-alpine }} - rc-docker-tag-alpine: ${{ needs.release-versions.outputs.rc-docker-tag-alpine }} + rc-dockerfile-debian: ${{ needs.release-versions.outputs.rc-dockerfile-debian }} + rc-docker-tag-debian: ${{ needs.release-versions.outputs.rc-docker-tag-debian }} gh-docker-tag: ${{ needs.release-versions.outputs.gh-docker-tag }} retries: ${{ (github.event_name == 'release' || github.ref == 'refs/heads/develop' || github.ref == 'refs/heads/master') && 2 || 0 }} secrets: @@ -537,8 +537,8 @@ jobs: lowercase-repo: ${{ needs.release-versions.outputs.lowercase-repo }} rc-dockerfile: ${{ needs.release-versions.outputs.rc-dockerfile }} rc-docker-tag: ${{ needs.release-versions.outputs.rc-docker-tag }} - rc-dockerfile-alpine: ${{ needs.release-versions.outputs.rc-dockerfile-alpine }} - rc-docker-tag-alpine: ${{ needs.release-versions.outputs.rc-docker-tag-alpine }} + rc-dockerfile-debian: ${{ needs.release-versions.outputs.rc-dockerfile-debian }} + rc-docker-tag-debian: ${{ needs.release-versions.outputs.rc-docker-tag-debian }} gh-docker-tag: ${{ needs.release-versions.outputs.gh-docker-tag }} retries: ${{ (github.event_name == 'release' || github.ref == 'refs/heads/develop' || github.ref == 'refs/heads/master') && 2 || 0 }} db-watcher-disabled: 'true' @@ -683,7 +683,7 @@ jobs: strategy: matrix: # this is currently a mix of variants and different images - release: ['official', 'preview', 'alpine'] + release: ['official', 'preview', 'debian'] env: IMAGE_NAME: 'rocketchat/rocket.chat' @@ -729,7 +729,7 @@ jobs: DOCKER_TAG=$GITHUB_REF_NAME # append the variant name to docker tag - if [[ '${{ matrix.release }}' = 'alpine' ]]; then + if [[ '${{ matrix.release }}' = 'debian'] ]]; then DOCKER_TAG="${DOCKER_TAG}-${{ matrix.release }}" fi; @@ -744,7 +744,7 @@ jobs: if [[ $GITHUB_REF == refs/tags/* ]]; then RELEASE="${{ needs.release-versions.outputs.release }}" - if [[ '${{ matrix.release }}' = 'alpine' ]]; then + if [[ '${{ matrix.release }}' = 'debian' ]]; then RELEASE="${RELEASE}-${{ matrix.release }}" fi; @@ -769,7 +769,7 @@ jobs: TAG_SHA="${{ steps.gh-docker.outputs.gh-docker-tag-sha }}" # append the variant name to docker tag - if [[ '${{ matrix.release }}' = 'alpine' ]]; then + if [[ '${{ matrix.release }}' = 'debian'] ]]; then TAG_SHA="${TAG_SHA}-${{ matrix.release }}" fi; diff --git a/apps/meteor/.docker/Dockerfile b/apps/meteor/.docker/Dockerfile index c8ede9db3cae..e225594ec44f 100644 --- a/apps/meteor/.docker/Dockerfile +++ b/apps/meteor/.docker/Dockerfile @@ -1,25 +1,14 @@ -ARG DENO_VERSION="1.37.1" - -FROM denoland/deno:bin-${DENO_VERSION} as deno - -FROM node:20.17.0-bullseye-slim +FROM node:20.17.0-alpine3.20 LABEL maintainer="buildmaster@rocket.chat" -# dependencies -RUN groupadd -g 65533 -r rocketchat \ - && useradd -u 65533 -r -g rocketchat rocketchat \ - && mkdir -p /app/uploads \ - && chown rocketchat:rocketchat /app/uploads \ - && apt-get update \ - && apt-get install -y --no-install-recommends fontconfig +ENV LANG=C.UTF-8 -COPY --from=deno /deno /bin/deno +RUN apk add --no-cache deno ttf-dejavu -# --chown requires Docker 17.12 and works only on Linux -ADD --chown=rocketchat:rocketchat . /app +ADD . /app -# needs a mongoinstance - defaults to container linking with alias 'mongo' +# needs a mongo instance - defaults to container linking with alias 'mongo' ENV DEPLOY_METHOD=docker \ NODE_ENV=production \ MONGO_URL=mongodb://mongo:27017/rocketchat \ @@ -28,25 +17,24 @@ ENV DEPLOY_METHOD=docker \ ROOT_URL=http://localhost:3000 \ Accounts_AvatarStorePath=/app/uploads -RUN aptMark="$(apt-mark showmanual)" \ - && apt-get install -y --no-install-recommends g++ make python3 ca-certificates \ +RUN set -x \ + && apk add --no-cache --virtual .fetch-deps python3 make g++ py3-setuptools libc6-compat \ && cd /app/bundle/programs/server \ - && npm install \ - && cd npm/node_modules/isolated-vm \ - && npm install \ - && apt-mark auto '.*' > /dev/null \ - && apt-mark manual $aptMark > /dev/null \ - && find /usr/local -type f -executable -exec ldd '{}' ';' \ - | awk '/=>/ { print $(NF-1) }' \ - | sort -u \ - | xargs -r dpkg-query --search \ - | cut -d: -f1 \ - | sort -u \ - | xargs -r apt-mark manual \ - && apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false \ - && npm cache clear --force - -USER rocketchat + && npm install --omit=dev --unsafe-perm \ + # Start hack for sharp... + && rm -rf npm/node_modules/sharp \ + && npm install sharp@0.32.6 \ + && mv node_modules/sharp npm/node_modules/sharp \ + # End hack for sharp + # # Start hack for isolated-vm... + # && rm -rf npm/node_modules/isolated-vm \ + # && npm install isolated-vm@4.6.0 \ + # && mv node_modules/isolated-vm npm/node_modules/isolated-vm \ + # # End hack for isolated-vm + && cd /app/bundle/programs/server/npm \ + && npm rebuild bcrypt --build-from-source \ + && npm cache clear --force \ + && apk del .fetch-deps VOLUME /app/uploads diff --git a/apps/meteor/.docker/Dockerfile.alpine b/apps/meteor/.docker/Dockerfile.alpine deleted file mode 100644 index 7138711c3b11..000000000000 --- a/apps/meteor/.docker/Dockerfile.alpine +++ /dev/null @@ -1,49 +0,0 @@ -FROM node:20.17.0-alpine3.20 - -LABEL maintainer="buildmaster@rocket.chat" - -ENV LANG=C.UTF-8 - -RUN apk add --no-cache deno ttf-dejavu - -ADD . /app - -# needs a mongo instance - defaults to container linking with alias 'mongo' -ENV DEPLOY_METHOD=docker \ - NODE_ENV=production \ - MONGO_URL=mongodb://mongo:27017/rocketchat \ - HOME=/tmp \ - PORT=3000 \ - ROOT_URL=http://localhost:3000 \ - Accounts_AvatarStorePath=/app/uploads - -RUN set -x \ - && apk add --no-cache --virtual .fetch-deps python3 make g++ py3-setuptools libc6-compat \ - && cd /app/bundle/programs/server \ - && npm install --omit=dev --unsafe-perm \ - # Start hack for sharp... - && rm -rf npm/node_modules/sharp \ - && npm install sharp@0.32.6 \ - && mv node_modules/sharp npm/node_modules/sharp \ - # End hack for sharp - # # Start hack for isolated-vm... - # && rm -rf npm/node_modules/isolated-vm \ - # && npm install isolated-vm@4.6.0 \ - # && mv node_modules/isolated-vm npm/node_modules/isolated-vm \ - # # End hack for isolated-vm - && cd /app/bundle/programs/server/npm \ - && npm rebuild bcrypt --build-from-source \ - && npm cache clear --force \ - && apk del .fetch-deps - -# TODO: remove hack once upstream builds are fixed -COPY matrix-sdk-crypto.linux-x64-musl.node /app/bundle/programs/server/npm/node_modules/@matrix-org/matrix-sdk-crypto-nodejs -COPY matrix-sdk-crypto.linux-x64-musl.node /app/bundle/programs/server/npm/node_modules/@vector-im/matrix-bot-sdk/node_modules/@matrix-org/matrix-sdk-crypto-nodejs - -VOLUME /app/uploads - -WORKDIR /app/bundle - -EXPOSE 3000 - -CMD ["node", "main.js"] diff --git a/apps/meteor/.docker/Dockerfile.debian b/apps/meteor/.docker/Dockerfile.debian new file mode 100644 index 000000000000..c8ede9db3cae --- /dev/null +++ b/apps/meteor/.docker/Dockerfile.debian @@ -0,0 +1,57 @@ +ARG DENO_VERSION="1.37.1" + +FROM denoland/deno:bin-${DENO_VERSION} as deno + +FROM node:20.17.0-bullseye-slim + +LABEL maintainer="buildmaster@rocket.chat" + +# dependencies +RUN groupadd -g 65533 -r rocketchat \ + && useradd -u 65533 -r -g rocketchat rocketchat \ + && mkdir -p /app/uploads \ + && chown rocketchat:rocketchat /app/uploads \ + && apt-get update \ + && apt-get install -y --no-install-recommends fontconfig + +COPY --from=deno /deno /bin/deno + +# --chown requires Docker 17.12 and works only on Linux +ADD --chown=rocketchat:rocketchat . /app + +# needs a mongoinstance - defaults to container linking with alias 'mongo' +ENV DEPLOY_METHOD=docker \ + NODE_ENV=production \ + MONGO_URL=mongodb://mongo:27017/rocketchat \ + HOME=/tmp \ + PORT=3000 \ + ROOT_URL=http://localhost:3000 \ + Accounts_AvatarStorePath=/app/uploads + +RUN aptMark="$(apt-mark showmanual)" \ + && apt-get install -y --no-install-recommends g++ make python3 ca-certificates \ + && cd /app/bundle/programs/server \ + && npm install \ + && cd npm/node_modules/isolated-vm \ + && npm install \ + && apt-mark auto '.*' > /dev/null \ + && apt-mark manual $aptMark > /dev/null \ + && find /usr/local -type f -executable -exec ldd '{}' ';' \ + | awk '/=>/ { print $(NF-1) }' \ + | sort -u \ + | xargs -r dpkg-query --search \ + | cut -d: -f1 \ + | sort -u \ + | xargs -r apt-mark manual \ + && apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false \ + && npm cache clear --force + +USER rocketchat + +VOLUME /app/uploads + +WORKDIR /app/bundle + +EXPOSE 3000 + +CMD ["node", "main.js"] From 634a6a59dc5f2017fb6db7ca59a8cbc57cf4db32 Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo Date: Fri, 18 Oct 2024 20:25:04 -0300 Subject: [PATCH 22/25] ci: cache `matrix-rust-sdk-crypto-nodejs` (#33673) --- .github/actions/build-docker/action.yml | 7 +++--- .github/workflows/ci.yml | 32 +++++++++++++++---------- 2 files changed, 23 insertions(+), 16 deletions(-) diff --git a/.github/actions/build-docker/action.yml b/.github/actions/build-docker/action.yml index 1c1132d30daf..2891d892c359 100644 --- a/.github/actions/build-docker/action.yml +++ b/.github/actions/build-docker/action.yml @@ -84,12 +84,11 @@ runs: - run: yarn build if: inputs.setup == 'true' shell: bash - - - if: ${{ matrix.platform == 'alpine' }} - uses: actions/download-artifact@v4 + - if: ${{ inputs.platform == 'official' }} + uses: actions/cache@v3 with: - name: napi-binary path: /tmp/build/matrix-sdk-crypto.linux-x64-musl.node + key: matrix-rust-sdk-crypto-nodejs-v0.2.0-beta.1 - name: Build Docker images shell: bash diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 90b7d93f642a..449406bfc496 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -289,37 +289,45 @@ jobs: name: Builds matrix rust bindings against alpine runs-on: ubuntu-latest steps: - - run: sudo apt-get install -y musl-tools libunwind-dev && find /usr/include -name stdarg.h 2>/dev/null || true + - name: check cache for matrix-rust-sdk-crypto-nodejs + id: matrix-rust-sdk-crypto-nodejs + uses: actions/cache@v3 + with: + path: /tmp/build/matrix-sdk-crypto.linux-x64-musl.node + key: matrix-rust-sdk-crypto-nodejs-v0.2.0-beta.1 + - uses: actions/checkout@v4 with: repository: matrix-org/matrix-rust-sdk-crypto-nodejs ref: v0.2.0-beta.1 # https://github.com/element-hq/matrix-bot-sdk/blob/e72a4c498e00c6c339a791630c45d00a351f56a8/package.json#L58 - - uses: actions/setup-node@v4 + - if: steps.matrix-rust-sdk-crypto-nodejs.outputs.cache-hit != 'true' + run: sudo apt-get install -y musl-tools libunwind-dev && find /usr/include -name stdarg.h 2>/dev/null || true + + - if: steps.matrix-rust-sdk-crypto-nodejs.outputs.cache-hit != 'true' + uses: actions/setup-node@v4 with: node-version: '20' - - uses: dtolnay/rust-toolchain@stable + - if: steps.matrix-rust-sdk-crypto-nodejs.outputs.cache-hit != 'true' + uses: dtolnay/rust-toolchain@stable with: toolchain: stable targets: x86_64-unknown-linux-musl - - name: Install ziglang + - if: steps.matrix-rust-sdk-crypto-nodejs.outputs.cache-hit != 'true' + name: Install ziglang uses: mlugg/setup-zig@v1 with: version: 0.13.0 - - name: Build + - if: steps.matrix-rust-sdk-crypto-nodejs.outputs.cache-hit != 'true' + name: Build run: | npm install --ignore-scripts npx napi build --release --target x86_64-unknown-linux-musl --platform --zig - - - name: Upload bin - uses: actions/upload-artifact@v4 - with: - path: matrix-sdk-crypto.linux-x64-musl.node - name: napi-binary - if-no-files-found: error + mkdir -p /tmp/build + mv matrix-sdk-crypto.linux-x64-musl.node /tmp/build/matrix-sdk-crypto.linux-x64-musl.node build-gh-docker-coverage: name: 🚢 Build Docker Images for Testing From f33c07ebb8d0bb2a187f6a132e209adcf4faaed7 Mon Sep 17 00:00:00 2001 From: Marcos Spessatto Defendi Date: Fri, 18 Oct 2024 21:44:52 -0300 Subject: [PATCH 23/25] feat!: apply restrictions to air gapped environments (#33241) Co-authored-by: gabriellsh Co-authored-by: Guilherme Gazzo --- .changeset/little-gifts-do.md | 7 + apps/meteor/app/api/server/v1/chat.ts | 29 ++-- apps/meteor/app/api/server/v1/rooms.ts | 31 +++-- .../app/lib/server/methods/sendMessage.ts | 5 +- .../app/lib/server/methods/updateMessage.ts | 3 +- .../server/airGappedRestrictionsWrapper.ts | 5 + .../server/functions/sendUsageReport.ts} | 19 +-- .../hooks/useAirGappedRestriction.spec.ts | 52 ++++++++ .../client/hooks/useAirGappedRestriction.ts | 19 +++ apps/meteor/client/sidebar/Sidebar.tsx | 10 +- .../AirGappedRestrictionBanner.tsx | 19 +++ .../AirGappedRestrictionWarning.tsx | 27 ++++ .../sidebar/sections/BannerSection.spec.tsx | 69 ++++++++++ .../client/sidebar/sections/BannerSection.tsx | 27 ++++ ...edSection.tsx => StatusDisabledBanner.tsx} | 4 +- apps/meteor/client/sidebarv2/Sidebar.tsx | 9 +- .../AirGappedRestrictionBanner.tsx | 23 ++++ .../AirGappedRestrictionWarning.tsx | 27 ++++ .../sidebarv2/sections/BannerSection.spec.tsx | 69 ++++++++++ .../sidebarv2/sections/BannerSection.tsx | 27 ++++ ...edSection.tsx => StatusDisabledBanner.tsx} | 0 .../composer/ComposerAirGappedRestricted.tsx | 23 ++++ .../views/room/composer/ComposerContainer.tsx | 9 ++ .../license/server/airGappedRestrictions.ts | 40 ++++++ apps/meteor/ee/app/license/server/index.ts | 1 + apps/meteor/ee/app/license/server/settings.ts | 5 + .../patches/airGappedRestrictionsWrapper.ts | 10 ++ apps/meteor/ee/server/patches/index.ts | 1 + .../airGappedRestrictionsWrapper.spec.ts | 34 +++++ .../airgappedRestrictions.spec.ts | 126 ++++++++++++++++++ apps/meteor/jest.config.ts | 2 + apps/meteor/server/cron/usageReport.ts | 18 +++ apps/meteor/server/models/raw/Statistics.ts | 16 +++ apps/meteor/server/startup/cron.ts | 4 +- .../license/src/AirGappedRestriction.spec.ts | 113 ++++++++++++++++ .../license/src/AirGappedRestriction.ts | 73 ++++++++++ .../license/src/MockedLicenseBuilder.ts | 30 ++++- ee/packages/license/src/index.ts | 1 + ee/packages/license/src/token.ts | 37 ++++- packages/i18n/src/locales/en.i18n.json | 4 + .../src/MockedAppRootBuilder.tsx | 3 +- .../src/models/IStatisticsModel.ts | 1 + .../MessageFooterCallout.tsx | 1 + 43 files changed, 971 insertions(+), 62 deletions(-) create mode 100644 .changeset/little-gifts-do.md create mode 100644 apps/meteor/app/license/server/airGappedRestrictionsWrapper.ts rename apps/meteor/{server/cron/statistics.ts => app/statistics/server/functions/sendUsageReport.ts} (60%) create mode 100644 apps/meteor/client/hooks/useAirGappedRestriction.spec.ts create mode 100644 apps/meteor/client/hooks/useAirGappedRestriction.ts create mode 100644 apps/meteor/client/sidebar/sections/AirGappedRestrictionBanner/AirGappedRestrictionBanner.tsx create mode 100644 apps/meteor/client/sidebar/sections/AirGappedRestrictionBanner/AirGappedRestrictionWarning.tsx create mode 100644 apps/meteor/client/sidebar/sections/BannerSection.spec.tsx create mode 100644 apps/meteor/client/sidebar/sections/BannerSection.tsx rename apps/meteor/client/sidebar/sections/{StatusDisabledSection.tsx => StatusDisabledBanner.tsx} (81%) create mode 100644 apps/meteor/client/sidebarv2/sections/AirGappedRestrictionBanner/AirGappedRestrictionBanner.tsx create mode 100644 apps/meteor/client/sidebarv2/sections/AirGappedRestrictionBanner/AirGappedRestrictionWarning.tsx create mode 100644 apps/meteor/client/sidebarv2/sections/BannerSection.spec.tsx create mode 100644 apps/meteor/client/sidebarv2/sections/BannerSection.tsx rename apps/meteor/client/sidebarv2/sections/{StatusDisabledSection.tsx => StatusDisabledBanner.tsx} (100%) create mode 100644 apps/meteor/client/views/room/composer/ComposerAirGappedRestricted.tsx create mode 100644 apps/meteor/ee/app/license/server/airGappedRestrictions.ts create mode 100644 apps/meteor/ee/server/patches/airGappedRestrictionsWrapper.ts create mode 100644 apps/meteor/ee/tests/unit/server/airgappedRestrictions/airGappedRestrictionsWrapper.spec.ts create mode 100644 apps/meteor/ee/tests/unit/server/airgappedRestrictions/airgappedRestrictions.spec.ts create mode 100644 apps/meteor/server/cron/usageReport.ts create mode 100644 ee/packages/license/src/AirGappedRestriction.spec.ts create mode 100644 ee/packages/license/src/AirGappedRestriction.ts diff --git a/.changeset/little-gifts-do.md b/.changeset/little-gifts-do.md new file mode 100644 index 000000000000..3cdc0f2a84ac --- /dev/null +++ b/.changeset/little-gifts-do.md @@ -0,0 +1,7 @@ +--- +"@rocket.chat/meteor": major +"@rocket.chat/i18n": major +"@rocket.chat/license": major +--- + +Adds restrictions to air-gapped environments without a license diff --git a/apps/meteor/app/api/server/v1/chat.ts b/apps/meteor/app/api/server/v1/chat.ts index 98c28d24581e..b5cfd9e46ce6 100644 --- a/apps/meteor/app/api/server/v1/chat.ts +++ b/apps/meteor/app/api/server/v1/chat.ts @@ -20,6 +20,7 @@ import { deleteMessageValidatingPermission } from '../../../lib/server/functions import { processWebhookMessage } from '../../../lib/server/functions/processWebhookMessage'; import { executeSendMessage } from '../../../lib/server/methods/sendMessage'; import { executeUpdateMessage } from '../../../lib/server/methods/updateMessage'; +import { applyAirGappedRestrictionsValidation } from '../../../license/server/airGappedRestrictionsWrapper'; import { OEmbed } from '../../../oembed/server/server'; import { executeSetReaction } from '../../../reactions/server/setReaction'; import { settings } from '../../../settings/server'; @@ -160,7 +161,7 @@ API.v1.addRoute( { authRequired: true }, { async post() { - const messageReturn = (await processWebhookMessage(this.bodyParams, this.user))[0]; + const messageReturn = (await applyAirGappedRestrictionsValidation(() => processWebhookMessage(this.bodyParams, this.user)))[0]; if (!messageReturn) { return API.v1.failure('unknown-error'); @@ -218,7 +219,9 @@ API.v1.addRoute( throw new Error("Cannot send system messages using 'chat.sendMessage'"); } - const sent = await executeSendMessage(this.userId, this.bodyParams.message as Pick, this.bodyParams.previewUrls); + const sent = await applyAirGappedRestrictionsValidation(() => + executeSendMessage(this.userId, this.bodyParams.message as Pick, this.bodyParams.previewUrls), + ); const [message] = await normalizeMessagesForUser([sent], this.userId); return API.v1.success({ @@ -318,16 +321,20 @@ API.v1.addRoute( return API.v1.failure('The room id provided does not match where the message is from.'); } + const msgFromBody = this.bodyParams.text; + // Permission checks are already done in the updateMessage method, so no need to duplicate them - await executeUpdateMessage( - this.userId, - { - _id: msg._id, - msg: this.bodyParams.text, - rid: msg.rid, - customFields: this.bodyParams.customFields as Record | undefined, - }, - this.bodyParams.previewUrls, + await applyAirGappedRestrictionsValidation(() => + executeUpdateMessage( + this.userId, + { + _id: msg._id, + msg: msgFromBody, + rid: msg.rid, + customFields: this.bodyParams.customFields as Record | undefined, + }, + this.bodyParams.previewUrls, + ), ); const updatedMessage = await Messages.findOneById(msg._id); diff --git a/apps/meteor/app/api/server/v1/rooms.ts b/apps/meteor/app/api/server/v1/rooms.ts index 4a42adf6f5bd..355cce24d40b 100644 --- a/apps/meteor/app/api/server/v1/rooms.ts +++ b/apps/meteor/app/api/server/v1/rooms.ts @@ -25,6 +25,7 @@ import { createDiscussion } from '../../../discussion/server/methods/createDiscu import { FileUpload } from '../../../file-upload/server'; import { sendFileMessage } from '../../../file-upload/server/methods/sendFileMessage'; import { leaveRoomMethod } from '../../../lib/server/methods/leaveRoom'; +import { applyAirGappedRestrictionsValidation } from '../../../license/server/airGappedRestrictionsWrapper'; import { settings } from '../../../settings/server'; import { API } from '../api'; import { composeRoomWithLastMessage } from '../helpers/composeRoomWithLastMessage'; @@ -199,7 +200,9 @@ API.v1.addRoute( delete fields.description; - await sendFileMessage(this.userId, { roomId: this.urlParams.rid, file: uploadedFile, msgData: fields }); + await applyAirGappedRestrictionsValidation(() => + sendFileMessage(this.userId, { roomId: this.urlParams.rid, file: uploadedFile, msgData: fields }), + ); const message = await Messages.getMessageByFileIdAndUsername(uploadedFile._id, this.userId); @@ -299,10 +302,8 @@ API.v1.addRoute( file.description = this.bodyParams.description; delete this.bodyParams.description; - await sendFileMessage( - this.userId, - { roomId: this.urlParams.rid, file, msgData: this.bodyParams }, - { parseAttachmentsForE2EE: false }, + await applyAirGappedRestrictionsValidation(() => + sendFileMessage(this.userId, { roomId: this.urlParams.rid, file, msgData: this.bodyParams }, { parseAttachmentsForE2EE: false }), ); await Uploads.confirmTemporaryFile(this.urlParams.fileId, this.userId); @@ -479,15 +480,17 @@ API.v1.addRoute( return API.v1.failure('Body parameter "encrypted" must be a boolean when included.'); } - const discussion = await createDiscussion(this.userId, { - prid, - pmid, - t_name, - reply, - users: users?.filter(isTruthy) || [], - encrypted, - topic, - }); + const discussion = await applyAirGappedRestrictionsValidation(() => + createDiscussion(this.userId, { + prid, + pmid, + t_name, + reply, + users: users?.filter(isTruthy) || [], + encrypted, + topic, + }), + ); return API.v1.success({ discussion }); }, diff --git a/apps/meteor/app/lib/server/methods/sendMessage.ts b/apps/meteor/app/lib/server/methods/sendMessage.ts index c75f6dbf72e2..76134c81d0b3 100644 --- a/apps/meteor/app/lib/server/methods/sendMessage.ts +++ b/apps/meteor/app/lib/server/methods/sendMessage.ts @@ -12,6 +12,7 @@ import { i18n } from '../../../../server/lib/i18n'; import { SystemLogger } from '../../../../server/lib/logger/system'; import { canSendMessageAsync } from '../../../authorization/server/functions/canSendMessage'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; +import { applyAirGappedRestrictionsValidation } from '../../../license/server/airGappedRestrictionsWrapper'; import { metrics } from '../../../metrics/server'; import { settings } from '../../../settings/server'; import { MessageTypes } from '../../../ui-utils/server'; @@ -136,9 +137,9 @@ Meteor.methods({ } try { - return await executeSendMessage(uid, message, previewUrls); + return await applyAirGappedRestrictionsValidation(() => executeSendMessage(uid, message, previewUrls)); } catch (error: any) { - if ((error.error || error.message) === 'error-not-allowed') { + if (['error-not-allowed', 'restricted-workspace'].includes(error.error || error.message)) { throw new Meteor.Error(error.error || error.message, error.reason, { method: 'sendMessage', }); diff --git a/apps/meteor/app/lib/server/methods/updateMessage.ts b/apps/meteor/app/lib/server/methods/updateMessage.ts index c03208a438e9..786d3555c145 100644 --- a/apps/meteor/app/lib/server/methods/updateMessage.ts +++ b/apps/meteor/app/lib/server/methods/updateMessage.ts @@ -7,6 +7,7 @@ import moment from 'moment'; import { canSendMessageAsync } from '../../../authorization/server/functions/canSendMessage'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; +import { applyAirGappedRestrictionsValidation } from '../../../license/server/airGappedRestrictionsWrapper'; import { settings } from '../../../settings/server'; import { updateMessage } from '../functions/updateMessage'; @@ -115,6 +116,6 @@ Meteor.methods({ throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'updateMessage' }); } - return executeUpdateMessage(uid, message, previewUrls); + return applyAirGappedRestrictionsValidation(() => executeUpdateMessage(uid, message, previewUrls)); }, }); diff --git a/apps/meteor/app/license/server/airGappedRestrictionsWrapper.ts b/apps/meteor/app/license/server/airGappedRestrictionsWrapper.ts new file mode 100644 index 000000000000..f6f05c052e4a --- /dev/null +++ b/apps/meteor/app/license/server/airGappedRestrictionsWrapper.ts @@ -0,0 +1,5 @@ +import { makeFunction } from '@rocket.chat/patch-injection'; + +export const applyAirGappedRestrictionsValidation = makeFunction(async (fn: () => Promise): Promise => { + return fn(); +}); diff --git a/apps/meteor/server/cron/statistics.ts b/apps/meteor/app/statistics/server/functions/sendUsageReport.ts similarity index 60% rename from apps/meteor/server/cron/statistics.ts rename to apps/meteor/app/statistics/server/functions/sendUsageReport.ts index 846fac2c2e51..dc684fe5fa6a 100644 --- a/apps/meteor/server/cron/statistics.ts +++ b/apps/meteor/app/statistics/server/functions/sendUsageReport.ts @@ -1,13 +1,12 @@ -import { cronJobs } from '@rocket.chat/cron'; import type { Logger } from '@rocket.chat/logger'; import { Statistics } from '@rocket.chat/models'; import { serverFetch as fetch } from '@rocket.chat/server-fetch'; import { Meteor } from 'meteor/meteor'; -import { getWorkspaceAccessToken } from '../../app/cloud/server'; -import { statistics } from '../../app/statistics/server'; +import { statistics } from '..'; +import { getWorkspaceAccessToken } from '../../../cloud/server'; -async function generateStatistics(logger: Logger): Promise { +export async function sendUsageReport(logger: Logger): Promise { const cronStatistics = await statistics.save(); try { @@ -27,20 +26,10 @@ async function generateStatistics(logger: Logger): Promise { if (statsToken != null) { await Statistics.updateOne({ _id: cronStatistics._id }, { $set: { statsToken } }); + return statsToken; } } catch (error) { /* error*/ logger.warn('Failed to send usage report'); } } - -export async function statsCron(logger: Logger): Promise { - const name = 'Generate and save statistics'; - await generateStatistics(logger); - - const now = new Date(); - - await cronJobs.add(name, `12 ${now.getHours()} * * *`, async () => { - await generateStatistics(logger); - }); -} diff --git a/apps/meteor/client/hooks/useAirGappedRestriction.spec.ts b/apps/meteor/client/hooks/useAirGappedRestriction.spec.ts new file mode 100644 index 000000000000..b790fe99b24a --- /dev/null +++ b/apps/meteor/client/hooks/useAirGappedRestriction.spec.ts @@ -0,0 +1,52 @@ +import { mockAppRoot } from '@rocket.chat/mock-providers'; +import { renderHook } from '@testing-library/react'; + +import { useAirGappedRestriction } from './useAirGappedRestriction'; + +// [restricted, warning, remainingDays] +describe('useAirGappedRestriction hook', () => { + it('should return [false, false, -1] if setting value is not a number', () => { + const { result } = renderHook(() => useAirGappedRestriction(), { + legacyRoot: true, + wrapper: mockAppRoot().withSetting('Cloud_Workspace_AirGapped_Restrictions_Remaining_Days', -1).build(), + }); + + expect(result.current).toEqual([false, false, -1]); + }); + + it('should return [false, false, -1] if user has a license (remaining days is a negative value)', () => { + const { result } = renderHook(() => useAirGappedRestriction(), { + legacyRoot: true, + wrapper: mockAppRoot().withSetting('Cloud_Workspace_AirGapped_Restrictions_Remaining_Days', -1).build(), + }); + + expect(result.current).toEqual([false, false, -1]); + }); + + it('should return [false, false, 8] if not on warning or restriction phase', () => { + const { result } = renderHook(() => useAirGappedRestriction(), { + legacyRoot: true, + wrapper: mockAppRoot().withSetting('Cloud_Workspace_AirGapped_Restrictions_Remaining_Days', 8).build(), + }); + + expect(result.current).toEqual([false, false, 8]); + }); + + it('should return [true, false, 7] if on warning phase', () => { + const { result } = renderHook(() => useAirGappedRestriction(), { + legacyRoot: true, + wrapper: mockAppRoot().withSetting('Cloud_Workspace_AirGapped_Restrictions_Remaining_Days', 7).build(), + }); + + expect(result.current).toEqual([false, true, 7]); + }); + + it('should return [true, false, 0] if on restriction phase', () => { + const { result } = renderHook(() => useAirGappedRestriction(), { + legacyRoot: true, + wrapper: mockAppRoot().withSetting('Cloud_Workspace_AirGapped_Restrictions_Remaining_Days', 0).build(), + }); + + expect(result.current).toEqual([true, false, 0]); + }); +}); diff --git a/apps/meteor/client/hooks/useAirGappedRestriction.ts b/apps/meteor/client/hooks/useAirGappedRestriction.ts new file mode 100644 index 000000000000..fbb502d5b49d --- /dev/null +++ b/apps/meteor/client/hooks/useAirGappedRestriction.ts @@ -0,0 +1,19 @@ +import { useSetting } from '@rocket.chat/ui-contexts'; + +export const useAirGappedRestriction = (): [isRestrictionPhase: boolean, isWarningPhase: boolean, remainingDays: number] => { + const airGappedRestrictionRemainingDays = useSetting('Cloud_Workspace_AirGapped_Restrictions_Remaining_Days'); + + if (typeof airGappedRestrictionRemainingDays !== 'number') { + return [false, false, -1]; + } + + // If this value is negative, the user has a license with valid module + if (airGappedRestrictionRemainingDays < 0) { + return [false, false, airGappedRestrictionRemainingDays]; + } + + const isRestrictionPhase = airGappedRestrictionRemainingDays === 0; + const isWarningPhase = !isRestrictionPhase && airGappedRestrictionRemainingDays <= 7; + + return [isRestrictionPhase, isWarningPhase, airGappedRestrictionRemainingDays]; +}; diff --git a/apps/meteor/client/sidebar/Sidebar.tsx b/apps/meteor/client/sidebar/Sidebar.tsx index b947554f4f3b..683013b38213 100644 --- a/apps/meteor/client/sidebar/Sidebar.tsx +++ b/apps/meteor/client/sidebar/Sidebar.tsx @@ -1,24 +1,22 @@ import { css } from '@rocket.chat/css-in-js'; import { Box } from '@rocket.chat/fuselage'; -import { useSessionStorage } from '@rocket.chat/fuselage-hooks'; -import { useLayout, useSetting, useUserPreference } from '@rocket.chat/ui-contexts'; +import { useLayout, useUserPreference } from '@rocket.chat/ui-contexts'; import React, { memo } from 'react'; import { useOmnichannelEnabled } from '../hooks/omnichannel/useOmnichannelEnabled'; import SidebarRoomList from './RoomList'; import SidebarFooter from './footer'; import SidebarHeader from './header'; +import BannerSection from './sections/BannerSection'; import OmnichannelSection from './sections/OmnichannelSection'; -import StatusDisabledSection from './sections/StatusDisabledSection'; +// TODO unit test airgappedbanner const Sidebar = () => { const showOmnichannel = useOmnichannelEnabled(); const sidebarViewMode = useUserPreference('sidebarViewMode'); const sidebarHideAvatar = !useUserPreference('sidebarDisplayAvatar'); const { sidebar } = useLayout(); - const [bannerDismissed, setBannerDismissed] = useSessionStorage('presence_cap_notifier', false); - const presenceDisabled = useSetting('Presence_broadcast_disabled'); const sidebarLink = css` a { @@ -41,7 +39,7 @@ const Sidebar = () => { data-qa-opened={sidebar.isCollapsed ? 'false' : 'true'} > - {presenceDisabled && !bannerDismissed && setBannerDismissed(true)} />} + {showOmnichannel && } diff --git a/apps/meteor/client/sidebar/sections/AirGappedRestrictionBanner/AirGappedRestrictionBanner.tsx b/apps/meteor/client/sidebar/sections/AirGappedRestrictionBanner/AirGappedRestrictionBanner.tsx new file mode 100644 index 000000000000..dca296a2ea19 --- /dev/null +++ b/apps/meteor/client/sidebar/sections/AirGappedRestrictionBanner/AirGappedRestrictionBanner.tsx @@ -0,0 +1,19 @@ +import { SidebarBanner } from '@rocket.chat/fuselage'; +import { ExternalLink } from '@rocket.chat/ui-client'; +import React from 'react'; +import { useTranslation } from 'react-i18next'; + +import AirGappedRestrictionWarning from './AirGappedRestrictionWarning'; + +const AirGappedRestrictionSection = ({ isRestricted, remainingDays }: { isRestricted: boolean; remainingDays: number }) => { + const { t } = useTranslation(); + + return ( + } + description={{t('Learn_more')}} + /> + ); +}; + +export default AirGappedRestrictionSection; diff --git a/apps/meteor/client/sidebar/sections/AirGappedRestrictionBanner/AirGappedRestrictionWarning.tsx b/apps/meteor/client/sidebar/sections/AirGappedRestrictionBanner/AirGappedRestrictionWarning.tsx new file mode 100644 index 000000000000..d6db0abbf9fd --- /dev/null +++ b/apps/meteor/client/sidebar/sections/AirGappedRestrictionBanner/AirGappedRestrictionWarning.tsx @@ -0,0 +1,27 @@ +import { Box } from '@rocket.chat/fuselage'; +import React from 'react'; +import { Trans } from 'react-i18next'; + +const AirGappedRestrictionWarning = ({ isRestricted, remainingDays }: { isRestricted: boolean; remainingDays: number }) => { + if (isRestricted) { + return ( + + This air-gapped workspace is in read-only mode.{' '} + + Connect it to internet or upgraded to a premium plan to restore full functionality. + + + ); + } + + return ( + + This air-gapped workspace will enter read-only mode in <>{{ remainingDays }} days.{' '} + + Connect it to internet or upgrade to a premium plan to prevent this. + + + ); +}; + +export default AirGappedRestrictionWarning; diff --git a/apps/meteor/client/sidebar/sections/BannerSection.spec.tsx b/apps/meteor/client/sidebar/sections/BannerSection.spec.tsx new file mode 100644 index 000000000000..9486d34a17bc --- /dev/null +++ b/apps/meteor/client/sidebar/sections/BannerSection.spec.tsx @@ -0,0 +1,69 @@ +import { mockAppRoot } from '@rocket.chat/mock-providers'; +import { render, screen } from '@testing-library/react'; +import React from 'react'; + +import BannerSection from './BannerSection'; + +// TODO: test presence banner +describe('Sidebar -> BannerSection -> Airgapped restriction', () => { + it('Should render null if restricted and not admin', () => { + render(, { + legacyRoot: true, + wrapper: mockAppRoot() + .withJohnDoe({ roles: ['user'] }) + .withSetting('Cloud_Workspace_AirGapped_Restrictions_Remaining_Days', 0) + .build(), + }); + + expect(screen.queryByText('air-gapped', { exact: false })).not.toBeInTheDocument(); + }); + + it('Should render null if admin and not restricted or warning', () => { + render(, { + legacyRoot: true, + wrapper: mockAppRoot().withJohnDoe().withSetting('Cloud_Workspace_AirGapped_Restrictions_Remaining_Days', 8).build(), + }); + + expect(screen.queryByText('air-gapped', { exact: false })).not.toBeInTheDocument(); + }); + + it('Should render warning message if admin and warning phase', () => { + render(, { + legacyRoot: true, + wrapper: mockAppRoot() + .withJohnDoe() + .withRole('admin') + .withSetting('Cloud_Workspace_AirGapped_Restrictions_Remaining_Days', 7) + .build(), + }); + + expect(screen.getByText('will enter read-only', { exact: false })).toBeInTheDocument(); + }); + + it('Should render restriction message if admin and restricted phase', () => { + render(, { + legacyRoot: true, + wrapper: mockAppRoot() + .withJohnDoe() + .withRole('admin') + .withSetting('Cloud_Workspace_AirGapped_Restrictions_Remaining_Days', 0) + .build(), + }); + + expect(screen.getByText('is in read-only', { exact: false })).toBeInTheDocument(); + }); + + it('Should render restriction message instead of another banner', () => { + render(, { + legacyRoot: true, + wrapper: mockAppRoot() + .withJohnDoe() + .withRole('admin') + .withSetting('Cloud_Workspace_AirGapped_Restrictions_Remaining_Days', 0) + .withSetting('Presence_broadcast_disabled', true) + .build(), + }); + + expect(screen.getByText('is in read-only', { exact: false })).toBeInTheDocument(); + }); +}); diff --git a/apps/meteor/client/sidebar/sections/BannerSection.tsx b/apps/meteor/client/sidebar/sections/BannerSection.tsx new file mode 100644 index 000000000000..f6ce2ac835c0 --- /dev/null +++ b/apps/meteor/client/sidebar/sections/BannerSection.tsx @@ -0,0 +1,27 @@ +import { useSessionStorage } from '@rocket.chat/fuselage-hooks'; +import { useRole, useSetting } from '@rocket.chat/ui-contexts'; +import React from 'react'; + +import { useAirGappedRestriction } from '../../hooks/useAirGappedRestriction'; +import AirGappedRestrictionBanner from './AirGappedRestrictionBanner/AirGappedRestrictionBanner'; +import StatusDisabledBanner from './StatusDisabledBanner'; + +const BannerSection = () => { + const [isRestricted, isWarning, remainingDays] = useAirGappedRestriction(); + const isAdmin = useRole('admin'); + + const [bannerDismissed, setBannerDismissed] = useSessionStorage('presence_cap_notifier', false); + const presenceDisabled = useSetting('Presence_broadcast_disabled'); + + if ((isWarning || isRestricted) && isAdmin) { + return ; + } + + if (presenceDisabled && !bannerDismissed) { + return setBannerDismissed(true)} />; + } + + return null; +}; + +export default BannerSection; diff --git a/apps/meteor/client/sidebar/sections/StatusDisabledSection.tsx b/apps/meteor/client/sidebar/sections/StatusDisabledBanner.tsx similarity index 81% rename from apps/meteor/client/sidebar/sections/StatusDisabledSection.tsx rename to apps/meteor/client/sidebar/sections/StatusDisabledBanner.tsx index c8f56ffe5458..f9525ec93337 100644 --- a/apps/meteor/client/sidebar/sections/StatusDisabledSection.tsx +++ b/apps/meteor/client/sidebar/sections/StatusDisabledBanner.tsx @@ -4,7 +4,7 @@ import { useTranslation } from 'react-i18next'; import { useStatusDisabledModal } from '../../views/admin/customUserStatus/hooks/useStatusDisabledModal'; -const StatusDisabledSection = ({ onDismiss }: { onDismiss: () => void }) => { +const StatusDisabledBanner = ({ onDismiss }: { onDismiss: () => void }) => { const { t } = useTranslation(); const handleStatusDisabledModal = useStatusDisabledModal(); @@ -18,4 +18,4 @@ const StatusDisabledSection = ({ onDismiss }: { onDismiss: () => void }) => { ); }; -export default StatusDisabledSection; +export default StatusDisabledBanner; diff --git a/apps/meteor/client/sidebarv2/Sidebar.tsx b/apps/meteor/client/sidebarv2/Sidebar.tsx index 7209f51507d9..8038bf8b1238 100644 --- a/apps/meteor/client/sidebarv2/Sidebar.tsx +++ b/apps/meteor/client/sidebarv2/Sidebar.tsx @@ -1,18 +1,15 @@ import { SidebarV2 } from '@rocket.chat/fuselage'; -import { useSessionStorage } from '@rocket.chat/fuselage-hooks'; -import { useSetting, useUserPreference } from '@rocket.chat/ui-contexts'; +import { useUserPreference } from '@rocket.chat/ui-contexts'; import React, { memo } from 'react'; import SidebarRoomList from './RoomList'; import SidebarFooter from './footer'; import SearchSection from './header/SearchSection'; -import StatusDisabledSection from './sections/StatusDisabledSection'; +import BannerSection from './sections/BannerSection'; const Sidebar = () => { const sidebarViewMode = useUserPreference('sidebarViewMode'); const sidebarHideAvatar = !useUserPreference('sidebarDisplayAvatar'); - const [bannerDismissed, setBannerDismissed] = useSessionStorage('presence_cap_notifier', false); - const presenceDisabled = useSetting('Presence_broadcast_disabled'); return ( { .join(' ')} > - {presenceDisabled && !bannerDismissed && setBannerDismissed(true)} />} + diff --git a/apps/meteor/client/sidebarv2/sections/AirGappedRestrictionBanner/AirGappedRestrictionBanner.tsx b/apps/meteor/client/sidebarv2/sections/AirGappedRestrictionBanner/AirGappedRestrictionBanner.tsx new file mode 100644 index 000000000000..ff8c53bf484a --- /dev/null +++ b/apps/meteor/client/sidebarv2/sections/AirGappedRestrictionBanner/AirGappedRestrictionBanner.tsx @@ -0,0 +1,23 @@ +import { SidebarV2Banner } from '@rocket.chat/fuselage'; +import React from 'react'; +import { useTranslation } from 'react-i18next'; + +import AirGappedRestrictionWarning from './AirGappedRestrictionWarning'; + +const AirGappedRestrictionSection = ({ isRestricted, remainingDays }: { isRestricted: boolean; remainingDays: number }) => { + const { t } = useTranslation(); + + return ( + } + linkText={t('Learn_more')} + linkProps={{ + target: '_blank', + rel: 'noopener noreferrer', + href: 'https://go.rocket.chat/i/airgapped-restriction', + }} + /> + ); +}; + +export default AirGappedRestrictionSection; diff --git a/apps/meteor/client/sidebarv2/sections/AirGappedRestrictionBanner/AirGappedRestrictionWarning.tsx b/apps/meteor/client/sidebarv2/sections/AirGappedRestrictionBanner/AirGappedRestrictionWarning.tsx new file mode 100644 index 000000000000..5c1555c8408b --- /dev/null +++ b/apps/meteor/client/sidebarv2/sections/AirGappedRestrictionBanner/AirGappedRestrictionWarning.tsx @@ -0,0 +1,27 @@ +import { Box } from '@rocket.chat/fuselage'; +import React from 'react'; +import { Trans } from 'react-i18next'; + +const AirGappedRestrictionWarning = ({ isRestricted, remainingDays }: { isRestricted: boolean; remainingDays: number }) => { + if (isRestricted) { + return ( + + This air-gapped workspace is in read-only mode.{' '} + + Connect it to internet or upgraded to a premium plan to restore full functionality. + + + ); + } + + return ( + + This air-gapped workspace will enter read-only mode in <>{{ remainingDays }} days.{' '} + + Connect it to internet or upgrade to a premium plan to prevent this. + + + ); +}; + +export default AirGappedRestrictionWarning; diff --git a/apps/meteor/client/sidebarv2/sections/BannerSection.spec.tsx b/apps/meteor/client/sidebarv2/sections/BannerSection.spec.tsx new file mode 100644 index 000000000000..9486d34a17bc --- /dev/null +++ b/apps/meteor/client/sidebarv2/sections/BannerSection.spec.tsx @@ -0,0 +1,69 @@ +import { mockAppRoot } from '@rocket.chat/mock-providers'; +import { render, screen } from '@testing-library/react'; +import React from 'react'; + +import BannerSection from './BannerSection'; + +// TODO: test presence banner +describe('Sidebar -> BannerSection -> Airgapped restriction', () => { + it('Should render null if restricted and not admin', () => { + render(, { + legacyRoot: true, + wrapper: mockAppRoot() + .withJohnDoe({ roles: ['user'] }) + .withSetting('Cloud_Workspace_AirGapped_Restrictions_Remaining_Days', 0) + .build(), + }); + + expect(screen.queryByText('air-gapped', { exact: false })).not.toBeInTheDocument(); + }); + + it('Should render null if admin and not restricted or warning', () => { + render(, { + legacyRoot: true, + wrapper: mockAppRoot().withJohnDoe().withSetting('Cloud_Workspace_AirGapped_Restrictions_Remaining_Days', 8).build(), + }); + + expect(screen.queryByText('air-gapped', { exact: false })).not.toBeInTheDocument(); + }); + + it('Should render warning message if admin and warning phase', () => { + render(, { + legacyRoot: true, + wrapper: mockAppRoot() + .withJohnDoe() + .withRole('admin') + .withSetting('Cloud_Workspace_AirGapped_Restrictions_Remaining_Days', 7) + .build(), + }); + + expect(screen.getByText('will enter read-only', { exact: false })).toBeInTheDocument(); + }); + + it('Should render restriction message if admin and restricted phase', () => { + render(, { + legacyRoot: true, + wrapper: mockAppRoot() + .withJohnDoe() + .withRole('admin') + .withSetting('Cloud_Workspace_AirGapped_Restrictions_Remaining_Days', 0) + .build(), + }); + + expect(screen.getByText('is in read-only', { exact: false })).toBeInTheDocument(); + }); + + it('Should render restriction message instead of another banner', () => { + render(, { + legacyRoot: true, + wrapper: mockAppRoot() + .withJohnDoe() + .withRole('admin') + .withSetting('Cloud_Workspace_AirGapped_Restrictions_Remaining_Days', 0) + .withSetting('Presence_broadcast_disabled', true) + .build(), + }); + + expect(screen.getByText('is in read-only', { exact: false })).toBeInTheDocument(); + }); +}); diff --git a/apps/meteor/client/sidebarv2/sections/BannerSection.tsx b/apps/meteor/client/sidebarv2/sections/BannerSection.tsx new file mode 100644 index 000000000000..f6ce2ac835c0 --- /dev/null +++ b/apps/meteor/client/sidebarv2/sections/BannerSection.tsx @@ -0,0 +1,27 @@ +import { useSessionStorage } from '@rocket.chat/fuselage-hooks'; +import { useRole, useSetting } from '@rocket.chat/ui-contexts'; +import React from 'react'; + +import { useAirGappedRestriction } from '../../hooks/useAirGappedRestriction'; +import AirGappedRestrictionBanner from './AirGappedRestrictionBanner/AirGappedRestrictionBanner'; +import StatusDisabledBanner from './StatusDisabledBanner'; + +const BannerSection = () => { + const [isRestricted, isWarning, remainingDays] = useAirGappedRestriction(); + const isAdmin = useRole('admin'); + + const [bannerDismissed, setBannerDismissed] = useSessionStorage('presence_cap_notifier', false); + const presenceDisabled = useSetting('Presence_broadcast_disabled'); + + if ((isWarning || isRestricted) && isAdmin) { + return ; + } + + if (presenceDisabled && !bannerDismissed) { + return setBannerDismissed(true)} />; + } + + return null; +}; + +export default BannerSection; diff --git a/apps/meteor/client/sidebarv2/sections/StatusDisabledSection.tsx b/apps/meteor/client/sidebarv2/sections/StatusDisabledBanner.tsx similarity index 100% rename from apps/meteor/client/sidebarv2/sections/StatusDisabledSection.tsx rename to apps/meteor/client/sidebarv2/sections/StatusDisabledBanner.tsx diff --git a/apps/meteor/client/views/room/composer/ComposerAirGappedRestricted.tsx b/apps/meteor/client/views/room/composer/ComposerAirGappedRestricted.tsx new file mode 100644 index 000000000000..64cf1e0a9a7e --- /dev/null +++ b/apps/meteor/client/views/room/composer/ComposerAirGappedRestricted.tsx @@ -0,0 +1,23 @@ +import { Box, Icon } from '@rocket.chat/fuselage'; +import { MessageFooterCallout } from '@rocket.chat/ui-composer'; +import type { ReactElement } from 'react'; +import React from 'react'; +import { Trans } from 'react-i18next'; + +const ComposerAirGappedRestricted = (): ReactElement => { + return ( + + + + + + Workspace in read-only mode. + + Admins can restore full functionality by connecting it to internet or upgrading to a premium plan. + + + + ); +}; + +export default ComposerAirGappedRestricted; diff --git a/apps/meteor/client/views/room/composer/ComposerContainer.tsx b/apps/meteor/client/views/room/composer/ComposerContainer.tsx index 7d3e09847b34..0e870067133f 100644 --- a/apps/meteor/client/views/room/composer/ComposerContainer.tsx +++ b/apps/meteor/client/views/room/composer/ComposerContainer.tsx @@ -3,7 +3,9 @@ import { usePermission } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; import React, { memo } from 'react'; +import { useAirGappedRestriction } from '../../../hooks/useAirGappedRestriction'; import { useRoom } from '../contexts/RoomContext'; +import ComposerAirGappedRestricted from './ComposerAirGappedRestricted'; import ComposerAnonymous from './ComposerAnonymous'; import ComposerArchived from './ComposerArchived'; import ComposerBlocked from './ComposerBlocked'; @@ -21,6 +23,7 @@ import { useMessageComposerIsReadOnly } from './hooks/useMessageComposerIsReadOn const ComposerContainer = ({ children, ...props }: ComposerMessageProps): ReactElement => { const room = useRoom(); + const canJoinWithoutCode = usePermission('join-without-join-code'); const mustJoinWithCode = !props.subscription && room.joinCodeRequired && !canJoinWithoutCode; @@ -33,6 +36,12 @@ const ComposerContainer = ({ children, ...props }: ComposerMessageProps): ReactE const isFederation = isRoomFederated(room); const isVoip = isVoipRoom(room); + const [isAirGappedRestricted] = useAirGappedRestriction(); + + if (isAirGappedRestricted) { + return ; + } + if (isOmnichannel) { return ; } diff --git a/apps/meteor/ee/app/license/server/airGappedRestrictions.ts b/apps/meteor/ee/app/license/server/airGappedRestrictions.ts new file mode 100644 index 000000000000..01a15e72e820 --- /dev/null +++ b/apps/meteor/ee/app/license/server/airGappedRestrictions.ts @@ -0,0 +1,40 @@ +import { AirGappedRestriction, License } from '@rocket.chat/license'; +import { Settings, Statistics } from '@rocket.chat/models'; + +import { notifyOnSettingChangedById } from '../../../../app/lib/server/lib/notifyListener'; +import { i18n } from '../../../../server/lib/i18n'; +import { sendMessagesToAdmins } from '../../../../server/lib/sendMessagesToAdmins'; + +const updateRestrictionSetting = async (remainingDays: number) => { + await Settings.updateValueById('Cloud_Workspace_AirGapped_Restrictions_Remaining_Days', remainingDays); + void notifyOnSettingChangedById('Cloud_Workspace_AirGapped_Restrictions_Remaining_Days'); +}; + +const sendRocketCatWarningToAdmins = async (remainingDays: number) => { + const lastDayOrNoRestrictionsAtAll = remainingDays <= 0; + if (lastDayOrNoRestrictionsAtAll) { + return; + } + if (AirGappedRestriction.isWarningPeriod(remainingDays)) { + await sendMessagesToAdmins({ + msgs: async ({ adminUser }) => ({ + msg: i18n.t('AirGapped_Restriction_Warning', { lng: adminUser.language || 'en', remainingDays }), + }), + }); + } +}; + +AirGappedRestriction.on('remainingDays', async ({ days }: { days: number }) => { + await updateRestrictionSetting(days); + await sendRocketCatWarningToAdmins(days); +}); + +License.onValidateLicense(async () => { + const token = await Statistics.findLastStatsToken(); + void AirGappedRestriction.computeRestriction(token); +}); + +License.onRemoveLicense(async () => { + const token = await Statistics.findLastStatsToken(); + void AirGappedRestriction.computeRestriction(token); +}); diff --git a/apps/meteor/ee/app/license/server/index.ts b/apps/meteor/ee/app/license/server/index.ts index 9177532a9e21..efef260a6cb0 100644 --- a/apps/meteor/ee/app/license/server/index.ts +++ b/apps/meteor/ee/app/license/server/index.ts @@ -1,2 +1,3 @@ import './settings'; import './methods'; +import './airGappedRestrictions'; diff --git a/apps/meteor/ee/app/license/server/settings.ts b/apps/meteor/ee/app/license/server/settings.ts index 590a937fe18f..045c74e331e4 100644 --- a/apps/meteor/ee/app/license/server/settings.ts +++ b/apps/meteor/ee/app/license/server/settings.ts @@ -20,4 +20,9 @@ await settingsRegistry.addGroup('Enterprise', async function () { i18nLabel: 'Status', }); }); + await this.add('Cloud_Workspace_AirGapped_Restrictions_Remaining_Days', -1, { + type: 'int', + readonly: true, + public: true, + }); }); diff --git a/apps/meteor/ee/server/patches/airGappedRestrictionsWrapper.ts b/apps/meteor/ee/server/patches/airGappedRestrictionsWrapper.ts new file mode 100644 index 000000000000..dabafe87c9fc --- /dev/null +++ b/apps/meteor/ee/server/patches/airGappedRestrictionsWrapper.ts @@ -0,0 +1,10 @@ +import { AirGappedRestriction } from '@rocket.chat/license'; + +import { applyAirGappedRestrictionsValidation } from '../../../app/license/server/airGappedRestrictionsWrapper'; + +applyAirGappedRestrictionsValidation.patch(async (_: any, fn: () => Promise): Promise => { + if (AirGappedRestriction.restricted) { + throw new Error('restricted-workspace'); + } + return fn(); +}); diff --git a/apps/meteor/ee/server/patches/index.ts b/apps/meteor/ee/server/patches/index.ts index ab3f4bf147c2..9e6cab1e0924 100644 --- a/apps/meteor/ee/server/patches/index.ts +++ b/apps/meteor/ee/server/patches/index.ts @@ -1,3 +1,4 @@ import './closeBusinessHour'; import './getInstanceList'; import './isDepartmentCreationAvailable'; +import './airGappedRestrictionsWrapper'; diff --git a/apps/meteor/ee/tests/unit/server/airgappedRestrictions/airGappedRestrictionsWrapper.spec.ts b/apps/meteor/ee/tests/unit/server/airgappedRestrictions/airGappedRestrictionsWrapper.spec.ts new file mode 100644 index 000000000000..982efe01158e --- /dev/null +++ b/apps/meteor/ee/tests/unit/server/airgappedRestrictions/airGappedRestrictionsWrapper.spec.ts @@ -0,0 +1,34 @@ +import { expect } from 'chai'; +import proxyquire from 'proxyquire'; +import sinon from 'sinon'; + +import { applyAirGappedRestrictionsValidation } from '../../../../../app/license/server/airGappedRestrictionsWrapper'; + +let restrictionFlag = true; + +const airgappedModule = { + get restricted() { + return restrictionFlag; + }, +}; + +proxyquire.noCallThru().load('../../../../server/patches/airGappedRestrictionsWrapper.ts', { + '@rocket.chat/license': { + AirGappedRestriction: airgappedModule, + }, + '../../../app/license/server/airGappedRestrictionsWrapper': { + applyAirGappedRestrictionsValidation, + }, +}); + +describe('#airGappedRestrictionsWrapper()', () => { + it('should throw an error when the workspace is restricted', async () => { + await expect(applyAirGappedRestrictionsValidation(sinon.stub())).to.be.rejectedWith('restricted-workspace'); + }); + it('should NOT throw an error when the workspace is not restricted', async () => { + restrictionFlag = false; + const spy = sinon.stub(); + await expect(applyAirGappedRestrictionsValidation(spy)).to.eventually.equal(undefined); + expect(spy.calledOnce).to.be.true; + }); +}); diff --git a/apps/meteor/ee/tests/unit/server/airgappedRestrictions/airgappedRestrictions.spec.ts b/apps/meteor/ee/tests/unit/server/airgappedRestrictions/airgappedRestrictions.spec.ts new file mode 100644 index 000000000000..a1aace7ae7dd --- /dev/null +++ b/apps/meteor/ee/tests/unit/server/airgappedRestrictions/airgappedRestrictions.spec.ts @@ -0,0 +1,126 @@ +import { Emitter } from '@rocket.chat/emitter'; +import { expect } from 'chai'; +import proxyquire from 'proxyquire'; +import sinon from 'sinon'; + +let promises: Array> = []; + +class AirgappedRestriction extends Emitter<{ remainingDays: { days: number } }> { + computeRestriction = sinon.spy(); + + isWarningPeriod = sinon.stub(); + + on(type: any, cb: any): any { + const newCb = (...args: any) => { + promises.push(cb(...args)); + }; + return super.on(type, newCb); + } +} + +const airgappedRestrictionObj = new AirgappedRestriction(); + +const mocks = { + sendMessagesToAdmins: ({ msgs }: any) => { + msgs({ adminUser: { language: 'pt-br' } }); + }, + settingsUpdate: sinon.spy(), + notifySetting: sinon.spy(), + i18n: sinon.spy(), + findLastToken: sinon.stub(), +}; + +const licenseMock = { + validateCb: async () => undefined, + removeCb: async () => undefined, + onValidateLicense: async (cb: any) => { + licenseMock.validateCb = cb; + }, + onRemoveLicense: async (cb: any) => { + licenseMock.removeCb = cb; + }, +}; + +proxyquire.noCallThru().load('../../../../app/license/server/airGappedRestrictions.ts', { + '@rocket.chat/license': { + AirGappedRestriction: airgappedRestrictionObj, + License: licenseMock, + }, + '@rocket.chat/models': { + Settings: { + updateValueById: mocks.settingsUpdate, + }, + Statistics: { + findLastStatsToken: mocks.findLastToken, + }, + }, + '../../../../app/lib/server/lib/notifyListener': { + notifyOnSettingChangedById: mocks.notifySetting, + }, + '../../../../server/lib/i18n': { + i18n: { + t: mocks.i18n, + }, + }, + '../../../../server/lib/sendMessagesToAdmins': { + sendMessagesToAdmins: mocks.sendMessagesToAdmins, + }, +}); + +describe('airgappedRestrictions', () => { + afterEach(() => { + Object.values(mocks).forEach((mock) => { + if ('resetHistory' in mock) { + mock.resetHistory(); + } + if ('reset' in mock) { + mock.reset(); + } + }); + airgappedRestrictionObj.computeRestriction.resetHistory(); + airgappedRestrictionObj.isWarningPeriod.reset(); + promises = []; + }); + it('should update setting when restriction is removed', async () => { + airgappedRestrictionObj.emit('remainingDays', { days: -1 }); + + await Promise.all(promises); + expect(mocks.settingsUpdate.calledWith('Cloud_Workspace_AirGapped_Restrictions_Remaining_Days', -1)).to.be.true; + expect(mocks.notifySetting.calledWith('Cloud_Workspace_AirGapped_Restrictions_Remaining_Days')).to.be.true; + expect(airgappedRestrictionObj.isWarningPeriod.called).to.be.false; + }); + + it('should update setting when restriction is applied', async () => { + airgappedRestrictionObj.emit('remainingDays', { days: 0 }); + + await Promise.all(promises); + expect(mocks.settingsUpdate.calledWith('Cloud_Workspace_AirGapped_Restrictions_Remaining_Days', 0)).to.be.true; + expect(mocks.notifySetting.calledWith('Cloud_Workspace_AirGapped_Restrictions_Remaining_Days')).to.be.true; + expect(airgappedRestrictionObj.isWarningPeriod.called).to.be.false; + }); + + it('should update setting and send rocket.cat message when in warning period', async () => { + airgappedRestrictionObj.emit('remainingDays', { days: 1 }); + airgappedRestrictionObj.isWarningPeriod.returns(true); + + await Promise.all(promises); + expect(mocks.settingsUpdate.calledWith('Cloud_Workspace_AirGapped_Restrictions_Remaining_Days', 1)).to.be.true; + expect(mocks.notifySetting.calledWith('Cloud_Workspace_AirGapped_Restrictions_Remaining_Days')).to.be.true; + expect(airgappedRestrictionObj.isWarningPeriod.called).to.be.true; + expect(mocks.i18n.calledWith('AirGapped_Restriction_Warning', { lng: 'pt-br' })); + }); + + it('should recompute restriction if license is applied', async () => { + mocks.findLastToken.returns('token'); + await licenseMock.validateCb(); + expect(mocks.findLastToken.calledOnce).to.be.true; + expect(airgappedRestrictionObj.computeRestriction.calledWith('token')).to.be.true; + }); + + it('should recompute restriction if license is removed', async () => { + mocks.findLastToken.returns('token'); + await licenseMock.removeCb(); + expect(mocks.findLastToken.calledOnce).to.be.true; + expect(airgappedRestrictionObj.computeRestriction.calledWith('token')).to.be.true; + }); +}); diff --git a/apps/meteor/jest.config.ts b/apps/meteor/jest.config.ts index b944cd795620..190ad361d4ad 100644 --- a/apps/meteor/jest.config.ts +++ b/apps/meteor/jest.config.ts @@ -35,6 +35,8 @@ export default { '/app/livechat/server/business-hour/**/*.spec.ts?(x)', '/app/livechat/server/api/**/*.spec.ts', '/ee/app/authorization/server/validateUserRoles.spec.ts', + '/ee/app/license/server/**/*.spec.ts', + '/ee/server/patches/**/*.spec.ts', '/app/cloud/server/functions/supportedVersionsToken/**.spec.ts', '/app/utils/lib/**.spec.ts', ], diff --git a/apps/meteor/server/cron/usageReport.ts b/apps/meteor/server/cron/usageReport.ts new file mode 100644 index 000000000000..c774335c9334 --- /dev/null +++ b/apps/meteor/server/cron/usageReport.ts @@ -0,0 +1,18 @@ +import { cronJobs } from '@rocket.chat/cron'; +import { AirGappedRestriction } from '@rocket.chat/license'; +import type { Logger } from '@rocket.chat/logger'; + +import { sendUsageReport } from '../../app/statistics/server/functions/sendUsageReport'; + +export async function usageReportCron(logger: Logger): Promise { + const name = 'Generate and save statistics'; + const statsToken = await sendUsageReport(logger); + void AirGappedRestriction.computeRestriction(statsToken); + + const now = new Date(); + + await cronJobs.add(name, `12 ${now.getHours()} * * *`, async () => { + const statsToken = await sendUsageReport(logger); + void AirGappedRestriction.computeRestriction(statsToken); + }); +} diff --git a/apps/meteor/server/models/raw/Statistics.ts b/apps/meteor/server/models/raw/Statistics.ts index bad44ee07c23..310d87f07d5d 100644 --- a/apps/meteor/server/models/raw/Statistics.ts +++ b/apps/meteor/server/models/raw/Statistics.ts @@ -26,6 +26,22 @@ export class StatisticsRaw extends BaseRaw implements IStatisticsModel { return records?.[0]; } + async findLastStatsToken(): Promise { + const records = await this.find( + {}, + { + sort: { + createdAt: -1, + }, + projection: { + statsToken: 1, + }, + limit: 1, + }, + ).toArray(); + return records?.[0]?.statsToken; + } + async findMonthlyPeakConnections() { const oneMonthAgo = new Date(); oneMonthAgo.setDate(oneMonthAgo.getDate() - 30); diff --git a/apps/meteor/server/startup/cron.ts b/apps/meteor/server/startup/cron.ts index 6057186a1e4e..308d1297a01a 100644 --- a/apps/meteor/server/startup/cron.ts +++ b/apps/meteor/server/startup/cron.ts @@ -5,8 +5,8 @@ import { federationCron } from '../cron/federation'; import { npsCron } from '../cron/nps'; import { oembedCron } from '../cron/oembed'; import { startCron } from '../cron/start'; -import { statsCron } from '../cron/statistics'; import { temporaryUploadCleanupCron } from '../cron/temporaryUploadsCleanup'; +import { usageReportCron } from '../cron/usageReport'; import { userDataDownloadsCron } from '../cron/userDataDownloads'; import { videoConferencesCron } from '../cron/videoConferences'; @@ -16,7 +16,7 @@ Meteor.defer(async () => { await startCron(); await oembedCron(); - await statsCron(logger); + await usageReportCron(logger); await npsCron(); await temporaryUploadCleanupCron(); await federationCron(); diff --git a/ee/packages/license/src/AirGappedRestriction.spec.ts b/ee/packages/license/src/AirGappedRestriction.spec.ts new file mode 100644 index 000000000000..960e170c5b16 --- /dev/null +++ b/ee/packages/license/src/AirGappedRestriction.spec.ts @@ -0,0 +1,113 @@ +import { AirGappedRestriction } from './AirGappedRestriction'; +import { StatsTokenBuilder } from './MockedLicenseBuilder'; +import { License } from './licenseImp'; + +jest.mock('./licenseImp', () => ({ + License: { + hasValidLicense: jest.fn().mockReturnValue(false), + }, +})); + +describe('AirGappedRestriction', () => { + describe('#computeRestriction()', () => { + it('should notify remaining days = 0 (apply restriction) when token is not a string', async () => { + const handler = jest.fn(); + + AirGappedRestriction.on('remainingDays', handler); + await AirGappedRestriction.computeRestriction(); + + expect(handler).toHaveBeenCalledTimes(1); + expect(handler).toHaveBeenCalledWith({ + days: 0, + }); + expect(AirGappedRestriction.restricted).toBe(true); + }); + it('should notify remaining days = 0 (apply restriction) when it was not possible to decrypt the stats token', async () => { + const handler = jest.fn(); + + AirGappedRestriction.on('remainingDays', handler); + await AirGappedRestriction.computeRestriction('invalid-token'); + + expect(handler).toHaveBeenCalledTimes(1); + expect(handler).toHaveBeenCalledWith({ + days: 0, + }); + expect(AirGappedRestriction.restricted).toBe(true); + }); + it('should notify remaining days (8) within the accepted range (1 - 10) when the last reported stats happened 2 days ago', async () => { + const now = new Date(); + now.setDate(now.getDate() - 2); + const token = await new StatsTokenBuilder().withTimeStamp(now).sign(); + const handler = jest.fn(); + + AirGappedRestriction.on('remainingDays', handler); + await AirGappedRestriction.computeRestriction(token); + + expect(handler).toHaveBeenCalledTimes(1); + expect(handler).toHaveBeenCalledWith({ + days: 8, + }); + expect(AirGappedRestriction.restricted).toBe(false); + }); + it('should notify remaining days = 0 (apply restrictions) when the last reported stats happened more than 10 days ago', async () => { + const now = new Date(); + now.setDate(now.getDate() - 11); + const token = await new StatsTokenBuilder().withTimeStamp(now).sign(); + const handler = jest.fn(); + + AirGappedRestriction.on('remainingDays', handler); + await AirGappedRestriction.computeRestriction(token); + + expect(handler).toHaveBeenCalledTimes(1); + expect(handler).toHaveBeenCalledWith({ + days: 0, + }); + expect(AirGappedRestriction.restricted).toBe(true); + }); + it('should notify remaining days = 0 (apply restrictions) when the last reported stats happened 10 days ago', async () => { + const now = new Date(); + now.setDate(now.getDate() - 10); + const token = await new StatsTokenBuilder().withTimeStamp(now).sign(); + const handler = jest.fn(); + + AirGappedRestriction.on('remainingDays', handler); + await AirGappedRestriction.computeRestriction(token); + + expect(handler).toHaveBeenCalledTimes(1); + expect(handler).toHaveBeenCalledWith({ + days: 0, + }); + expect(AirGappedRestriction.restricted).toBe(true); + }); + it('should notify remaining days = -1 when has valid license', () => { + const handler = jest.fn(); + (License.hasValidLicense as jest.Mock).mockReturnValueOnce(true); + + AirGappedRestriction.on('remainingDays', handler); + AirGappedRestriction.computeRestriction(); + + expect(handler).toHaveBeenCalledTimes(1); + expect(handler).toHaveBeenCalledWith({ + days: -1, + }); + expect(AirGappedRestriction.restricted).toBe(false); + }); + }); + describe('#isWarningPeriod', () => { + it('should return true if value is between or exactly 0 and 7', async () => { + expect(AirGappedRestriction.isWarningPeriod(0)).toBe(true); + expect(AirGappedRestriction.isWarningPeriod(1)).toBe(true); + expect(AirGappedRestriction.isWarningPeriod(2)).toBe(true); + expect(AirGappedRestriction.isWarningPeriod(3)).toBe(true); + expect(AirGappedRestriction.isWarningPeriod(4)).toBe(true); + expect(AirGappedRestriction.isWarningPeriod(5)).toBe(true); + expect(AirGappedRestriction.isWarningPeriod(6)).toBe(true); + expect(AirGappedRestriction.isWarningPeriod(7)).toBe(true); + }); + it('should return false if value is lesser than 0 or bigger than 7', async () => { + expect(AirGappedRestriction.isWarningPeriod(-1)).toBe(false); + expect(AirGappedRestriction.isWarningPeriod(8)).toBe(false); + expect(AirGappedRestriction.isWarningPeriod(10)).toBe(false); + }); + }); +}); diff --git a/ee/packages/license/src/AirGappedRestriction.ts b/ee/packages/license/src/AirGappedRestriction.ts new file mode 100644 index 000000000000..1ba3bb1b3299 --- /dev/null +++ b/ee/packages/license/src/AirGappedRestriction.ts @@ -0,0 +1,73 @@ +import EventEmitter from 'events'; + +import { License } from '.'; +import { decryptStatsToken } from './token'; + +const DAY_IN_MS = 24 * 60 * 60 * 1000; +const NO_ACTION_PERIOD_IN_DAYS = 3; +const WARNING_PERIOD_IN_DAYS = 7; + +class AirGappedRestrictionClass extends EventEmitter { + #restricted = true; + + public get restricted(): boolean { + return this.#restricted; + } + + public async computeRestriction(encryptedToken?: string): Promise { + if (License.hasValidLicense()) { + this.removeRestrictionsUnderLicense(); + return; + } + + if (typeof encryptedToken !== 'string') { + this.applyRestrictions(); + return; + } + + return this.checkRemainingDaysSinceLastStatsReport(encryptedToken); + } + + private async checkRemainingDaysSinceLastStatsReport(encryptedToken: string): Promise { + try { + const { timestamp: lastStatsReportTimestamp } = JSON.parse(await decryptStatsToken(encryptedToken)); + const now = new Date(); + const lastStatsReport = new Date(lastStatsReportTimestamp); + const nowUTC = Date.UTC(now.getFullYear(), now.getMonth(), now.getDate()); + const lastStatsReportUTC = Date.UTC(lastStatsReport.getFullYear(), lastStatsReport.getMonth(), lastStatsReport.getDate()); + + const daysSinceLastStatsReport = Math.floor((nowUTC - lastStatsReportUTC) / DAY_IN_MS); + + this.notifyRemainingDaysUntilRestriction(daysSinceLastStatsReport); + } catch { + this.applyRestrictions(); + } + } + + private applyRestrictions(): void { + this.notifyRemainingDaysUntilRestriction(NO_ACTION_PERIOD_IN_DAYS + WARNING_PERIOD_IN_DAYS); + } + + private removeRestrictionsUnderLicense(): void { + this.#restricted = false; + this.emit('remainingDays', { days: -1 }); + } + + public isWarningPeriod(days: number) { + if (days < 0) { + return false; + } + return days <= WARNING_PERIOD_IN_DAYS; + } + + private notifyRemainingDaysUntilRestriction(daysSinceLastStatsReport: number): void { + const remainingDaysUntilRestriction = Math.max(NO_ACTION_PERIOD_IN_DAYS + WARNING_PERIOD_IN_DAYS - daysSinceLastStatsReport, 0); + + this.#restricted = remainingDaysUntilRestriction === 0; + this.emit('remainingDays', { days: remainingDaysUntilRestriction }); + } +} + +const airGappedRestriction = new AirGappedRestrictionClass(); + +export { airGappedRestriction as AirGappedRestriction }; diff --git a/ee/packages/license/src/MockedLicenseBuilder.ts b/ee/packages/license/src/MockedLicenseBuilder.ts index d9def5b6b0d5..a0ae1a37f9a3 100644 --- a/ee/packages/license/src/MockedLicenseBuilder.ts +++ b/ee/packages/license/src/MockedLicenseBuilder.ts @@ -10,7 +10,7 @@ import { type Timestamp, } from '@rocket.chat/core-typings'; -import { encrypt } from './token'; +import { encrypt, encryptStatsToken } from './token'; export class MockedLicenseBuilder { information: { @@ -241,3 +241,31 @@ export class MockedLicenseBuilder { return encrypt(this.build()); } } + +export class StatsTokenBuilder { + private token: Record; + + constructor() { + this.token = { + workspaceId: '123456789', + uniqueId: '123456789', + recordId: '123456789', + timestamp: new Date().toISOString(), + info: {}, + }; + } + + public withTimeStamp(date: Date): StatsTokenBuilder { + this.token.timestamp = date.toISOString(); + + return this; + } + + public build(): Record { + return this.token; + } + + public sign(): Promise { + return encryptStatsToken(this.token); + } +} diff --git a/ee/packages/license/src/index.ts b/ee/packages/license/src/index.ts index 3536d572105d..bb816ea4183b 100644 --- a/ee/packages/license/src/index.ts +++ b/ee/packages/license/src/index.ts @@ -2,3 +2,4 @@ export { DuplicatedLicenseError } from './errors/DuplicatedLicenseError'; export * from './licenseImp'; export * from './MockedLicenseBuilder'; export * from './applyLicense'; +export * from './AirGappedRestriction'; diff --git a/ee/packages/license/src/token.ts b/ee/packages/license/src/token.ts index 09ad5a940b58..9a77adbc4418 100644 --- a/ee/packages/license/src/token.ts +++ b/ee/packages/license/src/token.ts @@ -3,13 +3,36 @@ import crypto from 'crypto'; import type { ILicenseV3 } from '@rocket.chat/core-typings'; import { verify, sign, getPairs } from '@rocket.chat/jwt'; +const base64Decode = (key: string): string => Buffer.from(key, 'base64').toString('utf-8'); + const PUBLIC_LICENSE_KEY_V2 = 'LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQ0lqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FnOEFNSUlDQ2dLQ0FnRUFxV1Nza2Q5LzZ6Ung4a3lQY2ljcwpiMzJ3Mnd4VnV3N3lCVDk2clEvOEQreU1lQ01POXdTU3BIYS85bkZ5d293RXRpZ3B0L3dyb1BOK1ZHU3didHdQCkZYQmVxRWxCbmRHRkFsODZlNStFbGlIOEt6L2hHbkNtSk5tWHB4RUsyUkUwM1g0SXhzWVg3RERCN010eC9pcXMKY2pCL091dlNCa2ppU2xlUzdibE5JVC9kQTdLNC9DSjNvaXUwMmJMNEV4Y2xDSGVwenFOTWVQM3dVWmdweE9uZgpOT3VkOElYWUs3M3pTY3VFOEUxNTdZd3B6Q0twVmFIWDdaSmY4UXVOc09PNVcvYUlqS2wzTDYyNjkrZUlPRXJHCndPTm1hSG56Zmc5RkxwSmh6Z3BPMzhhVm43NnZENUtLakJhaldza1krNGEyZ1NRbUtOZUZxYXFPb3p5RUZNMGUKY0ZXWlZWWjNMZWg0dkVNb1lWUHlJeng5Nng4ZjIveW1QbmhJdXZRdjV3TjRmeWVwYTdFWTVVQ2NwNzF6OGtmUAo0RmNVelBBMElEV3lNaWhYUi9HNlhnUVFaNEdiL3FCQmh2cnZpSkNGemZZRGNKZ0w3RmVnRllIUDNQR0wwN1FnCnZMZXZNSytpUVpQcnhyYnh5U3FkUE9rZ3VyS2pWclhUVXI0QTlUZ2lMeUlYNVVsSnEzRS9SVjdtZk9xWm5MVGEKU0NWWEhCaHVQbG5DR1pSMDFUb1RDZktoTUcxdTBDRm5MMisxNWhDOWZxT21XdjlRa2U0M3FsSjBQZ0YzVkovWAp1eC9tVHBuazlnbmJHOUpIK21mSDM5Um9GdlROaW5Zd1NNdll6dXRWT242OXNPemR3aERsYTkwbDNBQ2g0eENWCks3Sk9YK3VIa29OdTNnMmlWeGlaVU0wQ0F3RUFBUT09Ci0tLS0tRU5EIFBVQkxJQyBLRVktLS0tLQo='; +const PUBLIC_STATS_KEY_V2 = + 'LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUFuT0IvR3lCUXg1cVJZOC83dGxhTApkd0hSOWZBWHVzQ3ZBVU9YRjFPYjExaWx4ejdqY0pqWitaRjE2UTk3bjF3UDlvQnJMUDg5M3ZXUlc1bUtDdFpDClJQNTdJU1o2YjlHOXoyNStnV0NEa3ZZUUg1djJlMGoxUnE5TnNYMktlTWViOUZXenRQVFEvdDRvUW1SdUpiTEIKV013ajRRbHZ4OStpdWpkdGdQYmx5S0VFM0I3T1RPWk8xNzNOK2RDZW8wOWlQcStyKzBiRjNGSTRVcVFiZkFrdApOdGd0OUxVbjdlalNaQXNhSTZPbVBmOVVvSzM4NUdxWUxvbk1WbXBFbmZyRDlHSHl0OXFOY2pCTmZ1MUJLZWlUClBwVit6WE1FViszLzZ2bi84OW1id28zS0ZtTnRxVTJpRk5Yak5Cb2w1ZE82N3VlM0pURXBVQmJWRHZ2V3gwTk8KWndJREFRQUIKLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg=='; -const PUBLIC_LICENSE_KEY_V3 = Buffer.from(PUBLIC_LICENSE_KEY_V2, 'base64').toString('utf-8'); +const PUBLIC_LICENSE_KEY_V3 = base64Decode(PUBLIC_LICENSE_KEY_V2); let TEST_KEYS: [string, string] | undefined = undefined; +export async function decryptStatsToken(encrypted: string): Promise { + if (process.env.NODE_ENV?.toLowerCase() === 'test') { + TEST_KEYS = TEST_KEYS ?? (await getPairs()); + + if (!TEST_KEYS) { + throw new Error('Missing PUBLIC_STATS_KEY_V3'); + } + + const [spki] = TEST_KEYS; + + const [payload] = await verify(encrypted, spki); + return JSON.stringify(payload); + } + + const [payload] = await verify(encrypted, base64Decode(PUBLIC_STATS_KEY_V2)); + + return JSON.stringify(payload); +} + export async function decrypt(encrypted: string): Promise { if (process.env.NODE_ENV === 'test') { if (encrypted.startsWith('RCV3_')) { @@ -52,3 +75,15 @@ export async function encrypt(license: ILicenseV3): Promise { return `RCV3_${await sign(license, pkcs8)}`; } + +export async function encryptStatsToken(data: Record): Promise { + if (process.env.NODE_ENV !== 'test') { + throw new Error('This function should only be used in tests'); + } + + TEST_KEYS = TEST_KEYS ?? (await getPairs()); + + const [, pkcs8] = TEST_KEYS; + + return sign(data, pkcs8); +} diff --git a/packages/i18n/src/locales/en.i18n.json b/packages/i18n/src/locales/en.i18n.json index e199e8a1dcb6..de4cfac7193f 100644 --- a/packages/i18n/src/locales/en.i18n.json +++ b/packages/i18n/src/locales/en.i18n.json @@ -402,6 +402,9 @@ "Agents": "Agents", "Agree": "Agree", "AI_Actions": "AI Actions", + "AirGapped_Restriction_Warning": "**Your air-gapped workspace will enter read-only mode in {{remainingDays}} days.** \n Users will still be able to access rooms and read existing messages but will be unable to send new messages. \n Reconnect it to the internet or [upgrade to a premium license](https://go.rocket.chat/i/air-gapped) to prevent this.", + "Airgapped_workspace_warning": "This air-gapped workspace will enter read-only mode in {{remainingDays}} days. <2>Connect it to internet or upgrade to a premium plan to prevent this.", + "Airgapped_workspace_restriction": "This air-gapped workspace is in read-only mode. <2>Connect it to internet or upgraded to a premium plan to restore full functionality.", "Alerts": "Alerts", "Alias": "Alias", "Alias_Format": "Alias Format", @@ -1149,6 +1152,7 @@ "Contextualbar_resizable_description": "Adjust the size of the contextual bar by clicking and dragging the edge, giving you instant customization and flexibility.", "Free_Edition": "Free edition", "Composer_not_available_phone_calls": "Messages are not available on phone calls", + "Composer_readonly_airgapped": "<0>Workspace in read-only mode. Admins can restore full functionality by connecting it to internet or upgrading to a premium plan.", "Condensed": "Condensed", "Condition": "Condition", "Commit_details": "Commit Details", diff --git a/packages/mock-providers/src/MockedAppRootBuilder.tsx b/packages/mock-providers/src/MockedAppRootBuilder.tsx index 36fe123c244a..69ca9cde99e1 100644 --- a/packages/mock-providers/src/MockedAppRootBuilder.tsx +++ b/packages/mock-providers/src/MockedAppRootBuilder.tsx @@ -239,7 +239,7 @@ export class MockedAppRootBuilder { return this; } - withJohnDoe(): this { + withJohnDoe(overrides: Partial = {}): this { this.user.userId = 'john.doe'; this.user.user = { @@ -251,6 +251,7 @@ export class MockedAppRootBuilder { _updatedAt: new Date(), roles: ['admin'], type: 'user', + ...overrides, }; return this; diff --git a/packages/model-typings/src/models/IStatisticsModel.ts b/packages/model-typings/src/models/IStatisticsModel.ts index fe4534eaee0f..4926c59b7135 100644 --- a/packages/model-typings/src/models/IStatisticsModel.ts +++ b/packages/model-typings/src/models/IStatisticsModel.ts @@ -5,4 +5,5 @@ import type { IBaseModel } from './IBaseModel'; export interface IStatisticsModel extends IBaseModel { findLast(): Promise; findMonthlyPeakConnections(): Promise | null>; + findLastStatsToken(): Promise; } diff --git a/packages/ui-composer/src/MessageFooterCallout/MessageFooterCallout.tsx b/packages/ui-composer/src/MessageFooterCallout/MessageFooterCallout.tsx index 5835ad683186..9c946f9fadd1 100644 --- a/packages/ui-composer/src/MessageFooterCallout/MessageFooterCallout.tsx +++ b/packages/ui-composer/src/MessageFooterCallout/MessageFooterCallout.tsx @@ -27,6 +27,7 @@ const MessageFooterCallout = forwardRef< alignItems='center' minHeight='x48' justifyContent='center' + color='default' {...props} /> ); From f4365b7dd45ab3f3e42de703ff76584b069f5a66 Mon Sep 17 00:00:00 2001 From: Ricardo Garim Date: Fri, 18 Oct 2024 21:54:33 -0300 Subject: [PATCH 24/25] chore!: remove query field on custom emojis listing (#33650) --- .changeset/perfect-wolves-impress.md | 6 +++ .../app/api/server/helpers/parseJsonQuery.ts | 1 + apps/meteor/app/api/server/v1/emoji-custom.ts | 42 +++++++++++++++---- .../customEmoji/EditCustomEmojiWithData.tsx | 2 +- .../tests/end-to-end/api/emoji-custom.ts | 4 +- packages/rest-typings/src/v1/emojiCustom.ts | 13 +++--- 6 files changed, 51 insertions(+), 17 deletions(-) create mode 100644 .changeset/perfect-wolves-impress.md diff --git a/.changeset/perfect-wolves-impress.md b/.changeset/perfect-wolves-impress.md new file mode 100644 index 000000000000..becaf1d706d2 --- /dev/null +++ b/.changeset/perfect-wolves-impress.md @@ -0,0 +1,6 @@ +--- +'@rocket.chat/rest-typings': major +'@rocket.chat/meteor': major +--- + +Changes custom emoji listing endpoint by moving query params from the 'query' attribute to standard query parameters. diff --git a/apps/meteor/app/api/server/helpers/parseJsonQuery.ts b/apps/meteor/app/api/server/helpers/parseJsonQuery.ts index bbd58e118556..35fe6155a396 100644 --- a/apps/meteor/app/api/server/helpers/parseJsonQuery.ts +++ b/apps/meteor/app/api/server/helpers/parseJsonQuery.ts @@ -62,6 +62,7 @@ export async function parseJsonQuery(api: PartialThis): Promise<{ '/api/v1/custom-sounds.list', '/api/v1/channels.list', '/api/v1/channels.online', + '/api/v1/emoji-custom.list', ].includes(route); const isUnsafeQueryParamsAllowed = process.env.ALLOW_UNSAFE_QUERY_AND_FIELDS_API_PARAMS?.toUpperCase() === 'TRUE'; diff --git a/apps/meteor/app/api/server/v1/emoji-custom.ts b/apps/meteor/app/api/server/v1/emoji-custom.ts index 9cbf202896e1..261743e6c0a9 100644 --- a/apps/meteor/app/api/server/v1/emoji-custom.ts +++ b/apps/meteor/app/api/server/v1/emoji-custom.ts @@ -1,5 +1,6 @@ import { Media } from '@rocket.chat/core-services'; import { EmojiCustom } from '@rocket.chat/models'; +import { isEmojiCustomList } from '@rocket.chat/rest-typings'; import { Meteor } from 'meteor/meteor'; import { SystemLogger } from '../../../../server/lib/logger/system'; @@ -11,22 +12,41 @@ import { getPaginationItems } from '../helpers/getPaginationItems'; import { findEmojisCustom } from '../lib/emoji-custom'; import { getUploadFormData } from '../lib/getUploadFormData'; +function validateDateParam(paramName: string, paramValue: string | undefined): Date | undefined { + if (!paramValue) { + return undefined; + } + + const date = new Date(paramValue); + if (isNaN(date.getTime())) { + throw new Meteor.Error('error-roomId-param-invalid', `The "${paramName}" query parameter must be a valid date.`); + } + + return date; +} + API.v1.addRoute( 'emoji-custom.list', - { authRequired: true }, + { authRequired: true, validateParams: isEmojiCustomList }, { async get() { const { query } = await this.parseJsonQuery(); - const { updatedSince } = this.queryParams; - if (updatedSince) { - const updatedSinceDate = new Date(updatedSince); - if (isNaN(Date.parse(updatedSince))) { - throw new Meteor.Error('error-roomId-param-invalid', 'The "updatedSince" query parameter must be a valid date.'); - } + const { updatedSince, _updatedAt, _id } = this.queryParams; + + const updatedSinceDate = validateDateParam('updatedSince', updatedSince); + const _updatedAtDate = validateDateParam('_updatedAt', _updatedAt); + + if (updatedSinceDate) { const [update, remove] = await Promise.all([ - EmojiCustom.find({ ...query, _updatedAt: { $gt: updatedSinceDate } }).toArray(), + EmojiCustom.find({ + ...query, + ...(_id ? { _id } : {}), + ...(_updatedAtDate ? { _updatedAt: { $gt: _updatedAtDate } } : {}), + _updatedAt: { $gt: updatedSinceDate }, + }).toArray(), EmojiCustom.trashFindDeletedAfter(updatedSinceDate).toArray(), ]); + return API.v1.success({ emojis: { update, @@ -37,7 +57,11 @@ API.v1.addRoute( return API.v1.success({ emojis: { - update: await EmojiCustom.find(query).toArray(), + update: await EmojiCustom.find({ + ...query, + ...(_id ? { _id } : {}), + ...(_updatedAtDate ? { _updatedAt: { $gt: _updatedAtDate } } : {}), + }).toArray(), remove: [], }, }); diff --git a/apps/meteor/client/views/admin/customEmoji/EditCustomEmojiWithData.tsx b/apps/meteor/client/views/admin/customEmoji/EditCustomEmojiWithData.tsx index 2ee8a79f5568..fff37214b530 100644 --- a/apps/meteor/client/views/admin/customEmoji/EditCustomEmojiWithData.tsx +++ b/apps/meteor/client/views/admin/customEmoji/EditCustomEmojiWithData.tsx @@ -14,7 +14,7 @@ type EditCustomEmojiWithDataProps = { const EditCustomEmojiWithData = ({ _id, onChange, close, ...props }: EditCustomEmojiWithDataProps) => { const t = useTranslation(); - const query = useMemo(() => ({ query: JSON.stringify({ _id }) }), [_id]); + const query = useMemo(() => ({ _id }), [_id]); const getEmojis = useEndpoint('GET', '/v1/emoji-custom.list'); diff --git a/apps/meteor/tests/end-to-end/api/emoji-custom.ts b/apps/meteor/tests/end-to-end/api/emoji-custom.ts index 31042363836e..c87a83783e0e 100644 --- a/apps/meteor/tests/end-to-end/api/emoji-custom.ts +++ b/apps/meteor/tests/end-to-end/api/emoji-custom.ts @@ -214,7 +214,7 @@ describe('[EmojiCustom]', () => { it('should return emojis when use "query" query parameter', (done) => { void request .get(api('emoji-custom.list')) - .query({ query: `{ "_updatedAt": { "$gt": { "$date": "${new Date().toISOString()}" } } }` }) + .query({ _updatedAt: new Date().toISOString() }) .set(credentials) .expect(200) .expect((res) => { @@ -242,7 +242,7 @@ describe('[EmojiCustom]', () => { it('should return emojis when use both, "updateSince" and "query" query parameter', (done) => { void request .get(api('emoji-custom.list')) - .query({ query: `{"_updatedAt": {"$gt": { "$date": "${new Date().toISOString()}" } }}`, updatedSince: new Date().toISOString() }) + .query({ _updatedAt: new Date().toISOString(), updatedSince: new Date().toISOString() }) .set(credentials) .expect(200) .expect((res) => { diff --git a/packages/rest-typings/src/v1/emojiCustom.ts b/packages/rest-typings/src/v1/emojiCustom.ts index e0cd87906c36..1ffb41f671a8 100644 --- a/packages/rest-typings/src/v1/emojiCustom.ts +++ b/packages/rest-typings/src/v1/emojiCustom.ts @@ -25,10 +25,7 @@ const emojiCustomDeletePropsSchema = { export const isEmojiCustomDelete = ajv.compile(emojiCustomDeletePropsSchema); -type emojiCustomList = { - query: string; - updatedSince?: string; -}; +type emojiCustomList = { query?: string; updatedSince?: string; _updatedAt?: string; _id?: string }; const emojiCustomListSchema = { type: 'object', @@ -40,8 +37,14 @@ const emojiCustomListSchema = { type: 'string', nullable: true, }, + _updatedAt: { + type: 'string', + }, + _id: { + type: 'string', + }, }, - required: ['query'], + required: [], additionalProperties: false, }; From 429d00024c87ae44a14a1bab8ebaf8febfd1faec Mon Sep 17 00:00:00 2001 From: Rodrigo Nascimento Date: Fri, 18 Oct 2024 22:03:47 -0300 Subject: [PATCH 25/25] feat: Trace api calls (#32779) Co-authored-by: Diego Sampaio Co-authored-by: Ricardo Garim Co-authored-by: Guilherme Gazzo --- .gitignore | 1 + apps/meteor/app/api/server/api.ts | 26 +- apps/meteor/app/lib/server/lib/debug.js | 18 +- .../app/metrics/server/lib/collectMetrics.ts | 16 +- .../server/NotificationQueue.ts | 26 +- .../server/functions/sendUsageReport.ts | 43 +- apps/meteor/package.json | 4 + .../rocketchat-mongo-config/server/index.js | 3 + apps/meteor/server/database/utils.ts | 3 + apps/meteor/server/main.ts | 1 + apps/meteor/server/models/raw/BaseRaw.ts | 3 + apps/meteor/server/tracing.ts | 3 + development/agent.yml | 16 + development/collector.config.yml | 24 + development/docker-compose-monitoring.yml | 79 +++ development/grafana-datasources.yml | 30 + development/prometheus.yml | 11 + development/tempo.yml | 60 ++ ee/apps/account-service/Dockerfile | 3 + ee/apps/account-service/package.json | 1 + ee/apps/account-service/src/service.ts | 3 + ee/apps/authorization-service/Dockerfile | 3 + ee/apps/authorization-service/package.json | 1 + ee/apps/authorization-service/src/service.ts | 3 + ee/apps/ddp-streamer/Dockerfile | 3 + ee/apps/ddp-streamer/package.json | 1 + ee/apps/ddp-streamer/src/service.ts | 3 + ee/apps/omnichannel-transcript/Dockerfile | 3 + ee/apps/omnichannel-transcript/package.json | 1 + ee/apps/omnichannel-transcript/src/service.ts | 3 + ee/apps/presence-service/Dockerfile | 3 + ee/apps/presence-service/package.json | 1 + ee/apps/presence-service/src/service.ts | 3 + ee/apps/queue-worker/Dockerfile | 3 + ee/apps/queue-worker/package.json | 1 + ee/apps/queue-worker/src/service.ts | 3 + ee/apps/stream-hub-service/Dockerfile | 3 + ee/apps/stream-hub-service/package.json | 1 + ee/apps/stream-hub-service/src/service.ts | 3 + .../network-broker/src/NetworkBroker.ts | 39 +- ee/packages/network-broker/src/index.ts | 22 - packages/core-services/package.json | 1 + packages/core-services/src/index.ts | 1 + .../src/lib/asyncMethodCallContext.ts | 44 ++ packages/core-services/src/lib/mongo.ts | 8 +- packages/instance-status/package.json | 3 +- packages/instance-status/src/index.ts | 5 +- packages/tracing/.eslintrc.json | 4 + packages/tracing/package.json | 32 + packages/tracing/src/index.ts | 96 +++ packages/tracing/src/traceDatabaseCalls.ts | 65 ++ packages/tracing/tsconfig.json | 9 + tsconfig.base.server.json | 5 +- yarn.lock | 581 +++++++++++++++++- 54 files changed, 1243 insertions(+), 88 deletions(-) create mode 100644 apps/meteor/server/tracing.ts create mode 100644 development/agent.yml create mode 100644 development/collector.config.yml create mode 100644 development/docker-compose-monitoring.yml create mode 100644 development/grafana-datasources.yml create mode 100644 development/prometheus.yml create mode 100644 development/tempo.yml create mode 100644 packages/core-services/src/lib/asyncMethodCallContext.ts create mode 100644 packages/tracing/.eslintrc.json create mode 100644 packages/tracing/package.json create mode 100644 packages/tracing/src/index.ts create mode 100644 packages/tracing/src/traceDatabaseCalls.ts create mode 100644 packages/tracing/tsconfig.json diff --git a/.gitignore b/.gitignore index 8ca2d018f92e..8741b33f36c3 100644 --- a/.gitignore +++ b/.gitignore @@ -58,3 +58,4 @@ data/ registration.yaml storybook-static +development/tempo-data/ diff --git a/apps/meteor/app/api/server/api.ts b/apps/meteor/app/api/server/api.ts index 76f9cc44de7a..7bb7f5af28ba 100644 --- a/apps/meteor/app/api/server/api.ts +++ b/apps/meteor/app/api/server/api.ts @@ -3,6 +3,7 @@ import { Logger } from '@rocket.chat/logger'; import { Users } from '@rocket.chat/models'; import { Random } from '@rocket.chat/random'; import type { JoinPathPattern, Method } from '@rocket.chat/rest-typings'; +import { tracerSpan } from '@rocket.chat/tracing'; import { Accounts } from 'meteor/accounts-base'; import { DDP } from 'meteor/ddp'; import { DDPCommon } from 'meteor/ddp-common'; @@ -645,8 +646,29 @@ export class APIClass extends Restivus { this.queryFields = options.queryFields; this.parseJsonQuery = api.parseJsonQuery.bind(this as PartialThis); - result = - (await DDP._CurrentInvocation.withValue(invocation as any, async () => originalAction.apply(this))) || API.v1.success(); + result = await tracerSpan( + `${this.request.method} ${this.request.url}`, + { + attributes: { + url: this.request.url, + route: this.request.route, + method: this.request.method, + userId: this.userId, + }, + }, + async (span) => { + if (span) { + this.response.setHeader('X-Trace-Id', span.spanContext().traceId); + } + + const result = + (await DDP._CurrentInvocation.withValue(invocation as any, async () => originalAction.apply(this))) || API.v1.success(); + + span?.setAttribute('status', result.statusCode); + + return result; + }, + ); log.http({ status: result.statusCode, diff --git a/apps/meteor/app/lib/server/lib/debug.js b/apps/meteor/app/lib/server/lib/debug.js index cbf38528579f..71daf1a4fbcf 100644 --- a/apps/meteor/app/lib/server/lib/debug.js +++ b/apps/meteor/app/lib/server/lib/debug.js @@ -1,5 +1,6 @@ import { InstanceStatus } from '@rocket.chat/instance-status'; import { Logger } from '@rocket.chat/logger'; +import { tracerActiveSpan } from '@rocket.chat/tracing'; import { Meteor } from 'meteor/meteor'; import { WebApp } from 'meteor/webapp'; import _ from 'underscore'; @@ -72,9 +73,20 @@ const wrapMethods = function (name, originalHandler, methodsMap) { ...getMethodArgs(name, originalArgs), }); - const result = originalHandler.apply(this, originalArgs); - end(); - return result; + return tracerActiveSpan( + `Method ${name}`, + { + attributes: { + method: name, + userId: this.userId, + }, + }, + async () => { + const result = await originalHandler.apply(this, originalArgs); + end(); + return result; + }, + ); }; }; diff --git a/apps/meteor/app/metrics/server/lib/collectMetrics.ts b/apps/meteor/app/metrics/server/lib/collectMetrics.ts index 978b3d59ec98..8f2b256f6759 100644 --- a/apps/meteor/app/metrics/server/lib/collectMetrics.ts +++ b/apps/meteor/app/metrics/server/lib/collectMetrics.ts @@ -1,6 +1,7 @@ import http from 'http'; import { Statistics } from '@rocket.chat/models'; +import { tracerSpan } from '@rocket.chat/tracing'; import connect from 'connect'; import { Facts } from 'meteor/facts-base'; import { Meteor } from 'meteor/meteor'; @@ -169,7 +170,20 @@ const updatePrometheusConfig = async (): Promise => { host: process.env.BIND_IP || '0.0.0.0', }); - timer = setInterval(setPrometheusData, 5000); + timer = setInterval(async () => { + void tracerSpan( + 'setPrometheusData', + { + attributes: { + port: is.port, + host: process.env.BIND_IP || '0.0.0.0', + }, + }, + () => { + void setPrometheusData(); + }, + ); + }, 5000); } clearInterval(resetTimer); diff --git a/apps/meteor/app/notification-queue/server/NotificationQueue.ts b/apps/meteor/app/notification-queue/server/NotificationQueue.ts index f58eaec3f576..78a9e931fcb5 100644 --- a/apps/meteor/app/notification-queue/server/NotificationQueue.ts +++ b/apps/meteor/app/notification-queue/server/NotificationQueue.ts @@ -1,5 +1,6 @@ import type { INotification, INotificationItemPush, INotificationItemEmail, NotificationItem, IUser } from '@rocket.chat/core-typings'; import { NotificationQueue, Users } from '@rocket.chat/models'; +import { tracerSpan } from '@rocket.chat/tracing'; import { Meteor } from 'meteor/meteor'; import { SystemLogger } from '../../../server/lib/logger/system'; @@ -43,7 +44,19 @@ class NotificationClass { setTimeout(async () => { try { - await this.worker(); + const continueLater = await tracerSpan( + 'NotificationWorker', + { + attributes: { + workerTime: new Date().toISOString(), + }, + }, + () => this.worker(), + ); + + if (continueLater) { + this.executeWorkerLater(); + } } catch (err) { SystemLogger.error({ msg: 'Error sending notification', err }); this.executeWorkerLater(); @@ -51,17 +64,17 @@ class NotificationClass { }, this.cyclePause); } - async worker(counter = 0): Promise { + async worker(counter = 0): Promise { const notification = await this.getNextNotification(); if (!notification) { - return this.executeWorkerLater(); + return true; } // Once we start notifying the user we anticipate all the schedules const flush = await NotificationQueue.clearScheduleByUserId(notification.uid); - // start worker again it queue flushed + // start worker again if queue flushed if (flush.modifiedCount) { await NotificationQueue.unsetSendingById(notification._id); return this.worker(counter); @@ -86,9 +99,10 @@ class NotificationClass { } if (counter >= this.maxBatchSize) { - return this.executeWorkerLater(); + return true; } - await this.worker(counter++); + + return this.worker(counter++); } getNextNotification(): Promise { diff --git a/apps/meteor/app/statistics/server/functions/sendUsageReport.ts b/apps/meteor/app/statistics/server/functions/sendUsageReport.ts index dc684fe5fa6a..a4b1e6182c96 100644 --- a/apps/meteor/app/statistics/server/functions/sendUsageReport.ts +++ b/apps/meteor/app/statistics/server/functions/sendUsageReport.ts @@ -1,35 +1,38 @@ import type { Logger } from '@rocket.chat/logger'; import { Statistics } from '@rocket.chat/models'; import { serverFetch as fetch } from '@rocket.chat/server-fetch'; +import { tracerSpan } from '@rocket.chat/tracing'; import { Meteor } from 'meteor/meteor'; import { statistics } from '..'; import { getWorkspaceAccessToken } from '../../../cloud/server'; export async function sendUsageReport(logger: Logger): Promise { - const cronStatistics = await statistics.save(); + return tracerSpan('generateStatistics', {}, async () => { + const cronStatistics = await statistics.save(); - try { - const token = await getWorkspaceAccessToken(); - const headers = { ...(token && { Authorization: `Bearer ${token}` }) }; + try { + const token = await getWorkspaceAccessToken(); + const headers = { ...(token && { Authorization: `Bearer ${token}` }) }; - const response = await fetch('https://collector.rocket.chat/', { - method: 'POST', - body: { - ...cronStatistics, - host: Meteor.absoluteUrl(), - }, - headers, - }); + const response = await fetch('https://collector.rocket.chat/', { + method: 'POST', + body: { + ...cronStatistics, + host: Meteor.absoluteUrl(), + }, + headers, + }); - const { statsToken } = await response.json(); + const { statsToken } = await response.json(); - if (statsToken != null) { - await Statistics.updateOne({ _id: cronStatistics._id }, { $set: { statsToken } }); - return statsToken; + if (statsToken != null) { + await Statistics.updateOne({ _id: cronStatistics._id }, { $set: { statsToken } }); + return statsToken; + } + } catch (error) { + /* error*/ + logger.warn('Failed to send usage report'); } - } catch (error) { - /* error*/ - logger.warn('Failed to send usage report'); - } + }); } diff --git a/apps/meteor/package.json b/apps/meteor/package.json index fafecd90266b..107688676fe9 100644 --- a/apps/meteor/package.json +++ b/apps/meteor/package.json @@ -225,6 +225,9 @@ "@nivo/heatmap": "0.84.0", "@nivo/line": "0.84.0", "@nivo/pie": "0.84.0", + "@opentelemetry/api": "^1.9.0", + "@opentelemetry/exporter-trace-otlp-grpc": "^0.53.0", + "@opentelemetry/sdk-node": "^0.53.0", "@react-aria/color": "^3.0.0-beta.15", "@react-aria/toolbar": "^3.0.0-beta.1", "@react-pdf/renderer": "^3.4.5", @@ -278,6 +281,7 @@ "@rocket.chat/sha256": "workspace:^", "@rocket.chat/string-helpers": "~0.31.25", "@rocket.chat/tools": "workspace:^", + "@rocket.chat/tracing": "workspace:^", "@rocket.chat/ui-avatar": "workspace:^", "@rocket.chat/ui-client": "workspace:^", "@rocket.chat/ui-composer": "workspace:^", diff --git a/apps/meteor/packages/rocketchat-mongo-config/server/index.js b/apps/meteor/packages/rocketchat-mongo-config/server/index.js index 684620d09054..0cf40df6bbb4 100644 --- a/apps/meteor/packages/rocketchat-mongo-config/server/index.js +++ b/apps/meteor/packages/rocketchat-mongo-config/server/index.js @@ -20,6 +20,9 @@ const mongoConnectionOptions = { // add retryWrites=false if not present in MONGO_URL ...(!process.env.MONGO_URL.includes('retryWrites') && { retryWrites: false }), // ignoreUndefined: false, // TODO evaluate adding this config + + // TODO ideally we should call isTracingEnabled(), but since this is a Meteor package we can't :/ + monitorCommands: ['yes', 'true'].includes(String(process.env.TRACING_ENABLED).toLowerCase()), }; const mongoOptionStr = process.env.MONGO_OPTIONS; diff --git a/apps/meteor/server/database/utils.ts b/apps/meteor/server/database/utils.ts index ec3864586924..e07df1e9e737 100644 --- a/apps/meteor/server/database/utils.ts +++ b/apps/meteor/server/database/utils.ts @@ -1,3 +1,6 @@ +import { initDatabaseTracing } from '@rocket.chat/tracing'; import { MongoInternals } from 'meteor/mongo'; export const { db, client } = MongoInternals.defaultRemoteCollectionDriver().mongo; + +initDatabaseTracing(client); diff --git a/apps/meteor/server/main.ts b/apps/meteor/server/main.ts index 294cdcd4feab..b5f34876c0bd 100644 --- a/apps/meteor/server/main.ts +++ b/apps/meteor/server/main.ts @@ -1,3 +1,4 @@ +import './tracing'; import './models/startup'; /** * ./settings uses top level await, in theory the settings creation diff --git a/apps/meteor/server/models/raw/BaseRaw.ts b/apps/meteor/server/models/raw/BaseRaw.ts index 8e1bd302b50d..0a7fc56449b0 100644 --- a/apps/meteor/server/models/raw/BaseRaw.ts +++ b/apps/meteor/server/models/raw/BaseRaw.ts @@ -1,3 +1,4 @@ +import { traceInstanceMethods } from '@rocket.chat/core-services'; import type { RocketChatRecordDeleted } from '@rocket.chat/core-typings'; import type { IBaseModel, DefaultFields, ResultFields, FindPaginated, InsertionModel } from '@rocket.chat/model-typings'; import type { Updater } from '@rocket.chat/models'; @@ -76,6 +77,8 @@ export abstract class BaseRaw< void this.createIndexes(); this.preventSetUpdatedAt = options?.preventSetUpdatedAt ?? false; + + return traceInstanceMethods(this); } private pendingIndexes: Promise | undefined; diff --git a/apps/meteor/server/tracing.ts b/apps/meteor/server/tracing.ts new file mode 100644 index 000000000000..e08bfebb20ac --- /dev/null +++ b/apps/meteor/server/tracing.ts @@ -0,0 +1,3 @@ +import { startTracing } from '@rocket.chat/tracing'; + +startTracing({ service: 'core' }); diff --git a/development/agent.yml b/development/agent.yml new file mode 100644 index 000000000000..e40e39401d64 --- /dev/null +++ b/development/agent.yml @@ -0,0 +1,16 @@ +server: + log_level: debug + +traces: + configs: + - name: default + receivers: + otlp: + protocols: + grpc: + remote_write: + - endpoint: tempo:4317 + insecure: true + batch: + timeout: 5s + send_batch_size: 100 diff --git a/development/collector.config.yml b/development/collector.config.yml new file mode 100644 index 000000000000..a284c46de807 --- /dev/null +++ b/development/collector.config.yml @@ -0,0 +1,24 @@ +receivers: + otlp: + protocols: + grpc: + http: + +processors: + batch: + timeout: 100ms + +exporters: + logging: + loglevel: debug + otlp/1: + endpoint: tempo:4317 + tls: + insecure: true + +service: + pipelines: + traces: + receivers: [otlp] + processors: [batch] + exporters: [otlp/1] diff --git a/development/docker-compose-monitoring.yml b/development/docker-compose-monitoring.yml new file mode 100644 index 000000000000..90c75e67db22 --- /dev/null +++ b/development/docker-compose-monitoring.yml @@ -0,0 +1,79 @@ +services: + # Tempo runs as user 10001, and docker compose creates the volume as root. + # As such, we need to chown the volume in order for Tempo to start correctly. + init: + image: &tempoImage grafana/tempo:latest + user: root + entrypoint: + - "chown" + - "10001:10001" + - "/var/tempo" + volumes: + - ./tempo-data:/var/tempo + + tempo: + image: *tempoImage + command: [ "-config.file=/etc/tempo.yaml" ] + volumes: + - ./tempo.yml:/etc/tempo.yaml + - ./tempo-data:/var/tempo + ports: + - "14268" # jaeger ingest + - "3200" # tempo + - "4317" # otlp grpc + - "4318" # otlp http + - "9411" # zipkin2024-04-23T16:16:57+0000 + depends_on: + - init + + # # Generate fake traces... + # k6-tracing: + # image: ghcr.io/grafana/xk6-client-tracing:v0.0.5 + # environment: + # - ENDPOINT=agent:4317 + # restart: always + # depends_on: + # - tempo + + otel-collector: + image: otel/opentelemetry-collector-contrib:0.100.0 + command: + - "--config" + - "/otel-local-config.yaml" + volumes: + - ./collector.config.yml:/otel-local-config.yaml + ports: + - "4317:4317" + + # And put them in a Grafana Agent pipeline... + agent: + image: grafana/agent:v0.27.1 + volumes: + - ./agent.yml:/etc/agent.yaml + entrypoint: + - /bin/agent + - -config.file=/etc/agent.yaml + + prometheus: + image: prom/prometheus:latest + command: + - --config.file=/etc/prometheus.yaml + - --web.enable-remote-write-receiver + - --enable-feature=exemplar-storage + - --enable-feature=native-histograms + volumes: + - ./prometheus.yml:/etc/prometheus.yaml + ports: + - "9090:9090" + + grafana: + image: grafana/grafana:11.0.0 + volumes: + - ./grafana-datasources.yml:/etc/grafana/provisioning/datasources/datasources.yaml + environment: + - GF_AUTH_ANONYMOUS_ENABLED=true + - GF_AUTH_ANONYMOUS_ORG_ROLE=Admin + - GF_AUTH_DISABLE_LOGIN_FORM=true + - GF_FEATURE_TOGGLES_ENABLE=traceqlEditor + ports: + - "4001:3000" diff --git a/development/grafana-datasources.yml b/development/grafana-datasources.yml new file mode 100644 index 000000000000..4a3bc2c4e742 --- /dev/null +++ b/development/grafana-datasources.yml @@ -0,0 +1,30 @@ +apiVersion: 1 + +datasources: +- name: Prometheus + type: prometheus + uid: prometheus + access: proxy + orgId: 1 + url: http://prometheus:9090 + basicAuth: false + isDefault: false + version: 1 + editable: false + jsonData: + httpMethod: GET +- name: Tempo + type: tempo + access: proxy + orgId: 1 + url: http://tempo:3200 + basicAuth: false + isDefault: true + version: 1 + editable: false + apiVersion: 1 + uid: tempo + jsonData: + httpMethod: GET + serviceMap: + datasourceUid: prometheus diff --git a/development/prometheus.yml b/development/prometheus.yml new file mode 100644 index 000000000000..eda5d0261c8f --- /dev/null +++ b/development/prometheus.yml @@ -0,0 +1,11 @@ +global: + scrape_interval: 15s + evaluation_interval: 15s + +scrape_configs: + - job_name: 'prometheus' + static_configs: + - targets: [ 'localhost:9090' ] + - job_name: 'tempo' + static_configs: + - targets: [ 'tempo:3200' ] diff --git a/development/tempo.yml b/development/tempo.yml new file mode 100644 index 000000000000..b1ab5ce7607b --- /dev/null +++ b/development/tempo.yml @@ -0,0 +1,60 @@ +stream_over_http_enabled: true +server: + http_listen_port: 3200 + log_level: info + +query_frontend: + search: + duration_slo: 5s + throughput_bytes_slo: 1.073741824e+09 + trace_by_id: + duration_slo: 5s + +distributor: + receivers: # this configuration will listen on all ports and protocols that tempo is capable of. + jaeger: # the receives all come from the OpenTelemetry collector. more configuration information can + protocols: # be found there: https://github.com/open-telemetry/opentelemetry-collector/tree/main/receiver + thrift_http: # + grpc: # for a production deployment you should only enable the receivers you need! + thrift_binary: + thrift_compact: + zipkin: + otlp: + protocols: + http: + grpc: + opencensus: + +ingester: + max_block_duration: 5m # cut the headblock when this much time passes. this is being set for demo purposes and should probably be left alone normally + +compactor: + compaction: + block_retention: 1h # overall Tempo trace retention. set for demo purposes + +metrics_generator: + registry: + external_labels: + source: tempo + cluster: docker-compose + storage: + path: /var/tempo/generator/wal + remote_write: + - url: http://prometheus:9090/api/v1/write + send_exemplars: true + traces_storage: + path: /var/tempo/generator/traces + +storage: + trace: + backend: local # backend configuration to use + wal: + path: /var/tempo/wal # where to store the wal locally + local: + path: /var/tempo/blocks + +overrides: + defaults: + metrics_generator: + processors: [service-graphs, span-metrics, local-blocks] # enables metrics generator + generate_native_histograms: both diff --git a/ee/apps/account-service/Dockerfile b/ee/apps/account-service/Dockerfile index dec9433667f4..7c7f4f5ebb3d 100644 --- a/ee/apps/account-service/Dockerfile +++ b/ee/apps/account-service/Dockerfile @@ -47,6 +47,9 @@ COPY ./ee/packages/network-broker/dist ee/packages/network-broker/dist COPY ./ee/packages/license/package.json packages/license/package.json COPY ./ee/packages/license/dist packages/license/dist +COPY ./packages/tracing/package.json packages/tracing/package.json +COPY ./packages/tracing/dist packages/tracing/dist + COPY ./packages/ui-kit/package.json packages/ui-kit/package.json COPY ./packages/ui-kit/dist packages/ui-kit/dist diff --git a/ee/apps/account-service/package.json b/ee/apps/account-service/package.json index 78326b6065f7..c88c6292957f 100644 --- a/ee/apps/account-service/package.json +++ b/ee/apps/account-service/package.json @@ -24,6 +24,7 @@ "@rocket.chat/rest-typings": "workspace:^", "@rocket.chat/string-helpers": "~0.31.25", "@rocket.chat/tools": "workspace:^", + "@rocket.chat/tracing": "workspace:^", "@types/node": "^14.18.63", "bcrypt": "^5.0.1", "ejson": "^2.2.3", diff --git a/ee/apps/account-service/src/service.ts b/ee/apps/account-service/src/service.ts index c2f64e37bde3..b5279998a52b 100755 --- a/ee/apps/account-service/src/service.ts +++ b/ee/apps/account-service/src/service.ts @@ -1,9 +1,12 @@ import { api, getConnection, getTrashCollection } from '@rocket.chat/core-services'; import { broker } from '@rocket.chat/network-broker'; +import { startTracing } from '@rocket.chat/tracing'; import polka from 'polka'; import { registerServiceModels } from '../../../../apps/meteor/ee/server/lib/registerServiceModels'; +startTracing({ service: 'account-service' }); + const PORT = process.env.PORT || 3033; (async () => { diff --git a/ee/apps/authorization-service/Dockerfile b/ee/apps/authorization-service/Dockerfile index 612dbe5d73c9..792afc778088 100644 --- a/ee/apps/authorization-service/Dockerfile +++ b/ee/apps/authorization-service/Dockerfile @@ -47,6 +47,9 @@ COPY ./ee/packages/network-broker/dist ee/packages/network-broker/dist COPY ./ee/packages/license/package.json packages/license/package.json COPY ./ee/packages/license/dist packages/license/dist +COPY ./packages/tracing/package.json packages/tracing/package.json +COPY ./packages/tracing/dist packages/tracing/dist + COPY ./packages/ui-kit/package.json packages/ui-kit/package.json COPY ./packages/ui-kit/dist packages/ui-kit/dist diff --git a/ee/apps/authorization-service/package.json b/ee/apps/authorization-service/package.json index 3ce8f9734916..1e1aae2fa34e 100644 --- a/ee/apps/authorization-service/package.json +++ b/ee/apps/authorization-service/package.json @@ -23,6 +23,7 @@ "@rocket.chat/network-broker": "workspace:^", "@rocket.chat/rest-typings": "workspace:^", "@rocket.chat/string-helpers": "~0.31.25", + "@rocket.chat/tracing": "workspace:^", "@types/node": "^14.18.63", "ejson": "^2.2.3", "event-loop-stats": "^1.4.1", diff --git a/ee/apps/authorization-service/src/service.ts b/ee/apps/authorization-service/src/service.ts index 1698ef7a115c..699de7aeb518 100755 --- a/ee/apps/authorization-service/src/service.ts +++ b/ee/apps/authorization-service/src/service.ts @@ -1,11 +1,14 @@ import { api, getConnection, getTrashCollection } from '@rocket.chat/core-services'; import { broker } from '@rocket.chat/network-broker'; +import { startTracing } from '@rocket.chat/tracing'; import polka from 'polka'; import { registerServiceModels } from '../../../../apps/meteor/ee/server/lib/registerServiceModels'; const PORT = process.env.PORT || 3034; +startTracing({ service: 'authorization-service' }); + (async () => { const db = await getConnection(); diff --git a/ee/apps/ddp-streamer/Dockerfile b/ee/apps/ddp-streamer/Dockerfile index 40dcfa5ccb3d..e2106da66a5b 100644 --- a/ee/apps/ddp-streamer/Dockerfile +++ b/ee/apps/ddp-streamer/Dockerfile @@ -53,6 +53,9 @@ COPY ./ee/packages/license/dist packages/license/dist COPY ./packages/instance-status/package.json packages/instance-status/package.json COPY ./packages/instance-status/dist packages/instance-status/dist +COPY ./packages/tracing/package.json packages/tracing/package.json +COPY ./packages/tracing/dist packages/tracing/dist + COPY ./packages/ui-kit/package.json packages/ui-kit/package.json COPY ./packages/ui-kit/dist packages/ui-kit/dist diff --git a/ee/apps/ddp-streamer/package.json b/ee/apps/ddp-streamer/package.json index edfbb38e5cbf..e2ee7251513d 100644 --- a/ee/apps/ddp-streamer/package.json +++ b/ee/apps/ddp-streamer/package.json @@ -25,6 +25,7 @@ "@rocket.chat/network-broker": "workspace:^", "@rocket.chat/rest-typings": "workspace:^", "@rocket.chat/string-helpers": "~0.31.25", + "@rocket.chat/tracing": "workspace:^", "colorette": "^1.4.0", "ejson": "^2.2.3", "event-loop-stats": "^1.4.1", diff --git a/ee/apps/ddp-streamer/src/service.ts b/ee/apps/ddp-streamer/src/service.ts index 58552240cadd..4b43e1baed96 100755 --- a/ee/apps/ddp-streamer/src/service.ts +++ b/ee/apps/ddp-streamer/src/service.ts @@ -1,8 +1,11 @@ import { api, getConnection, getTrashCollection } from '@rocket.chat/core-services'; import { broker } from '@rocket.chat/network-broker'; +import { startTracing } from '@rocket.chat/tracing'; import { registerServiceModels } from '../../../../apps/meteor/ee/server/lib/registerServiceModels'; +startTracing({ service: 'ddp-streamer' }); + (async () => { const db = await getConnection(); diff --git a/ee/apps/omnichannel-transcript/Dockerfile b/ee/apps/omnichannel-transcript/Dockerfile index 7c3e0eaaa070..b2595b06f1fe 100644 --- a/ee/apps/omnichannel-transcript/Dockerfile +++ b/ee/apps/omnichannel-transcript/Dockerfile @@ -56,6 +56,9 @@ COPY ./ee/packages/pdf-worker/dist ee/packages/pdf-worker/dist COPY ./packages/tools/package.json packages/tools/package.json COPY ./packages/tools/dist packages/tools/dist +COPY ./packages/tracing/package.json packages/tracing/package.json +COPY ./packages/tracing/dist packages/tracing/dist + COPY ./packages/ui-kit/package.json packages/ui-kit/package.json COPY ./packages/ui-kit/dist packages/ui-kit/dist diff --git a/ee/apps/omnichannel-transcript/package.json b/ee/apps/omnichannel-transcript/package.json index b88cf06956a4..61c86a741610 100644 --- a/ee/apps/omnichannel-transcript/package.json +++ b/ee/apps/omnichannel-transcript/package.json @@ -26,6 +26,7 @@ "@rocket.chat/omnichannel-services": "workspace:^", "@rocket.chat/pdf-worker": "workspace:^", "@rocket.chat/tools": "workspace:^", + "@rocket.chat/tracing": "workspace:^", "@types/node": "^14.18.63", "ejson": "^2.2.3", "emoji-toolkit": "^7.0.1", diff --git a/ee/apps/omnichannel-transcript/src/service.ts b/ee/apps/omnichannel-transcript/src/service.ts index ad60687d5ba4..e313c0069e8d 100644 --- a/ee/apps/omnichannel-transcript/src/service.ts +++ b/ee/apps/omnichannel-transcript/src/service.ts @@ -1,10 +1,13 @@ import { api, getConnection, getTrashCollection } from '@rocket.chat/core-services'; import { Logger } from '@rocket.chat/logger'; import { broker } from '@rocket.chat/network-broker'; +import { startTracing } from '@rocket.chat/tracing'; import polka from 'polka'; import { registerServiceModels } from '../../../../apps/meteor/ee/server/lib/registerServiceModels'; +startTracing({ service: 'omnichannel-transcript' }); + const PORT = process.env.PORT || 3036; (async () => { diff --git a/ee/apps/presence-service/Dockerfile b/ee/apps/presence-service/Dockerfile index fd1fc0741bcf..ce1977fd3c87 100644 --- a/ee/apps/presence-service/Dockerfile +++ b/ee/apps/presence-service/Dockerfile @@ -50,6 +50,9 @@ COPY ./ee/packages/network-broker/dist ee/packages/network-broker/dist COPY ./ee/packages/license/package.json packages/license/package.json COPY ./ee/packages/license/dist packages/license/dist +COPY ./packages/tracing/package.json packages/tracing/package.json +COPY ./packages/tracing/dist packages/tracing/dist + COPY ./packages/ui-kit/package.json packages/ui-kit/package.json COPY ./packages/ui-kit/dist packages/ui-kit/dist diff --git a/ee/apps/presence-service/package.json b/ee/apps/presence-service/package.json index d4d8883596db..2b3b08de5078 100644 --- a/ee/apps/presence-service/package.json +++ b/ee/apps/presence-service/package.json @@ -23,6 +23,7 @@ "@rocket.chat/network-broker": "workspace:^", "@rocket.chat/presence": "workspace:^", "@rocket.chat/string-helpers": "~0.31.25", + "@rocket.chat/tracing": "workspace:^", "@types/node": "^14.18.63", "ejson": "^2.2.3", "event-loop-stats": "^1.4.1", diff --git a/ee/apps/presence-service/src/service.ts b/ee/apps/presence-service/src/service.ts index 0c51c30dc577..5114c712dae7 100755 --- a/ee/apps/presence-service/src/service.ts +++ b/ee/apps/presence-service/src/service.ts @@ -1,9 +1,12 @@ import { api, getConnection, getTrashCollection } from '@rocket.chat/core-services'; import { broker } from '@rocket.chat/network-broker'; +import { startTracing } from '@rocket.chat/tracing'; import polka from 'polka'; import { registerServiceModels } from '../../../../apps/meteor/ee/server/lib/registerServiceModels'; +startTracing({ service: 'presence-service' }); + const PORT = process.env.PORT || 3031; (async () => { diff --git a/ee/apps/queue-worker/Dockerfile b/ee/apps/queue-worker/Dockerfile index 7c3e0eaaa070..b2595b06f1fe 100644 --- a/ee/apps/queue-worker/Dockerfile +++ b/ee/apps/queue-worker/Dockerfile @@ -56,6 +56,9 @@ COPY ./ee/packages/pdf-worker/dist ee/packages/pdf-worker/dist COPY ./packages/tools/package.json packages/tools/package.json COPY ./packages/tools/dist packages/tools/dist +COPY ./packages/tracing/package.json packages/tracing/package.json +COPY ./packages/tracing/dist packages/tracing/dist + COPY ./packages/ui-kit/package.json packages/ui-kit/package.json COPY ./packages/ui-kit/dist packages/ui-kit/dist diff --git a/ee/apps/queue-worker/package.json b/ee/apps/queue-worker/package.json index f1853bc41218..5c3e91f2b8ac 100644 --- a/ee/apps/queue-worker/package.json +++ b/ee/apps/queue-worker/package.json @@ -23,6 +23,7 @@ "@rocket.chat/models": "workspace:^", "@rocket.chat/network-broker": "workspace:^", "@rocket.chat/omnichannel-services": "workspace:^", + "@rocket.chat/tracing": "workspace:^", "@types/node": "^14.18.63", "ejson": "^2.2.3", "emoji-toolkit": "^7.0.1", diff --git a/ee/apps/queue-worker/src/service.ts b/ee/apps/queue-worker/src/service.ts index c11376d56534..a66d1a579bad 100644 --- a/ee/apps/queue-worker/src/service.ts +++ b/ee/apps/queue-worker/src/service.ts @@ -1,10 +1,13 @@ import { api, getConnection, getTrashCollection } from '@rocket.chat/core-services'; import { Logger } from '@rocket.chat/logger'; import { broker } from '@rocket.chat/network-broker'; +import { startTracing } from '@rocket.chat/tracing'; import polka from 'polka'; import { registerServiceModels } from '../../../../apps/meteor/ee/server/lib/registerServiceModels'; +startTracing({ service: 'queue-worker' }); + const PORT = process.env.PORT || 3038; (async () => { diff --git a/ee/apps/stream-hub-service/Dockerfile b/ee/apps/stream-hub-service/Dockerfile index 612dbe5d73c9..792afc778088 100644 --- a/ee/apps/stream-hub-service/Dockerfile +++ b/ee/apps/stream-hub-service/Dockerfile @@ -47,6 +47,9 @@ COPY ./ee/packages/network-broker/dist ee/packages/network-broker/dist COPY ./ee/packages/license/package.json packages/license/package.json COPY ./ee/packages/license/dist packages/license/dist +COPY ./packages/tracing/package.json packages/tracing/package.json +COPY ./packages/tracing/dist packages/tracing/dist + COPY ./packages/ui-kit/package.json packages/ui-kit/package.json COPY ./packages/ui-kit/dist packages/ui-kit/dist diff --git a/ee/apps/stream-hub-service/package.json b/ee/apps/stream-hub-service/package.json index 382b252a3bf9..601608c6d6d8 100644 --- a/ee/apps/stream-hub-service/package.json +++ b/ee/apps/stream-hub-service/package.json @@ -23,6 +23,7 @@ "@rocket.chat/models": "workspace:^", "@rocket.chat/network-broker": "workspace:^", "@rocket.chat/string-helpers": "~0.31.25", + "@rocket.chat/tracing": "workspace:^", "@types/node": "^14.18.63", "ejson": "^2.2.3", "event-loop-stats": "^1.4.1", diff --git a/ee/apps/stream-hub-service/src/service.ts b/ee/apps/stream-hub-service/src/service.ts index 5e035548dc38..26cea9676a02 100755 --- a/ee/apps/stream-hub-service/src/service.ts +++ b/ee/apps/stream-hub-service/src/service.ts @@ -1,12 +1,15 @@ import { api, getConnection, getTrashCollection } from '@rocket.chat/core-services'; import { Logger } from '@rocket.chat/logger'; import { broker } from '@rocket.chat/network-broker'; +import { startTracing } from '@rocket.chat/tracing'; import polka from 'polka'; import { registerServiceModels } from '../../../../apps/meteor/ee/server/lib/registerServiceModels'; import { DatabaseWatcher } from '../../../../apps/meteor/server/database/DatabaseWatcher'; import { StreamHub } from './StreamHub'; +startTracing({ service: 'stream-hub-service' }); + const PORT = process.env.PORT || 3035; (async () => { diff --git a/ee/packages/network-broker/src/NetworkBroker.ts b/ee/packages/network-broker/src/NetworkBroker.ts index e326357cba0a..64b690c3eff6 100644 --- a/ee/packages/network-broker/src/NetworkBroker.ts +++ b/ee/packages/network-broker/src/NetworkBroker.ts @@ -1,5 +1,6 @@ import { asyncLocalStorage } from '@rocket.chat/core-services'; import type { IBroker, IBrokerNode, IServiceMetrics, IServiceClass, EventSignatures } from '@rocket.chat/core-services'; +import { injectCurrentContext, tracerSpan } from '@rocket.chat/tracing'; import type { ServiceBroker, Context, ServiceSchema } from 'moleculer'; import { EnterpriseCheck } from './EnterpriseCheck'; @@ -52,7 +53,12 @@ export class NetworkBroker implements IBroker { if (!services.find((service) => service.name === method.split('.')[0])) { return new Error('method-not-available'); } - return this.broker.call(method, data); + + return this.broker.call(method, data, { + meta: { + optl: injectCurrentContext(), + }, + }); } async waitAndCall(method: string, data: any): Promise { @@ -72,7 +78,11 @@ export class NetworkBroker implements IBroker { return context.ctx.call(method, data); } - return this.broker.call(method, data); + return this.broker.call(method, data, { + meta: { + optl: injectCurrentContext(), + }, + }); } async destroyService(instance: IServiceClass): Promise { @@ -148,16 +158,23 @@ export class NetworkBroker implements IBroker { continue; } - service.actions[method] = async (ctx: Context<[]>): Promise => { - return asyncLocalStorage.run( - { - id: ctx.id, - nodeID: ctx.nodeID, - requestID: ctx.requestID, - broker: this, - ctx, + service.actions[method] = async (ctx: Context<[], { optl?: unknown }>): Promise => { + return tracerSpan( + `action ${name}:${method}`, + {}, + () => { + return asyncLocalStorage.run( + { + id: ctx.id, + nodeID: ctx.nodeID, + requestID: ctx.requestID, + broker: this, + ctx, + }, + () => serviceInstance[method](...ctx.params), + ); }, - () => serviceInstance[method](...ctx.params), + ctx.meta?.optl, ); }; } diff --git a/ee/packages/network-broker/src/index.ts b/ee/packages/network-broker/src/index.ts index caa12890b514..8dcd99dac07d 100644 --- a/ee/packages/network-broker/src/index.ts +++ b/ee/packages/network-broker/src/index.ts @@ -27,7 +27,6 @@ const { BULKHEAD_MAX_QUEUE_SIZE = '10000', MS_METRICS = 'false', MS_METRICS_PORT = '9458', - TRACING_ENABLED = 'false', SKIP_PROCESS_EVENT_REGISTRATION = 'false', } = process.env; @@ -143,27 +142,6 @@ const network = new ServiceBroker({ maxQueueSize: parseInt(BULKHEAD_MAX_QUEUE_SIZE), }, - tracing: { - enabled: TRACING_ENABLED === 'true', - exporter: { - type: 'Jaeger', - options: { - endpoint: null, - host: 'jaeger', - port: 6832, - sampler: { - // Sampler type. More info: https://www.jaegertracing.io/docs/1.14/sampling/#client-sampling-configuration - type: 'Const', - // Sampler specific options. - options: {}, - }, - // Additional options for `Jaeger.Tracer` - tracerOptions: {}, - // Default tags. They will be added into all span tags. - defaultTags: null, - }, - }, - }, errorRegenerator: new CustomRegenerator(), started(): void { console.log('NetworkBroker started successfully.'); diff --git a/packages/core-services/package.json b/packages/core-services/package.json index 4f81be0fac02..e9487a8b3c72 100644 --- a/packages/core-services/package.json +++ b/packages/core-services/package.json @@ -38,6 +38,7 @@ "@rocket.chat/message-parser": "workspace:^", "@rocket.chat/models": "workspace:^", "@rocket.chat/rest-typings": "workspace:^", + "@rocket.chat/tracing": "workspace:^", "@rocket.chat/ui-kit": "workspace:~" } } diff --git a/packages/core-services/src/index.ts b/packages/core-services/src/index.ts index 34694b078e01..c9cfa82caabb 100644 --- a/packages/core-services/src/index.ts +++ b/packages/core-services/src/index.ts @@ -51,6 +51,7 @@ import type { IVoipFreeSwitchService } from './types/IVoipFreeSwitchService'; import type { IVoipService } from './types/IVoipService'; export { asyncLocalStorage } from './lib/asyncLocalStorage'; +export { traceInstanceMethods } from './lib/asyncMethodCallContext'; export { MeteorError, isMeteorError } from './MeteorError'; export { api } from './api'; export { EventSignatures } from './events/Events'; diff --git a/packages/core-services/src/lib/asyncMethodCallContext.ts b/packages/core-services/src/lib/asyncMethodCallContext.ts new file mode 100644 index 000000000000..cd9f19baf976 --- /dev/null +++ b/packages/core-services/src/lib/asyncMethodCallContext.ts @@ -0,0 +1,44 @@ +import { tracerActiveSpan } from '@rocket.chat/tracing'; + +const getArguments = (args: any[]): any[] => { + return args.map((arg) => { + if (typeof arg === 'object' && arg != null && 'session' in arg) { + return '[mongo options with session]'; + } + return arg; + }); +}; + +export function traceInstanceMethods(instance: T, ignoreMethods: string[] = []): T { + const className = instance.constructor.name; + + return new Proxy(instance, { + get(target: Record, prop: string): any { + if (typeof target[prop] === 'function' && !ignoreMethods.includes(prop)) { + return new Proxy(target[prop], { + apply: (target, thisArg, argumentsList): any => { + if (['doNotMixInclusionAndExclusionFields', 'ensureDefaultFields'].includes(prop)) { + return Reflect.apply(target, thisArg, argumentsList); + } + + return tracerActiveSpan( + `model ${className}.${prop}`, + { + attributes: { + model: className, + method: prop, + parameters: getArguments(argumentsList), + }, + }, + () => { + return Reflect.apply(target, thisArg, argumentsList); + }, + ); + }, + }); + } + + return Reflect.get(target, prop); + }, + }) as T; +} diff --git a/packages/core-services/src/lib/mongo.ts b/packages/core-services/src/lib/mongo.ts index fab1fd108d99..b41d838cd4a7 100644 --- a/packages/core-services/src/lib/mongo.ts +++ b/packages/core-services/src/lib/mongo.ts @@ -1,3 +1,4 @@ +import { initDatabaseTracing, isTracingEnabled } from '@rocket.chat/tracing'; import { MongoClient } from 'mongodb'; import type { Db, Collection, MongoClientOptions, Document } from 'mongodb'; @@ -6,7 +7,10 @@ const { MONGO_URL = 'mongodb://localhost:27017/rocketchat' } = process.env; const name = /^mongodb:\/\/.*?(?::[0-9]+)?\/([^?]*)/.exec(MONGO_URL)?.[1]; function connectDb(options?: MongoClientOptions): Promise { - const client = new MongoClient(MONGO_URL, options); + const client = new MongoClient(MONGO_URL, { + ...options, + monitorCommands: isTracingEnabled(), + }); return client.connect().catch((error) => { // exits the process in case of any error @@ -29,6 +33,8 @@ export const getConnection = ((): ((options?: MongoClientOptions) => Promise db = client.db(name); } + initDatabaseTracing(client); + // if getConnection was called multiple times before it was connected, wait for the connection return client.db(name); }; diff --git a/packages/instance-status/package.json b/packages/instance-status/package.json index be23f8a44498..9afd2d88dc0d 100644 --- a/packages/instance-status/package.json +++ b/packages/instance-status/package.json @@ -22,6 +22,7 @@ "/dist" ], "dependencies": { - "@rocket.chat/models": "workspace:^" + "@rocket.chat/models": "workspace:^", + "@rocket.chat/tracing": "workspace:^" } } diff --git a/packages/instance-status/src/index.ts b/packages/instance-status/src/index.ts index 0e74e1a7ef4f..38109e01626d 100644 --- a/packages/instance-status/src/index.ts +++ b/packages/instance-status/src/index.ts @@ -2,6 +2,7 @@ import { EventEmitter } from 'events'; import { InstanceStatus as InstanceStatusModel } from '@rocket.chat/models'; +import { tracerSpan } from '@rocket.chat/tracing'; import { v4 as uuidv4 } from 'uuid'; const events = new EventEmitter(); @@ -113,8 +114,8 @@ function start(interval?: number) { interval = interval || defaultPingInterval; - pingInterval = setInterval(function () { - ping(); + pingInterval = setInterval(async function () { + await tracerSpan('InstanceStatus.ping', {}, () => ping()); }, interval * 1000); } diff --git a/packages/tracing/.eslintrc.json b/packages/tracing/.eslintrc.json new file mode 100644 index 000000000000..a83aeda48e66 --- /dev/null +++ b/packages/tracing/.eslintrc.json @@ -0,0 +1,4 @@ +{ + "extends": ["@rocket.chat/eslint-config"], + "ignorePatterns": ["**/dist"] +} diff --git a/packages/tracing/package.json b/packages/tracing/package.json new file mode 100644 index 000000000000..ef19ad071b9f --- /dev/null +++ b/packages/tracing/package.json @@ -0,0 +1,32 @@ +{ + "name": "@rocket.chat/tracing", + "version": "0.0.1", + "private": true, + "devDependencies": { + "@types/jest": "~29.5.7", + "eslint": "~8.45.0", + "jest": "~29.6.4", + "ts-jest": "~29.1.1", + "typescript": "~5.3.3" + }, + "scripts": { + "lint": "eslint --ext .js,.jsx,.ts,.tsx .", + "lint:fix": "eslint --ext .js,.jsx,.ts,.tsx . --fix", + "testunit": "jest --passWithNoTests", + "dev": "tsc --watch --preserveWatchOutput", + "build": "rm -rf dist && tsc" + }, + "main": "./dist/index.js", + "typings": "./dist/index.d.ts", + "files": [ + "/dist" + ], + "volta": { + "extends": "../../package.json" + }, + "dependencies": { + "@opentelemetry/api": "^1.9.0", + "@opentelemetry/exporter-trace-otlp-grpc": "^0.53.0", + "@opentelemetry/sdk-node": "^0.53.0" + } +} diff --git a/packages/tracing/src/index.ts b/packages/tracing/src/index.ts new file mode 100644 index 000000000000..8e97dabc63c5 --- /dev/null +++ b/packages/tracing/src/index.ts @@ -0,0 +1,96 @@ +import { context, propagation, SpanStatusCode, trace } from '@opentelemetry/api'; +import type { Span, SpanOptions, Tracer } from '@opentelemetry/api'; +import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-grpc'; +import { NodeSDK } from '@opentelemetry/sdk-node'; + +export { initDatabaseTracing } from './traceDatabaseCalls'; + +let tracer: Tracer | undefined; + +export function isTracingEnabled() { + return ['yes', 'true'].includes(String(process.env.TRACING_ENABLED).toLowerCase()); +} + +export const startTracing = ({ service }: { service: string }) => { + const exporter = new OTLPTraceExporter(); + + const sdk = new NodeSDK({ + traceExporter: exporter, + instrumentations: [], + serviceName: service, + }); + sdk.start(); + + tracer = trace.getTracer(service); +}; + +export function tracerSpan ReturnType>( + name: string, + options: SpanOptions, + fn: F, + optl?: unknown, +): ReturnType { + if (!isTracingEnabled()) { + return fn(); + } + + if (!tracer) { + throw new Error(`Tracing is enabled but not started. You should call 'startTracing()' to fix this.`); + } + + const computeResult = (span: Span) => { + try { + const result = fn(span); + if (result instanceof Promise) { + result.catch((err) => { + span.recordException(err); + span.setStatus({ + code: SpanStatusCode.ERROR, + message: err.message, + }); + }); + + return result; + } + return result; + } catch (err: any) { + span.recordException(err); + span.setStatus({ + code: SpanStatusCode.ERROR, + message: err.message, + }); + throw err; + } finally { + span.end(); + } + }; + + if (optl) { + const activeContext = propagation.extract(context.active(), optl); + + return tracer.startActiveSpan(name, options, activeContext, computeResult); + } + + return tracer.startActiveSpan(name, options, computeResult); +} + +export function tracerActiveSpan ReturnType>( + name: string, + options: SpanOptions, + fn: F, + optl?: unknown, +): ReturnType { + const currentSpan = trace.getSpan(context.active()); + + if (process.env.LOG_UNTRACED_METHODS) { + console.log(`No active span for ${name}`, new Error().stack); + } + + return currentSpan ? tracerSpan(name, options, fn, optl) : fn(); +} + +export function injectCurrentContext() { + const output: Record = {}; + propagation.inject(context.active(), output); + return output; +} diff --git a/packages/tracing/src/traceDatabaseCalls.ts b/packages/tracing/src/traceDatabaseCalls.ts new file mode 100644 index 000000000000..6ddde0b39753 --- /dev/null +++ b/packages/tracing/src/traceDatabaseCalls.ts @@ -0,0 +1,65 @@ +import { trace, context, SpanStatusCode } from '@opentelemetry/api'; +import type { MongoClient } from 'mongodb'; + +import { isTracingEnabled } from '.'; + +const tracer = trace.getTracer('core'); + +export const initDatabaseTracing = (client: MongoClient) => { + if (!isTracingEnabled()) { + return; + } + + const DurationStart = new Map(); + + client.on('commandStarted', (event) => { + const collection = event.command[event.commandName]; + + const currentSpan = trace.getSpan(context.active()); + if (currentSpan) { + const span = tracer.startSpan(`mongodb ${collection}.${event.commandName}`, { + attributes: { + 'db.connection_string': event.address, + 'db.mongodb.collection': collection, + 'db.name': event.databaseName, + 'db.operation': event.commandName, + 'db.statement': JSON.stringify(event.command, null, 2), + 'db.system': 'mongodb', + // net.peer.name + // net.peer.port + }, + }); + + DurationStart.set(event.requestId, { event, span }); + } + }); + + client.on('commandSucceeded', (event) => { + if (!DurationStart.has(event.requestId)) { + return; + } + + const { span } = DurationStart.get(event.requestId); + DurationStart.delete(event.requestId); + + span.end(); + }); + + client.on('commandFailed', (event) => { + if (!DurationStart.has(event.requestId)) { + return; + } + + const { span } = DurationStart.get(event.requestId); + + DurationStart.delete(event.requestId); + + span.recordException(event.failure); + span.setStatus({ + code: SpanStatusCode.ERROR, + message: event.failure.message, + }); + + span.end(); + }); +}; diff --git a/packages/tracing/tsconfig.json b/packages/tracing/tsconfig.json new file mode 100644 index 000000000000..52e9dd8c4976 --- /dev/null +++ b/packages/tracing/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.base.server.json", + "compilerOptions": { + "declaration": true, + "rootDir": "./src", + "outDir": "./dist" + }, + "include": ["./src/**/*"] +} diff --git a/tsconfig.base.server.json b/tsconfig.base.server.json index a7a87f033d7f..6864b82a7f82 100644 --- a/tsconfig.base.server.json +++ b/tsconfig.base.server.json @@ -6,5 +6,8 @@ "module": "commonjs", "sourceMap": true, }, - "exclude": ["node_modules", "**/*.spec.ts"] + "exclude": ["node_modules", "**/*.spec.ts"], + "ts-node": { + "transpileOnly": true + } } diff --git a/yarn.lock b/yarn.lock index bdb87b964721..7f22ffc44c08 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4008,6 +4008,30 @@ __metadata: languageName: node linkType: hard +"@grpc/grpc-js@npm:^1.7.1": + version: 1.12.2 + resolution: "@grpc/grpc-js@npm:1.12.2" + dependencies: + "@grpc/proto-loader": "npm:^0.7.13" + "@js-sdsl/ordered-map": "npm:^4.4.2" + checksum: 10/0d0556da8515704b5e722b86097e04693d8c71ba286a076270a96e1ac3a4950e87559c718cc2875d3fcaa6cb8e07d0cc6b1db2673b8940829dfe8b75197844dd + languageName: node + linkType: hard + +"@grpc/proto-loader@npm:^0.7.13": + version: 0.7.13 + resolution: "@grpc/proto-loader@npm:0.7.13" + dependencies: + lodash.camelcase: "npm:^4.3.0" + long: "npm:^5.0.0" + protobufjs: "npm:^7.2.5" + yargs: "npm:^17.7.2" + bin: + proto-loader-gen-types: build/bin/proto-loader-gen-types.js + checksum: 10/7e2d842c2061cbaf6450c71da0077263be3bab165454d5c8a3e1ae4d3c6d2915f02fd27da63ff01f05e127b1221acd40705273f5d29303901e60514e852992f4 + languageName: node + linkType: hard + "@humanwhocodes/config-array@npm:^0.11.10": version: 0.11.10 resolution: "@humanwhocodes/config-array@npm:0.11.10" @@ -4192,7 +4216,7 @@ __metadata: languageName: node linkType: hard -"@jest/core@npm:^29.7.0": +"@jest/core@npm:^29.6.4, @jest/core@npm:^29.7.0": version: 29.7.0 resolution: "@jest/core@npm:29.7.0" dependencies: @@ -4489,6 +4513,13 @@ __metadata: languageName: node linkType: hard +"@js-sdsl/ordered-map@npm:^4.4.2": + version: 4.4.2 + resolution: "@js-sdsl/ordered-map@npm:4.4.2" + checksum: 10/ac64e3f0615ecc015461c9f527f124d2edaa9e68de153c1e270c627e01e83d046522d7e872692fd57a8c514578b539afceff75831c0d8b2a9a7a347fbed35af4 + languageName: node + linkType: hard + "@jsonjoy.com/base64@npm:^1.1.1": version: 1.1.2 resolution: "@jsonjoy.com/base64@npm:1.1.2" @@ -5295,13 +5326,329 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/api@npm:^1.4.0": +"@opentelemetry/api-logs@npm:0.53.0": + version: 0.53.0 + resolution: "@opentelemetry/api-logs@npm:0.53.0" + dependencies: + "@opentelemetry/api": "npm:^1.0.0" + checksum: 10/347b4554d6ee01afb29bd39e8f9cbbccd80abb0883fe6a84e3bcce8ab4dbfe357a2729246d2f66de0de6272846fd1bb2d71e286e18ad2690d9e7f46f02f00f73 + languageName: node + linkType: hard + +"@opentelemetry/api@npm:^1.0.0, @opentelemetry/api@npm:^1.4.0, @opentelemetry/api@npm:^1.9.0": version: 1.9.0 resolution: "@opentelemetry/api@npm:1.9.0" checksum: 10/a607f0eef971893c4f2ee2a4c2069aade6ec3e84e2a1f5c2aac19f65c5d9eeea41aa72db917c1029faafdd71789a1a040bdc18f40d63690e22ccae5d7070f194 languageName: node linkType: hard +"@opentelemetry/context-async-hooks@npm:1.26.0": + version: 1.26.0 + resolution: "@opentelemetry/context-async-hooks@npm:1.26.0" + peerDependencies: + "@opentelemetry/api": ">=1.0.0 <1.10.0" + checksum: 10/c8824cc00385f21ecdf5b48ac474096687f9ce2e8d34612a62ee8bc7a6e25797c787239349a12bfeefbff200dcb7379ca45355a5684b9755dcf8fbd3b69cf523 + languageName: node + linkType: hard + +"@opentelemetry/core@npm:1.26.0": + version: 1.26.0 + resolution: "@opentelemetry/core@npm:1.26.0" + dependencies: + "@opentelemetry/semantic-conventions": "npm:1.27.0" + peerDependencies: + "@opentelemetry/api": ">=1.0.0 <1.10.0" + checksum: 10/474b6bcf42cd2825d56f915eb0d6e6cdcb37777a11fc2618fc2fa50754f4b9b5df23944f3aab186cb3ab930db5c3a81efa3183362802314a966930110346e6a4 + languageName: node + linkType: hard + +"@opentelemetry/exporter-logs-otlp-grpc@npm:0.53.0": + version: 0.53.0 + resolution: "@opentelemetry/exporter-logs-otlp-grpc@npm:0.53.0" + dependencies: + "@grpc/grpc-js": "npm:^1.7.1" + "@opentelemetry/core": "npm:1.26.0" + "@opentelemetry/otlp-grpc-exporter-base": "npm:0.53.0" + "@opentelemetry/otlp-transformer": "npm:0.53.0" + "@opentelemetry/sdk-logs": "npm:0.53.0" + peerDependencies: + "@opentelemetry/api": ^1.0.0 + checksum: 10/4a8236acffe847d95ffb9098efdded74ff9ccc1e4f5ad68d7cc110f14a8a29841e2c9e5ee201f38bb49602690bd197c9ef3536ae3f23c012ce3248a65327f2bb + languageName: node + linkType: hard + +"@opentelemetry/exporter-logs-otlp-http@npm:0.53.0": + version: 0.53.0 + resolution: "@opentelemetry/exporter-logs-otlp-http@npm:0.53.0" + dependencies: + "@opentelemetry/api-logs": "npm:0.53.0" + "@opentelemetry/core": "npm:1.26.0" + "@opentelemetry/otlp-exporter-base": "npm:0.53.0" + "@opentelemetry/otlp-transformer": "npm:0.53.0" + "@opentelemetry/sdk-logs": "npm:0.53.0" + peerDependencies: + "@opentelemetry/api": ^1.0.0 + checksum: 10/bf0d19a71eed3d90f51f6ab1c5fdbb7837477db50b47a46944efeab42c6d72ef13677487eb1ed4cd0d6e6fccbd41cec33a31e17aaef0e24f9bad5b52ab8b3649 + languageName: node + linkType: hard + +"@opentelemetry/exporter-logs-otlp-proto@npm:0.53.0": + version: 0.53.0 + resolution: "@opentelemetry/exporter-logs-otlp-proto@npm:0.53.0" + dependencies: + "@opentelemetry/api-logs": "npm:0.53.0" + "@opentelemetry/core": "npm:1.26.0" + "@opentelemetry/otlp-exporter-base": "npm:0.53.0" + "@opentelemetry/otlp-transformer": "npm:0.53.0" + "@opentelemetry/resources": "npm:1.26.0" + "@opentelemetry/sdk-logs": "npm:0.53.0" + "@opentelemetry/sdk-trace-base": "npm:1.26.0" + peerDependencies: + "@opentelemetry/api": ^1.0.0 + checksum: 10/295f73fb7c098e54d83149ab1a681f8f16b379111dc23201b7eaa803eff6ba42bebd99a00df1a02112bd181b341588e8de3e09364b9f28d2c91d8bd2be4824a7 + languageName: node + linkType: hard + +"@opentelemetry/exporter-trace-otlp-grpc@npm:0.53.0, @opentelemetry/exporter-trace-otlp-grpc@npm:^0.53.0": + version: 0.53.0 + resolution: "@opentelemetry/exporter-trace-otlp-grpc@npm:0.53.0" + dependencies: + "@grpc/grpc-js": "npm:^1.7.1" + "@opentelemetry/core": "npm:1.26.0" + "@opentelemetry/otlp-grpc-exporter-base": "npm:0.53.0" + "@opentelemetry/otlp-transformer": "npm:0.53.0" + "@opentelemetry/resources": "npm:1.26.0" + "@opentelemetry/sdk-trace-base": "npm:1.26.0" + peerDependencies: + "@opentelemetry/api": ^1.0.0 + checksum: 10/3c8a854f9c401549ead8a900d4773de891663ef6233e457d57557834cacc547a637dccf0f4c63e9b367f33925f5a4991e37910566681481169ee17e5253b6894 + languageName: node + linkType: hard + +"@opentelemetry/exporter-trace-otlp-http@npm:0.53.0": + version: 0.53.0 + resolution: "@opentelemetry/exporter-trace-otlp-http@npm:0.53.0" + dependencies: + "@opentelemetry/core": "npm:1.26.0" + "@opentelemetry/otlp-exporter-base": "npm:0.53.0" + "@opentelemetry/otlp-transformer": "npm:0.53.0" + "@opentelemetry/resources": "npm:1.26.0" + "@opentelemetry/sdk-trace-base": "npm:1.26.0" + peerDependencies: + "@opentelemetry/api": ^1.0.0 + checksum: 10/28c75e25564833bc448b5733415730483c9f28714577acb679087d5ccfc46d74b3f24996c41f2c93bf6a6406edb1cad7e8cf2a76b61096e3f417f90044e1d795 + languageName: node + linkType: hard + +"@opentelemetry/exporter-trace-otlp-proto@npm:0.53.0": + version: 0.53.0 + resolution: "@opentelemetry/exporter-trace-otlp-proto@npm:0.53.0" + dependencies: + "@opentelemetry/core": "npm:1.26.0" + "@opentelemetry/otlp-exporter-base": "npm:0.53.0" + "@opentelemetry/otlp-transformer": "npm:0.53.0" + "@opentelemetry/resources": "npm:1.26.0" + "@opentelemetry/sdk-trace-base": "npm:1.26.0" + peerDependencies: + "@opentelemetry/api": ^1.0.0 + checksum: 10/2d4651db9ef5d5b1b22b84a6d268ae4e020bf107a71f7d96a8b149f3af4680d6087dfdabf3b3a606f862888ccc63bfe4368057c45befc58c60bda48eceab50ea + languageName: node + linkType: hard + +"@opentelemetry/exporter-zipkin@npm:1.26.0": + version: 1.26.0 + resolution: "@opentelemetry/exporter-zipkin@npm:1.26.0" + dependencies: + "@opentelemetry/core": "npm:1.26.0" + "@opentelemetry/resources": "npm:1.26.0" + "@opentelemetry/sdk-trace-base": "npm:1.26.0" + "@opentelemetry/semantic-conventions": "npm:1.27.0" + peerDependencies: + "@opentelemetry/api": ^1.0.0 + checksum: 10/155a450a17f1963667dd6b20150858ebcdc9bc358396b2d3df728faa7d66fed68e6c2f0c4cfb4598d3f03aa7ad2ba280e2af033245289d7a5d98b6e5ccd7c54a + languageName: node + linkType: hard + +"@opentelemetry/instrumentation@npm:0.53.0": + version: 0.53.0 + resolution: "@opentelemetry/instrumentation@npm:0.53.0" + dependencies: + "@opentelemetry/api-logs": "npm:0.53.0" + "@types/shimmer": "npm:^1.2.0" + import-in-the-middle: "npm:^1.8.1" + require-in-the-middle: "npm:^7.1.1" + semver: "npm:^7.5.2" + shimmer: "npm:^1.2.1" + peerDependencies: + "@opentelemetry/api": ^1.3.0 + checksum: 10/4b994c8568a503a15655cba249b1dbdef3f67dfda37938abba6267ba75b6d72a9aa276be4b0c8874e86f98ab89d92877e1874e0565a7e67f062c43dfcbbb16a5 + languageName: node + linkType: hard + +"@opentelemetry/otlp-exporter-base@npm:0.53.0": + version: 0.53.0 + resolution: "@opentelemetry/otlp-exporter-base@npm:0.53.0" + dependencies: + "@opentelemetry/core": "npm:1.26.0" + "@opentelemetry/otlp-transformer": "npm:0.53.0" + peerDependencies: + "@opentelemetry/api": ^1.0.0 + checksum: 10/ca59d73ae8f83946062b060a9a382fc7db6154c892ed56b6ab7f545530ba4850b4d0a748daaa30d1177ef6a8c2a0fddd34a199080f4474ec445944cece86f1ef + languageName: node + linkType: hard + +"@opentelemetry/otlp-grpc-exporter-base@npm:0.53.0": + version: 0.53.0 + resolution: "@opentelemetry/otlp-grpc-exporter-base@npm:0.53.0" + dependencies: + "@grpc/grpc-js": "npm:^1.7.1" + "@opentelemetry/core": "npm:1.26.0" + "@opentelemetry/otlp-exporter-base": "npm:0.53.0" + "@opentelemetry/otlp-transformer": "npm:0.53.0" + peerDependencies: + "@opentelemetry/api": ^1.0.0 + checksum: 10/412e0428946277b7fbfb7ceafd9624fa930cbc9ff892cc0f796f712ee4b1a6d53516a2891bce5ffc9e72a209b32953d4d87e726e55c9ea422dc75ed580c0af37 + languageName: node + linkType: hard + +"@opentelemetry/otlp-transformer@npm:0.53.0": + version: 0.53.0 + resolution: "@opentelemetry/otlp-transformer@npm:0.53.0" + dependencies: + "@opentelemetry/api-logs": "npm:0.53.0" + "@opentelemetry/core": "npm:1.26.0" + "@opentelemetry/resources": "npm:1.26.0" + "@opentelemetry/sdk-logs": "npm:0.53.0" + "@opentelemetry/sdk-metrics": "npm:1.26.0" + "@opentelemetry/sdk-trace-base": "npm:1.26.0" + protobufjs: "npm:^7.3.0" + peerDependencies: + "@opentelemetry/api": ^1.3.0 + checksum: 10/578cf13d7984a0b1ba1db3d86d1e358bf70e8b534166f8327a10fccca0afd3900896a80e5e73caae61837b0cbc99d81b44784edee68a3517d73f5330a3624ccd + languageName: node + linkType: hard + +"@opentelemetry/propagator-b3@npm:1.26.0": + version: 1.26.0 + resolution: "@opentelemetry/propagator-b3@npm:1.26.0" + dependencies: + "@opentelemetry/core": "npm:1.26.0" + peerDependencies: + "@opentelemetry/api": ">=1.0.0 <1.10.0" + checksum: 10/fa99958ccd7e2d8140c6271a6c2bc0b95054691ce227b75272951bb1f387bd442ee0813b5f5e268c837d3a563c36ac516bac37aa821e1b5119c7f21c90742c89 + languageName: node + linkType: hard + +"@opentelemetry/propagator-jaeger@npm:1.26.0": + version: 1.26.0 + resolution: "@opentelemetry/propagator-jaeger@npm:1.26.0" + dependencies: + "@opentelemetry/core": "npm:1.26.0" + peerDependencies: + "@opentelemetry/api": ">=1.0.0 <1.10.0" + checksum: 10/f895186e6c95a1ca9cc172de50867c036f87dacdfd9206df2ab35710134221b0021974eb2ced2453840a4fbb6aae55a732847b898e420b3871b3157eb81183ed + languageName: node + linkType: hard + +"@opentelemetry/resources@npm:1.26.0": + version: 1.26.0 + resolution: "@opentelemetry/resources@npm:1.26.0" + dependencies: + "@opentelemetry/core": "npm:1.26.0" + "@opentelemetry/semantic-conventions": "npm:1.27.0" + peerDependencies: + "@opentelemetry/api": ">=1.0.0 <1.10.0" + checksum: 10/ce60dbf2bd424b01824b72f533724eaf64418e01c43bef952b87dbff6d2a0f28cdcbea0d3d95c5e324f609e58721bf52ea91b5518b0e30d6bb03fb95af85cc33 + languageName: node + linkType: hard + +"@opentelemetry/sdk-logs@npm:0.53.0": + version: 0.53.0 + resolution: "@opentelemetry/sdk-logs@npm:0.53.0" + dependencies: + "@opentelemetry/api-logs": "npm:0.53.0" + "@opentelemetry/core": "npm:1.26.0" + "@opentelemetry/resources": "npm:1.26.0" + peerDependencies: + "@opentelemetry/api": ">=1.4.0 <1.10.0" + checksum: 10/b11b512820f3d55288f7478831587ebe2e7077980f060a779a13848c62cab30023734857c68ef110eebe961884cb8892d7c77841a5f1d22c2426cbb18d762975 + languageName: node + linkType: hard + +"@opentelemetry/sdk-metrics@npm:1.26.0": + version: 1.26.0 + resolution: "@opentelemetry/sdk-metrics@npm:1.26.0" + dependencies: + "@opentelemetry/core": "npm:1.26.0" + "@opentelemetry/resources": "npm:1.26.0" + peerDependencies: + "@opentelemetry/api": ">=1.3.0 <1.10.0" + checksum: 10/e48e4dd1fed1e501750460e1320f89507c19287c5059cfaccc8268ad8cc3e1de40feeee6584b23626e01f9cde0f10301d08edf6a65bbd1346ef94f70ae8844f5 + languageName: node + linkType: hard + +"@opentelemetry/sdk-node@npm:^0.53.0": + version: 0.53.0 + resolution: "@opentelemetry/sdk-node@npm:0.53.0" + dependencies: + "@opentelemetry/api-logs": "npm:0.53.0" + "@opentelemetry/core": "npm:1.26.0" + "@opentelemetry/exporter-logs-otlp-grpc": "npm:0.53.0" + "@opentelemetry/exporter-logs-otlp-http": "npm:0.53.0" + "@opentelemetry/exporter-logs-otlp-proto": "npm:0.53.0" + "@opentelemetry/exporter-trace-otlp-grpc": "npm:0.53.0" + "@opentelemetry/exporter-trace-otlp-http": "npm:0.53.0" + "@opentelemetry/exporter-trace-otlp-proto": "npm:0.53.0" + "@opentelemetry/exporter-zipkin": "npm:1.26.0" + "@opentelemetry/instrumentation": "npm:0.53.0" + "@opentelemetry/resources": "npm:1.26.0" + "@opentelemetry/sdk-logs": "npm:0.53.0" + "@opentelemetry/sdk-metrics": "npm:1.26.0" + "@opentelemetry/sdk-trace-base": "npm:1.26.0" + "@opentelemetry/sdk-trace-node": "npm:1.26.0" + "@opentelemetry/semantic-conventions": "npm:1.27.0" + peerDependencies: + "@opentelemetry/api": ">=1.3.0 <1.10.0" + checksum: 10/ce05610b11336ad8218a39c1798d7090e1c1666956f37347b585e5e51e6e62b0cd24207474fa312528c10c8473dc6cbaefb6fe3647d76613c241795e1cfe6303 + languageName: node + linkType: hard + +"@opentelemetry/sdk-trace-base@npm:1.26.0": + version: 1.26.0 + resolution: "@opentelemetry/sdk-trace-base@npm:1.26.0" + dependencies: + "@opentelemetry/core": "npm:1.26.0" + "@opentelemetry/resources": "npm:1.26.0" + "@opentelemetry/semantic-conventions": "npm:1.27.0" + peerDependencies: + "@opentelemetry/api": ">=1.0.0 <1.10.0" + checksum: 10/e4a3d296ad908b9f58d7aefdcc1f7383fb0eb64fc85b0b5d18c4a7d829ce3d0efa5e53f5fe1a23185d9b5d97b782431384efe01aba8ba788922260a9dbbdb662 + languageName: node + linkType: hard + +"@opentelemetry/sdk-trace-node@npm:1.26.0": + version: 1.26.0 + resolution: "@opentelemetry/sdk-trace-node@npm:1.26.0" + dependencies: + "@opentelemetry/context-async-hooks": "npm:1.26.0" + "@opentelemetry/core": "npm:1.26.0" + "@opentelemetry/propagator-b3": "npm:1.26.0" + "@opentelemetry/propagator-jaeger": "npm:1.26.0" + "@opentelemetry/sdk-trace-base": "npm:1.26.0" + semver: "npm:^7.5.2" + peerDependencies: + "@opentelemetry/api": ">=1.0.0 <1.10.0" + checksum: 10/ac89d54b65e10928a13e3c0adf4d4997d53baf8b03f376c03d47e921322959a0f8cfae744f4bdc63bfd1ab22d67fcae8d7bd1f6bbfbaad18cd1623543128ac54 + languageName: node + linkType: hard + +"@opentelemetry/semantic-conventions@npm:1.27.0": + version: 1.27.0 + resolution: "@opentelemetry/semantic-conventions@npm:1.27.0" + checksum: 10/98166522f299e2fe3d43376adbdeb92679b75ebb172e2a3c4c71f2942bd91585e9537618efbbae6dc08177699e5719368edf66d7e69e8636f360b85217bbdbe1 + languageName: node + linkType: hard + "@parcel/watcher-android-arm64@npm:2.4.1": version: 2.4.1 resolution: "@parcel/watcher-android-arm64@npm:2.4.1" @@ -5468,6 +5815,79 @@ __metadata: languageName: node linkType: hard +"@protobufjs/aspromise@npm:^1.1.1, @protobufjs/aspromise@npm:^1.1.2": + version: 1.1.2 + resolution: "@protobufjs/aspromise@npm:1.1.2" + checksum: 10/8a938d84fe4889411296db66b29287bd61ea3c14c2d23e7a8325f46a2b8ce899857c5f038d65d7641805e6c1d06b495525c7faf00c44f85a7ee6476649034969 + languageName: node + linkType: hard + +"@protobufjs/base64@npm:^1.1.2": + version: 1.1.2 + resolution: "@protobufjs/base64@npm:1.1.2" + checksum: 10/c71b100daeb3c9bdccab5cbc29495b906ba0ae22ceedc200e1ba49717d9c4ab15a6256839cebb6f9c6acae4ed7c25c67e0a95e734f612b258261d1a3098fe342 + languageName: node + linkType: hard + +"@protobufjs/codegen@npm:^2.0.4": + version: 2.0.4 + resolution: "@protobufjs/codegen@npm:2.0.4" + checksum: 10/c6ee5fa172a8464f5253174d3c2353ea520c2573ad7b6476983d9b1346f4d8f2b44aa29feb17a949b83c1816bc35286a5ea265ed9d8fdd2865acfa09668c0447 + languageName: node + linkType: hard + +"@protobufjs/eventemitter@npm:^1.1.0": + version: 1.1.0 + resolution: "@protobufjs/eventemitter@npm:1.1.0" + checksum: 10/03af3e99f17ad421283d054c88a06a30a615922a817741b43ca1b13e7c6b37820a37f6eba9980fb5150c54dba6e26cb6f7b64a6f7d8afa83596fafb3afa218c3 + languageName: node + linkType: hard + +"@protobufjs/fetch@npm:^1.1.0": + version: 1.1.0 + resolution: "@protobufjs/fetch@npm:1.1.0" + dependencies: + "@protobufjs/aspromise": "npm:^1.1.1" + "@protobufjs/inquire": "npm:^1.1.0" + checksum: 10/67ae40572ad536e4ef94269199f252c024b66e3059850906bdaee161ca1d75c73d04d35cd56f147a8a5a079f5808e342b99e61942c1dae15604ff0600b09a958 + languageName: node + linkType: hard + +"@protobufjs/float@npm:^1.0.2": + version: 1.0.2 + resolution: "@protobufjs/float@npm:1.0.2" + checksum: 10/634c2c989da0ef2f4f19373d64187e2a79f598c5fb7991afb689d29a2ea17c14b796b29725945fa34b9493c17fb799e08ac0a7ccaae460ee1757d3083ed35187 + languageName: node + linkType: hard + +"@protobufjs/inquire@npm:^1.1.0": + version: 1.1.0 + resolution: "@protobufjs/inquire@npm:1.1.0" + checksum: 10/c09efa34a5465cb120775e1a482136f2340a58b4abce7e93d72b8b5a9324a0e879275016ef9fcd73d72a4731639c54f2bb755bb82f916e4a78892d1d840bb3d2 + languageName: node + linkType: hard + +"@protobufjs/path@npm:^1.1.2": + version: 1.1.2 + resolution: "@protobufjs/path@npm:1.1.2" + checksum: 10/bb709567935fd385a86ad1f575aea98131bbd719c743fb9b6edd6b47ede429ff71a801cecbd64fc72deebf4e08b8f1bd8062793178cdaed3713b8d15771f9b83 + languageName: node + linkType: hard + +"@protobufjs/pool@npm:^1.1.0": + version: 1.1.0 + resolution: "@protobufjs/pool@npm:1.1.0" + checksum: 10/b9c7047647f6af28e92aac54f6f7c1f7ff31b201b4bfcc7a415b2861528854fce3ec666d7e7e10fd744da905f7d4aef2205bbcc8944ca0ca7a82e18134d00c46 + languageName: node + linkType: hard + +"@protobufjs/utf8@npm:^1.1.0": + version: 1.1.0 + resolution: "@protobufjs/utf8@npm:1.1.0" + checksum: 10/131e289c57534c1d73a0e55782d6751dd821db1583cb2f7f7e017c9d6747addaebe79f28120b2e0185395d990aad347fb14ffa73ef4096fa38508d61a0e64602 + languageName: node + linkType: hard + "@react-aria/breadcrumbs@npm:^3.5.0": version: 3.5.1 resolution: "@react-aria/breadcrumbs@npm:3.5.1" @@ -7607,6 +8027,7 @@ __metadata: "@rocket.chat/rest-typings": "workspace:^" "@rocket.chat/string-helpers": "npm:~0.31.25" "@rocket.chat/tools": "workspace:^" + "@rocket.chat/tracing": "workspace:^" "@types/bcrypt": "npm:^5.0.2" "@types/gc-stats": "npm:^1.4.3" "@types/node": "npm:^14.18.63" @@ -7742,6 +8163,7 @@ __metadata: "@rocket.chat/network-broker": "workspace:^" "@rocket.chat/rest-typings": "workspace:^" "@rocket.chat/string-helpers": "npm:~0.31.25" + "@rocket.chat/tracing": "workspace:^" "@types/gc-stats": "npm:^1.4.3" "@types/node": "npm:^14.18.63" "@types/polka": "npm:^0.5.7" @@ -7802,6 +8224,7 @@ __metadata: "@rocket.chat/message-parser": "workspace:^" "@rocket.chat/models": "workspace:^" "@rocket.chat/rest-typings": "workspace:^" + "@rocket.chat/tracing": "workspace:^" "@rocket.chat/ui-kit": "workspace:~" "@types/jest": "npm:~29.5.13" babel-jest: "npm:^29.5.0" @@ -7903,6 +8326,7 @@ __metadata: "@rocket.chat/network-broker": "workspace:^" "@rocket.chat/rest-typings": "workspace:^" "@rocket.chat/string-helpers": "npm:~0.31.25" + "@rocket.chat/tracing": "workspace:^" "@types/ejson": "npm:^2.2.2" "@types/gc-stats": "npm:^1.4.3" "@types/meteor": "npm:^2.9.8" @@ -8217,6 +8641,7 @@ __metadata: dependencies: "@rocket.chat/eslint-config": "workspace:^" "@rocket.chat/models": "workspace:^" + "@rocket.chat/tracing": "workspace:^" eslint: "npm:~8.45.0" mongodb: "patch:mongodb@npm%3A4.17.2#~/.yarn/patches/mongodb-npm-4.17.2-40d1286d70.patch" prettier: "npm:~2.8.8" @@ -8487,6 +8912,9 @@ __metadata: "@nivo/heatmap": "npm:0.84.0" "@nivo/line": "npm:0.84.0" "@nivo/pie": "npm:0.84.0" + "@opentelemetry/api": "npm:^1.9.0" + "@opentelemetry/exporter-trace-otlp-grpc": "npm:^0.53.0" + "@opentelemetry/sdk-node": "npm:^0.53.0" "@playwright/test": "npm:^1.40.1" "@react-aria/color": "npm:^3.0.0-beta.15" "@react-aria/toolbar": "npm:^3.0.0-beta.1" @@ -8545,6 +8973,7 @@ __metadata: "@rocket.chat/sha256": "workspace:^" "@rocket.chat/string-helpers": "npm:~0.31.25" "@rocket.chat/tools": "workspace:^" + "@rocket.chat/tracing": "workspace:^" "@rocket.chat/ui-avatar": "workspace:^" "@rocket.chat/ui-client": "workspace:^" "@rocket.chat/ui-composer": "workspace:^" @@ -8974,6 +9403,7 @@ __metadata: "@rocket.chat/omnichannel-services": "workspace:^" "@rocket.chat/pdf-worker": "workspace:^" "@rocket.chat/tools": "workspace:^" + "@rocket.chat/tracing": "workspace:^" "@types/gc-stats": "npm:^1.4.3" "@types/node": "npm:^14.18.63" "@types/polka": "npm:^0.5.7" @@ -9114,6 +9544,7 @@ __metadata: "@rocket.chat/network-broker": "workspace:^" "@rocket.chat/presence": "workspace:^" "@rocket.chat/string-helpers": "npm:~0.31.25" + "@rocket.chat/tracing": "workspace:^" "@types/gc-stats": "npm:^1.4.3" "@types/node": "npm:^14.18.63" "@types/polka": "npm:^0.5.7" @@ -9177,6 +9608,7 @@ __metadata: "@rocket.chat/models": "workspace:^" "@rocket.chat/network-broker": "workspace:^" "@rocket.chat/omnichannel-services": "workspace:^" + "@rocket.chat/tracing": "workspace:^" "@types/gc-stats": "npm:^1.4.3" "@types/node": "npm:^14.18.63" "@types/polka": "npm:^0.5.7" @@ -9336,6 +9768,7 @@ __metadata: "@rocket.chat/network-broker": "workspace:^" "@rocket.chat/rest-typings": "workspace:^" "@rocket.chat/string-helpers": "npm:~0.31.25" + "@rocket.chat/tracing": "workspace:^" "@types/bcrypt": "npm:^5.0.2" "@types/gc-stats": "npm:^1.4.3" "@types/node": "npm:^14.18.63" @@ -9396,6 +9829,21 @@ __metadata: languageName: unknown linkType: soft +"@rocket.chat/tracing@workspace:^, @rocket.chat/tracing@workspace:packages/tracing": + version: 0.0.0-use.local + resolution: "@rocket.chat/tracing@workspace:packages/tracing" + dependencies: + "@opentelemetry/api": "npm:^1.9.0" + "@opentelemetry/exporter-trace-otlp-grpc": "npm:^0.53.0" + "@opentelemetry/sdk-node": "npm:^0.53.0" + "@types/jest": "npm:~29.5.7" + eslint: "npm:~8.45.0" + jest: "npm:~29.6.4" + ts-jest: "npm:~29.1.1" + typescript: "npm:~5.3.3" + languageName: unknown + linkType: soft + "@rocket.chat/ui-avatar@workspace:^, @rocket.chat/ui-avatar@workspace:packages/ui-avatar, @rocket.chat/ui-avatar@workspace:~": version: 0.0.0-use.local resolution: "@rocket.chat/ui-avatar@workspace:packages/ui-avatar" @@ -11913,7 +12361,7 @@ __metadata: languageName: node linkType: hard -"@types/jest@npm:*, @types/jest@npm:~29.5.12, @types/jest@npm:~29.5.13": +"@types/jest@npm:*, @types/jest@npm:~29.5.12, @types/jest@npm:~29.5.13, @types/jest@npm:~29.5.7": version: 29.5.13 resolution: "@types/jest@npm:29.5.13" dependencies: @@ -12294,6 +12742,15 @@ __metadata: languageName: node linkType: hard +"@types/node@npm:>=13.7.0, @types/node@npm:^22.0.0": + version: 22.7.5 + resolution: "@types/node@npm:22.7.5" + dependencies: + undici-types: "npm:~6.19.2" + checksum: 10/e8ba102f8c1aa7623787d625389be68d64e54fcbb76d41f6c2c64e8cf4c9f4a2370e7ef5e5f1732f3c57529d3d26afdcb2edc0101c5e413a79081449825c57ac + languageName: node + linkType: hard + "@types/node@npm:^12.7.1": version: 12.20.55 resolution: "@types/node@npm:12.20.55" @@ -12324,15 +12781,6 @@ __metadata: languageName: node linkType: hard -"@types/node@npm:^22.0.0": - version: 22.7.5 - resolution: "@types/node@npm:22.7.5" - dependencies: - undici-types: "npm:~6.19.2" - checksum: 10/e8ba102f8c1aa7623787d625389be68d64e54fcbb76d41f6c2c64e8cf4c9f4a2370e7ef5e5f1732f3c57529d3d26afdcb2edc0101c5e413a79081449825c57ac - languageName: node - linkType: hard - "@types/nodemailer@npm:*, @types/nodemailer@npm:^6.4.15": version: 6.4.15 resolution: "@types/nodemailer@npm:6.4.15" @@ -12664,6 +13112,13 @@ __metadata: languageName: node linkType: hard +"@types/shimmer@npm:^1.2.0": + version: 1.2.0 + resolution: "@types/shimmer@npm:1.2.0" + checksum: 10/f081a31d826ce7bfe8cc7ba8129d2b1dffae44fd580eba4fcf741237646c4c2494ae6de2cada4b7713d138f35f4bc512dbf01311d813dee82020f97d7d8c491c + languageName: node + linkType: hard + "@types/sinon@npm:^10.0.20": version: 10.0.20 resolution: "@types/sinon@npm:10.0.20" @@ -16565,7 +17020,7 @@ __metadata: languageName: node linkType: hard -"cjs-module-lexer@npm:^1.0.0, cjs-module-lexer@npm:^1.2.3": +"cjs-module-lexer@npm:^1.0.0, cjs-module-lexer@npm:^1.2.2, cjs-module-lexer@npm:^1.2.3": version: 1.4.1 resolution: "cjs-module-lexer@npm:1.4.1" checksum: 10/6e830a1e00a34d416949bbc1924f3e8da65cef4a6a09e2b7fa35722e2d1c34bf378d3baca987b698d1cbc3eb83e44b044039b4e82755c96f30e0f03d1d227637 @@ -18340,7 +18795,7 @@ __metadata: languageName: node linkType: hard -"debug@npm:4, debug@npm:^4.0.0, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.4, debug@npm:^4.3.7": +"debug@npm:4, debug@npm:^4.0.0, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.4, debug@npm:^4.3.5, debug@npm:^4.3.7": version: 4.3.7 resolution: "debug@npm:4.3.7" dependencies: @@ -23647,6 +24102,18 @@ __metadata: languageName: node linkType: hard +"import-in-the-middle@npm:^1.8.1": + version: 1.11.2 + resolution: "import-in-the-middle@npm:1.11.2" + dependencies: + acorn: "npm:^8.8.2" + acorn-import-attributes: "npm:^1.9.5" + cjs-module-lexer: "npm:^1.2.2" + module-details-from-path: "npm:^1.0.3" + checksum: 10/ebd1aaba4441e54db124670e13038127f5283b686d83276dc004cd9d3bb1747e63ac37935c3c58885b52aedf48e669093d24ffe4b5849adef744d79ee67445ad + languageName: node + linkType: hard + "import-lazy@npm:^2.1.0": version: 2.1.0 resolution: "import-lazy@npm:2.1.0" @@ -25026,7 +25493,7 @@ __metadata: languageName: node linkType: hard -"jest-cli@npm:^29.7.0": +"jest-cli@npm:^29.6.4, jest-cli@npm:^29.7.0": version: 29.7.0 resolution: "jest-cli@npm:29.7.0" dependencies: @@ -25501,6 +25968,25 @@ __metadata: languageName: node linkType: hard +"jest@npm:~29.6.4": + version: 29.6.4 + resolution: "jest@npm:29.6.4" + dependencies: + "@jest/core": "npm:^29.6.4" + "@jest/types": "npm:^29.6.3" + import-local: "npm:^3.0.2" + jest-cli: "npm:^29.6.4" + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + bin: + jest: bin/jest.js + checksum: 10/d747e293bd63f583e7978ac0693ab7a019812fa44b9bf3b3fe20e75e8a343bcd8251d292326d73151dc0b8a2b5a974d878b3aa9ffb146dfa7980553f64a35b43 + languageName: node + linkType: hard + "jiti@npm:^1.20.0": version: 1.21.6 resolution: "jiti@npm:1.21.6" @@ -26420,6 +26906,13 @@ __metadata: languageName: node linkType: hard +"lodash.camelcase@npm:^4.3.0": + version: 4.3.0 + resolution: "lodash.camelcase@npm:4.3.0" + checksum: 10/c301cc379310441dc73cd6cebeb91fb254bea74e6ad3027f9346fc43b4174385153df420ffa521654e502fd34c40ef69ca4e7d40ee7129a99e06f306032bfc65 + languageName: node + linkType: hard + "lodash.clonedeep@npm:^4.5.0": version: 4.5.0 resolution: "lodash.clonedeep@npm:4.5.0" @@ -26674,6 +27167,13 @@ __metadata: languageName: node linkType: hard +"long@npm:^5.0.0": + version: 5.2.3 + resolution: "long@npm:5.2.3" + checksum: 10/9167ec6947a825b827c30da169a7384eec6c0c9ec2f0b9c74da2e93d81159bbe39fb09c3f13dae9721d4b807ccfa09797a7dd1012f5d478e3e33ca3c78b608e6 + languageName: node + linkType: hard + "long@npm:~3": version: 3.2.0 resolution: "long@npm:3.2.0" @@ -27852,6 +28352,13 @@ __metadata: languageName: node linkType: hard +"module-details-from-path@npm:^1.0.3": + version: 1.0.3 + resolution: "module-details-from-path@npm:1.0.3" + checksum: 10/f93226e9154fc8cb91f4609b639167ec7ad9155b30be4924d9717656648a3ae5f181d4e2338434d4c5afc7b5f4c10dd3b64109e5b89a4be70b20a25ba3573d54 + languageName: node + linkType: hard + "module-not-found-error@npm:^1.0.1": version: 1.0.1 resolution: "module-not-found-error@npm:1.0.1" @@ -31359,6 +31866,26 @@ __metadata: languageName: node linkType: hard +"protobufjs@npm:^7.2.5, protobufjs@npm:^7.3.0": + version: 7.4.0 + resolution: "protobufjs@npm:7.4.0" + dependencies: + "@protobufjs/aspromise": "npm:^1.1.2" + "@protobufjs/base64": "npm:^1.1.2" + "@protobufjs/codegen": "npm:^2.0.4" + "@protobufjs/eventemitter": "npm:^1.1.0" + "@protobufjs/fetch": "npm:^1.1.0" + "@protobufjs/float": "npm:^1.0.2" + "@protobufjs/inquire": "npm:^1.1.0" + "@protobufjs/path": "npm:^1.1.2" + "@protobufjs/pool": "npm:^1.1.0" + "@protobufjs/utf8": "npm:^1.1.0" + "@types/node": "npm:>=13.7.0" + long: "npm:^5.0.0" + checksum: 10/408423506610f70858d7593632f4a6aa4f05796c90fd632be9b9252457c795acc71aa6d3b54bb7f48a890141728fee4ca3906723ccea6c202ad71f21b3879b8b + languageName: node + linkType: hard + "proxy-addr@npm:~2.0.7": version: 2.0.7 resolution: "proxy-addr@npm:2.0.7" @@ -32919,6 +33446,17 @@ __metadata: languageName: node linkType: hard +"require-in-the-middle@npm:^7.1.1": + version: 7.4.0 + resolution: "require-in-the-middle@npm:7.4.0" + dependencies: + debug: "npm:^4.3.5" + module-details-from-path: "npm:^1.0.3" + resolve: "npm:^1.22.8" + checksum: 10/0ca30ad6a6183423f38599709fc8a670682db85b581a66cb31ea31342e8ba2ce7dca44ee29e8cfe4fb59ffcb0c2b0f9b77d44a10cdc7535c7c2907028e53afbf + languageName: node + linkType: hard + "require-main-filename@npm:^2.0.0": version: 2.0.0 resolution: "require-main-filename@npm:2.0.0" @@ -33656,7 +34194,7 @@ __metadata: languageName: node linkType: hard -"semver@npm:^7.3.2, semver@npm:^7.3.4, semver@npm:^7.3.5, semver@npm:^7.3.7, semver@npm:^7.5.3, semver@npm:^7.5.4, semver@npm:^7.6.2, semver@npm:^7.6.3": +"semver@npm:^7.3.2, semver@npm:^7.3.4, semver@npm:^7.3.5, semver@npm:^7.3.7, semver@npm:^7.5.2, semver@npm:^7.5.3, semver@npm:^7.5.4, semver@npm:^7.6.2, semver@npm:^7.6.3": version: 7.6.3 resolution: "semver@npm:7.6.3" bin: @@ -33999,6 +34537,13 @@ __metadata: languageName: node linkType: hard +"shimmer@npm:^1.2.1": + version: 1.2.1 + resolution: "shimmer@npm:1.2.1" + checksum: 10/aa0d6252ad1c682a4fdfda69e541be987f7a265ac7b00b1208e5e48cc68dc55f293955346ea4c71a169b7324b82c70f8400b3d3d2d60b2a7519f0a3522423250 + languageName: node + linkType: hard + "side-channel@npm:^1.0.4, side-channel@npm:^1.0.6": version: 1.0.6 resolution: "side-channel@npm:1.0.6" @@ -36261,7 +36806,7 @@ __metadata: languageName: node linkType: hard -"ts-jest@npm:~29.1.5": +"ts-jest@npm:~29.1.1, ts-jest@npm:~29.1.5": version: 29.1.5 resolution: "ts-jest@npm:29.1.5" dependencies: @@ -38976,7 +39521,7 @@ __metadata: languageName: node linkType: hard -"yargs@npm:^17.3.1, yargs@npm:^17.7.1": +"yargs@npm:^17.3.1, yargs@npm:^17.7.1, yargs@npm:^17.7.2": version: 17.7.2 resolution: "yargs@npm:17.7.2" dependencies: