feat: Export messages downloading as PDF (#36259)

This commit is contained in:
Tiago Evangelista Pinto 2025-06-20 10:36:46 -03:00 committed by GitHub
parent c992f7d78c
commit 4ec3b92df6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 244 additions and 129 deletions

View File

@ -0,0 +1,6 @@
---
"@rocket.chat/meteor": patch
"@rocket.chat/i18n": patch
---
Introduces PDF file as an export type for room messages

View File

@ -250,4 +250,5 @@ export const permissions = [
{ _id: 'view-moderation-console', roles: ['admin'] },
{ _id: 'manage-moderation-actions', roles: ['admin'] },
{ _id: 'bypass-time-limit-edit-and-delete', roles: ['bot', 'app'] },
{ _id: 'export-messages-as-pdf', roles: ['admin', 'user'] },
];

View File

@ -15,11 +15,13 @@ import {
Callout,
} from '@rocket.chat/fuselage';
import { useAutoFocus } from '@rocket.chat/fuselage-hooks';
import { usePermission } from '@rocket.chat/ui-contexts';
import { useContext, useEffect, useId, useMemo } from 'react';
import { Controller, useForm } from 'react-hook-form';
import { useTranslation } from 'react-i18next';
import { useDownloadExportMutation } from './useDownloadExportMutation';
import { useExportMessagesAsPDFMutation } from './useExportMessagesAsPDFMutation';
import { useRoomExportMutation } from './useRoomExportMutation';
import { validateEmail } from '../../../../../lib/emailValidator';
import {
@ -41,7 +43,7 @@ export type ExportMessagesFormValues = {
type: 'email' | 'file' | 'download';
dateFrom: string;
dateTo: string;
format: 'html' | 'json';
format: 'html' | 'json' | 'pdf';
toUsers: string[];
additionalEmails: string;
messagesCount: number;
@ -51,6 +53,7 @@ export type ExportMessagesFormValues = {
const ExportMessages = () => {
const { t } = useTranslation();
const { closeTab } = useRoomToolbox();
const pfdExportPermission = usePermission('export-messages-as-pdf');
const formFocus = useAutoFocus<HTMLFormElement>();
const room = useRoom();
const isE2ERoom = room.encrypted;
@ -92,13 +95,21 @@ const ExportMessages = () => {
[t],
);
const outputOptions = useMemo<SelectOption[]>(
() => [
const outputOptions = useMemo<SelectOption[]>(() => {
const options: SelectOption[] = [
['html', t('HTML')],
['json', t('JSON')],
],
[t],
);
];
if (pfdExportPermission) {
options.push(['pdf', t('PDF')]);
}
return options;
}, [t, pfdExportPermission]);
// Remove HTML from download options
const downloadOutputOptions = outputOptions.slice(1);
const roomExportMutation = useRoomExportMutation();
const downloadExportMutation = useDownloadExportMutation();
@ -108,6 +119,12 @@ const ExportMessages = () => {
const { type, toUsers } = watch();
useEffect(() => {
if (type === 'email') {
setValue('format', 'html');
}
}, [type, setValue]);
useEffect(() => {
if (type !== 'file') {
selectedMessageStore.setIsSelecting(true);
@ -119,24 +136,24 @@ const ExportMessages = () => {
}, [type, selectedMessageStore]);
useEffect(() => {
if (type === 'email') {
setValue('format', 'html');
}
if (type === 'download') {
setValue('format', 'json');
}
setValue('messagesCount', messageCount, { shouldDirty: true });
}, [type, setValue, messageCount]);
}, [messageCount, setValue]);
const { mutate: exportAsPDF } = useExportMessagesAsPDFMutation();
const handleExport = async ({ type, toUsers, dateFrom, dateTo, format, subject, additionalEmails }: ExportMessagesFormValues) => {
const messages = selectedMessageStore.getSelectedMessages();
if (type === 'download') {
return downloadExportMutation.mutateAsync({
mids: messages,
});
if (format === 'pdf') {
return exportAsPDF(messages);
}
if (format === 'json') {
return downloadExportMutation.mutateAsync({
mids: messages,
});
}
}
if (type === 'file') {
@ -145,7 +162,7 @@ const ExportMessages = () => {
type: 'file',
...(dateFrom && { dateFrom }),
...(dateTo && { dateTo }),
format,
format: format as 'html' | 'json',
});
}
@ -207,9 +224,9 @@ const ExportMessages = () => {
<Select
{...field}
id={formatField}
disabled={type === 'email' || type === 'download'}
disabled={type === 'email'}
placeholder={t('Format')}
options={outputOptions}
options={type === 'download' ? downloadOutputOptions : outputOptions}
/>
)}
/>

View File

@ -0,0 +1,84 @@
import { Document, Page, pdf, Text, View } from '@react-pdf/renderer';
import type { IMessage } from '@rocket.chat/core-typings';
import { escapeHTML } from '@rocket.chat/string-helpers';
import { useSetting } from '@rocket.chat/ui-contexts';
import { useMutation } from '@tanstack/react-query';
import { useTranslation } from 'react-i18next';
import { Messages } from '../../../../../app/models/client';
import { MessageTypes } from '../../../../../app/ui-utils/lib/MessageTypes';
import { useFormatDateAndTime } from '../../../../hooks/useFormatDateAndTime';
export const useExportMessagesAsPDFMutation = () => {
const { t } = useTranslation();
const chatopsUsername = useSetting('Chatops_Username');
const formatDateAndTime = useFormatDateAndTime();
return useMutation({
mutationFn: async (messageIds: IMessage['_id'][]) => {
const parseMessage = (msg: IMessage) => {
const messageType = MessageTypes.getType(msg);
if (messageType) {
if (messageType.template) {
// Render message
return;
}
if (messageType.message) {
const data = (typeof messageType.data === 'function' && messageType.data(msg)) || {};
return t(messageType.message, data);
}
}
if (msg.u && msg.u.username === chatopsUsername) {
msg.html = msg.msg;
return msg.html;
}
msg.html = msg.msg;
if (msg.html.trim() !== '') {
msg.html = escapeHTML(msg.html);
}
return msg.html;
};
const messages = Messages.state.filter((record) => messageIds.includes(record._id)).sort((a, b) => a.ts.getTime() - b.ts.getTime());
const jsx = (
<Document>
<Page size='A4'>
<View style={{ margin: 10 }}>
{messages.map((message) => {
const dateTime = formatDateAndTime(message.ts);
return (
<Text key={message._id} style={{ marginBottom: 5 }}>
<Text style={{ color: '#555', fontSize: 14 }}>{message.u.username}</Text>{' '}
<Text style={{ color: '#aaa', fontSize: 12 }}>{dateTime}</Text>
<Text>{'\n'}</Text>
{parseMessage(message)}
</Text>
);
})}
</View>
</Page>
</Document>
);
const instance = pdf();
const callback = async () => {
const link = document.createElement('a');
link.href = URL.createObjectURL(await instance.toBlob());
link.download = `exportedMessages-${new Date().toISOString()}.pdf`;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
URL.revokeObjectURL(link.href);
};
try {
instance.on('change', callback);
instance.updateContainer(jsx);
} finally {
instance.removeListener('change', callback);
}
},
});
};

View File

@ -24,5 +24,6 @@ import './v315';
import './v316';
import './v317';
import './v318';
import './v319';
export * from './xrun';

View File

@ -0,0 +1,9 @@
import { upsertPermissions } from '../../../app/authorization/server/functions/upsertPermissions';
import { addMigration } from '../../lib/migrations';
addMigration({
version: 319,
async up() {
await upsertPermissions();
},
});

View File

@ -39,6 +39,8 @@ test.describe.serial('export-messages', () => {
await poHomeChannel.tabs.kebab.click({ force: true });
await poHomeChannel.tabs.btnExportMessages.click();
await expect(poHomeChannel.btnContextualbarClose).toBeVisible();
await poHomeChannel.content.getMessageByText('hello world').click();
await poHomeChannel.tabs.exportMessages.btnSend.click();

View File

@ -59,6 +59,19 @@
"yarn": "1.22.18"
},
"resolutions": {
"@react-pdf/fns": "2.0.1",
"@react-pdf/font": "2.3.7",
"@react-pdf/image": "2.2.2",
"@react-pdf/layout": "3.6.3",
"@react-pdf/pdfkit": "3.0.2",
"@react-pdf/png-js": "2.2.0",
"@react-pdf/primitives": "3.0.1",
"@react-pdf/render": "3.2.7",
"@react-pdf/renderer": "3.1.14",
"@react-pdf/stylesheet": "4.1.8",
"@react-pdf/textkit": "4.2.0",
"@react-pdf/types": "2.3.4",
"@react-pdf/yoga": "4.1.2",
"minimist": "1.2.6",
"adm-zip": "0.5.9",
"underscore": "1.13.7",

View File

@ -2005,6 +2005,7 @@
"Export": "Export",
"Export_Messages": "Export messages",
"Export_My_Data": "Export My Data (JSON)",
"export-messages-as-pdf": "Export messages as PDF",
"Export_as_PDF": "Export as PDF",
"Export_as_file": "Export as file",
"Export_conversation_transcript_as_PDF": "Export conversation transcript as PDF",

197
yarn.lock
View File

@ -7076,163 +7076,169 @@ __metadata:
languageName: node
linkType: hard
"@react-pdf/fns@npm:2.2.1":
version: 2.2.1
resolution: "@react-pdf/fns@npm:2.2.1"
"@react-pdf/fns@npm:2.0.1":
version: 2.0.1
resolution: "@react-pdf/fns@npm:2.0.1"
dependencies:
"@babel/runtime": "npm:^7.20.13"
checksum: 10/e8da79803ceb74114260ac349ae51fdd40a308dd8a1023fb86b111e86bee79a4539c700fc0c5797c363f977f1acf5171c302bf621da8ec5fc0e0d3c684dfe5b9
checksum: 10/7e5a1ebc14164635af0ecfe80f90911db2f3c71f99c67590f4de87122555788abbe01ef02a9ddc7d95c513c0c9d77d2634ef68a11853dc42e629e7f23ed562d0
languageName: node
linkType: hard
"@react-pdf/font@npm:^2.5.2":
version: 2.5.2
resolution: "@react-pdf/font@npm:2.5.2"
"@react-pdf/font@npm:2.3.7":
version: 2.3.7
resolution: "@react-pdf/font@npm:2.3.7"
dependencies:
"@babel/runtime": "npm:^7.20.13"
"@react-pdf/types": "npm:^2.6.0"
"@react-pdf/types": "npm:^2.3.4"
cross-fetch: "npm:^3.1.5"
fontkit: "npm:^2.0.2"
is-url: "npm:^1.2.4"
checksum: 10/090497049faa1683dd167c3a543788f01fe5234ec712bbbda12589b79440427a841a4ceb146e4886681163cce29cca0d3d6e24ff26bfc749100e7862b6040cd6
checksum: 10/7f7c51fcaa835dc6c5a77326dc974c90de38f6ccd249df7e210b10d8a249e3814a7a5e196dec0b9b10b958759e02c7a69c6d6e1b15d6c5cd6093237258640a18
languageName: node
linkType: hard
"@react-pdf/image@npm:^2.3.6":
version: 2.3.6
resolution: "@react-pdf/image@npm:2.3.6"
"@react-pdf/image@npm:2.2.2":
version: 2.2.2
resolution: "@react-pdf/image@npm:2.2.2"
dependencies:
"@babel/runtime": "npm:^7.20.13"
"@react-pdf/png-js": "npm:^2.3.1"
"@react-pdf/png-js": "npm:^2.2.0"
cross-fetch: "npm:^3.1.5"
jay-peg: "npm:^1.0.2"
checksum: 10/3b1a686072bffda143cf22e0b536f3c7cda0972341897aa80dadce8254f2c35911e52a8202386d17282594b84cecf31622317606acec77aa7aca71d8634b6d6f
checksum: 10/7167c82afff28a84141a7662c786280390c2f42f6037982fc9d23cf3fc9e2aae9ff2ce62d0c88b5284cc7dda2b17506a14e052bec57fda80143013e2321c452b
languageName: node
linkType: hard
"@react-pdf/layout@npm:^3.13.0":
version: 3.13.0
resolution: "@react-pdf/layout@npm:3.13.0"
"@react-pdf/layout@npm:3.6.3":
version: 3.6.3
resolution: "@react-pdf/layout@npm:3.6.3"
dependencies:
"@babel/runtime": "npm:^7.20.13"
"@react-pdf/fns": "npm:2.2.1"
"@react-pdf/image": "npm:^2.3.6"
"@react-pdf/pdfkit": "npm:^3.2.0"
"@react-pdf/primitives": "npm:^3.1.1"
"@react-pdf/stylesheet": "npm:^4.3.0"
"@react-pdf/textkit": "npm:^4.4.1"
"@react-pdf/types": "npm:^2.6.0"
"@react-pdf/fns": "npm:2.0.1"
"@react-pdf/image": "npm:^2.2.2"
"@react-pdf/pdfkit": "npm:^3.0.2"
"@react-pdf/primitives": "npm:^3.0.0"
"@react-pdf/stylesheet": "npm:^4.1.8"
"@react-pdf/textkit": "npm:^4.2.0"
"@react-pdf/types": "npm:^2.3.4"
"@react-pdf/yoga": "npm:^4.1.2"
cross-fetch: "npm:^3.1.5"
emoji-regex: "npm:^10.3.0"
emoji-regex: "npm:^10.2.1"
queue: "npm:^6.0.1"
yoga-layout: "npm:^2.0.1"
checksum: 10/4d497c5cf92b945d49a56d3db1afb5f1c07bcbc11d617a96c64e008e6dfdbfa956d885ef7a5be98ac7e900768cfd88d8c5950cee6f2c80eb2bc7723d54a97d06
checksum: 10/fac7063d972708f36f149d90fa8b01b645652312376c841f2bef3d33eef1258f75a375e49375d969f7998691817dd58eb1709060f62a474922a43923d37d6b64
languageName: node
linkType: hard
"@react-pdf/pdfkit@npm:^3.2.0":
version: 3.2.0
resolution: "@react-pdf/pdfkit@npm:3.2.0"
"@react-pdf/pdfkit@npm:3.0.2":
version: 3.0.2
resolution: "@react-pdf/pdfkit@npm:3.0.2"
dependencies:
"@babel/runtime": "npm:^7.20.13"
"@react-pdf/png-js": "npm:^2.3.1"
"@react-pdf/png-js": "npm:^2.2.0"
browserify-zlib: "npm:^0.2.0"
crypto-js: "npm:^4.2.0"
crypto-js: "npm:^4.0.0"
fontkit: "npm:^2.0.2"
jay-peg: "npm:^1.0.2"
vite-compatible-readable-stream: "npm:^3.6.1"
checksum: 10/5fa01f1365b5df119e5bd63f3fbbae99c67926fec1595376907551ce2c16a6cad0231492bf9c21b2b57ce4a0bc5f52fafae0fee008419fdfc4e6d91932479259
checksum: 10/0e834d18aee4b15b304cc46ab1dda7c68f4504629b2cdd9f8c3f2874ac4ceecaebdc1b7d7b865ec910cb32ca6f58864e88d94916d2000fb676c4082928c9e408
languageName: node
linkType: hard
"@react-pdf/png-js@npm:^2.3.1":
version: 2.3.1
resolution: "@react-pdf/png-js@npm:2.3.1"
"@react-pdf/png-js@npm:2.2.0":
version: 2.2.0
resolution: "@react-pdf/png-js@npm:2.2.0"
dependencies:
browserify-zlib: "npm:^0.2.0"
checksum: 10/0f4de5c1654ac8d12a44b36d2316fd9c54b1a7647a00953b268610848778a83437dce0e5d0c21902b63a2e2e8d4b4503d689f765316ad1dd679d8c92c6c228f2
checksum: 10/b1f2e26ae8388ec8bfecca7a7479303d960bca68428ae4102c48d5d046a94e4cab84c638ebdeb2fc92362f8b51e5f3ac124942977fcefa0e2fcb43c1b431f591
languageName: node
linkType: hard
"@react-pdf/primitives@npm:^3.1.1":
version: 3.1.1
resolution: "@react-pdf/primitives@npm:3.1.1"
checksum: 10/a52c0cfff74d29d36e2e4c1c2b8935faf2f13bbe3800901e93354ea044385d8716166e45f3a49bb729e6d9944d7a8239056f5af80b345cb2984e245b2e719c1d
"@react-pdf/primitives@npm:3.0.1":
version: 3.0.1
resolution: "@react-pdf/primitives@npm:3.0.1"
checksum: 10/c4bea5c6c61a2bbfdc462385def8ec9a36b60320b2008643f694de4e587dd05058ff78c98aa8a5d66bd472bbe09c93a477b15fefd64db58f6e64c122adabbdf1
languageName: node
linkType: hard
"@react-pdf/render@npm:^3.5.0":
version: 3.5.0
resolution: "@react-pdf/render@npm:3.5.0"
"@react-pdf/render@npm:3.2.7":
version: 3.2.7
resolution: "@react-pdf/render@npm:3.2.7"
dependencies:
"@babel/runtime": "npm:^7.20.13"
"@react-pdf/fns": "npm:2.2.1"
"@react-pdf/primitives": "npm:^3.1.1"
"@react-pdf/textkit": "npm:^4.4.1"
"@react-pdf/types": "npm:^2.6.0"
"@react-pdf/fns": "npm:2.0.1"
"@react-pdf/primitives": "npm:^3.0.0"
"@react-pdf/textkit": "npm:^4.2.0"
"@react-pdf/types": "npm:^2.3.4"
abs-svg-path: "npm:^0.1.1"
color-string: "npm:^1.9.1"
color-string: "npm:^1.5.3"
normalize-svg-path: "npm:^1.1.0"
parse-svg-path: "npm:^0.1.2"
svg-arc-to-cubic-bezier: "npm:^3.2.0"
checksum: 10/0fa434449e69fa5f64dfa6677bd1f14e11e20940640a6847b7435db17698cd4ef1bc8a49e71f221cd6a217032535190c9895a9bdbf10dda16d9a114dcd1f84f2
checksum: 10/996dde60853ce16c22ffe9cf1a804a4154ac3122f61af749c9df9ae99b6b16c5bc4484aeb3269661f0ac4a06cf3703495e0112ffccbf509bcd87858601f3d486
languageName: node
linkType: hard
"@react-pdf/renderer@npm:^3.4.5":
version: 3.4.5
resolution: "@react-pdf/renderer@npm:3.4.5"
"@react-pdf/renderer@npm:3.1.14":
version: 3.1.14
resolution: "@react-pdf/renderer@npm:3.1.14"
dependencies:
"@babel/runtime": "npm:^7.20.13"
"@react-pdf/font": "npm:^2.5.2"
"@react-pdf/layout": "npm:^3.13.0"
"@react-pdf/pdfkit": "npm:^3.2.0"
"@react-pdf/primitives": "npm:^3.1.1"
"@react-pdf/render": "npm:^3.5.0"
"@react-pdf/types": "npm:^2.6.0"
"@react-pdf/font": "npm:^2.3.7"
"@react-pdf/layout": "npm:^3.6.3"
"@react-pdf/pdfkit": "npm:^3.0.2"
"@react-pdf/primitives": "npm:^3.0.0"
"@react-pdf/render": "npm:^3.2.7"
"@react-pdf/types": "npm:^2.3.4"
events: "npm:^3.3.0"
object-assign: "npm:^4.1.1"
prop-types: "npm:^15.6.2"
queue: "npm:^6.0.1"
scheduler: "npm:^0.17.0"
peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0
checksum: 10/eb940e2d05dd986fad827f4cde956a556d4c60a9ab9e182b993c13cab9962e1959804c2cd247832d335bab27b2bb7db3d9e253a7f6f18c526c1422b5b44b09c3
react: ^16.8.6 || ^17.0.0 || ^18.0.0
checksum: 10/c3e70f9a34ced9466c4665fa4f5ee4df8ef7ced262fda15f05db7cf9ea49535268a0b1943b5964f57d89e6f93f5d586ac68e50429e3774868fc8626b8f71a34e
languageName: node
linkType: hard
"@react-pdf/stylesheet@npm:^4.3.0":
version: 4.3.0
resolution: "@react-pdf/stylesheet@npm:4.3.0"
"@react-pdf/stylesheet@npm:4.1.8":
version: 4.1.8
resolution: "@react-pdf/stylesheet@npm:4.1.8"
dependencies:
"@babel/runtime": "npm:^7.20.13"
"@react-pdf/fns": "npm:2.2.1"
"@react-pdf/types": "npm:^2.6.0"
color-string: "npm:^1.9.1"
"@react-pdf/fns": "npm:2.0.1"
"@react-pdf/types": "npm:^2.3.4"
color-string: "npm:^1.5.3"
hsl-to-hex: "npm:^1.0.0"
media-engine: "npm:^1.0.3"
postcss-value-parser: "npm:^4.1.0"
checksum: 10/ca012d7f63676a85e11df00da814a75fd50d0b731b7bc10896e64cc5740df02080c40d21ea903ad9fd071a431987ce53ee2779f22b26ae36e72bbde283f41a37
checksum: 10/3ef2519805aecc52f4a64b2866186ce5fb96d8498e29cd7e451d5d98ae1c67309d84d54b80f1f7c60629f949beb4c44e9219cafe61ddbbccf9eeb442fc16437d
languageName: node
linkType: hard
"@react-pdf/textkit@npm:^4.4.1":
version: 4.4.1
resolution: "@react-pdf/textkit@npm:4.4.1"
"@react-pdf/textkit@npm:4.2.0":
version: 4.2.0
resolution: "@react-pdf/textkit@npm:4.2.0"
dependencies:
"@babel/runtime": "npm:^7.20.13"
"@react-pdf/fns": "npm:2.2.1"
bidi-js: "npm:^1.0.2"
"@react-pdf/fns": "npm:2.0.1"
hyphen: "npm:^1.6.4"
unicode-properties: "npm:^1.4.1"
checksum: 10/36c30514034367398382d822c2422fb64ea26f2350230073e9ff461c2a27d237d279b9264140d8399d3d92a5c3167bfd258f91eca475fa45a67999006ccc3139
checksum: 10/4471a05fe7d1a82e5406cf5fa99e224eacdecdf29a0a140ffc960b68b58f97f595b0449cf20abf7d5b02ab27b04a2a871c218ccd1e819cf83118ef428c853682
languageName: node
linkType: hard
"@react-pdf/types@npm:^2.6.0":
version: 2.7.0
resolution: "@react-pdf/types@npm:2.7.0"
checksum: 10/aabefb525363fd8f0e62ca943fec14090cd50e2bf37a359cafb4f26692ea529c680b64512d52d528bc4cf7d96e909fd6b3136115369d041880e792c95a0a8752
"@react-pdf/types@npm:2.3.4":
version: 2.3.4
resolution: "@react-pdf/types@npm:2.3.4"
checksum: 10/91027d1a1807f287d9537ba8ad88113cb45a6de352fd0388ebd8fe9d93aa6f60f73a3fc9d6f1c0e0f20d3cd397c968e8661e466be53432cd97aa74e4b4ae80c0
languageName: node
linkType: hard
"@react-pdf/yoga@npm:4.1.2":
version: 4.1.2
resolution: "@react-pdf/yoga@npm:4.1.2"
dependencies:
"@babel/runtime": "npm:^7.20.13"
checksum: 10/684efc153776f68ae6f88ed2bd8ba18d0e0e28270ebc5388a57e63f5cbf0fb6de6bc0f825be5e17103e9c1036787d09e495b990ba2694c6b298f9e2f3591b9ea
languageName: node
linkType: hard
@ -16771,15 +16777,6 @@ __metadata:
languageName: node
linkType: hard
"bidi-js@npm:^1.0.2":
version: 1.0.3
resolution: "bidi-js@npm:1.0.3"
dependencies:
require-from-string: "npm:^2.0.2"
checksum: 10/c4341c7a98797efe3d186cd99d6f97e9030a4f959794ca200ef2ec0a678483a916335bba6c2c0608a21d04a221288a31c9fd0faa0cd9b3903b93594b42466a6a
languageName: node
linkType: hard
"big-integer@npm:^1.6.51":
version: 1.6.51
resolution: "big-integer@npm:1.6.51"
@ -18302,7 +18299,7 @@ __metadata:
languageName: node
linkType: hard
"color-string@npm:^1.6.0, color-string@npm:^1.9.0, color-string@npm:^1.9.1":
"color-string@npm:^1.5.3, color-string@npm:^1.6.0, color-string@npm:^1.9.0":
version: 1.9.1
resolution: "color-string@npm:1.9.1"
dependencies:
@ -18931,7 +18928,7 @@ __metadata:
languageName: node
linkType: hard
"crypto-js@npm:^4.2.0":
"crypto-js@npm:^4.0.0":
version: 4.2.0
resolution: "crypto-js@npm:4.2.0"
checksum: 10/c7bcc56a6e01c3c397e95aa4a74e4241321f04677f9a618a8f48a63b5781617248afb9adb0629824792e7ec20ca0d4241a49b6b2938ae6f973ec4efc5c53c924
@ -20609,7 +20606,7 @@ __metadata:
languageName: node
linkType: hard
"emoji-regex@npm:^10.3.0":
"emoji-regex@npm:^10.2.1":
version: 10.4.0
resolution: "emoji-regex@npm:10.4.0"
checksum: 10/76bb92c5bcf0b6980d37e535156231e4a9d0aa6ab3b9f5eabf7690231d5aa5d5b8e516f36e6804cbdd0f1c23dfef2a60c40ab7bb8aedd890584281a565b97c50
@ -26141,15 +26138,6 @@ __metadata:
languageName: node
linkType: hard
"jay-peg@npm:^1.0.2":
version: 1.1.0
resolution: "jay-peg@npm:1.1.0"
dependencies:
restructure: "npm:^3.0.0"
checksum: 10/11abb5fe63974900e3bb2bfa05b305557d807fe516f63e235a25fd32c739141d3c4b94afc32074a661c7313f95229888720c92de7078824930f2a4c991cdd3ee
languageName: node
linkType: hard
"jest-axe@npm:~10.0.0":
version: 10.0.0
resolution: "jest-axe@npm:10.0.0"
@ -40089,13 +40077,6 @@ __metadata:
languageName: node
linkType: hard
"yoga-layout@npm:^2.0.1":
version: 2.0.1
resolution: "yoga-layout@npm:2.0.1"
checksum: 10/f8f6bb2219b6225105f57d88001eeb0909b7e205a6f8e6acae79ef09f1233da4210bc5185c0997f2f0ab42bc21d9fb723c90d2b0b05d078ba542a3a2f0ae6e92
languageName: node
linkType: hard
"zip-stream@npm:^6.0.1":
version: 6.0.1
resolution: "zip-stream@npm:6.0.1"