Enforce email templating (#3355)

* Add react-email

* WIP

* Fix import error

* Rename services

* Update logging

* Update email template

* Update email template

* Add Base Email template

* Move to proper place

* Remove test files

* Update logo

* Add email theme

* Revert "Remove test files"

This reverts commit fe062dd051.

* Add email theme 2

* Revert "Revert "Remove test files""

This reverts commit 6c6471273a.

* Revert "Revert "Revert "Remove test files"""

This reverts commit f851333c24.

* Revert "Revert "Revert "Revert "Remove test files""""

This reverts commit 7838e19e88.

* Fix theme
This commit is contained in:
martmull 2024-01-11 20:29:20 +01:00 committed by GitHub
parent e2bdf0ce45
commit b3d9bed91d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 531 additions and 16 deletions

View File

@ -39,6 +39,8 @@
"@octokit/graphql": "^7.0.2",
"@ptc-org/nestjs-query-core": "^4.2.0",
"@ptc-org/nestjs-query-typeorm": "4.2.1-alpha.2",
"@react-email/components": "0.0.12",
"@react-email/render": "0.0.10",
"@sentry/node": "^7.66.0",
"@sentry/profiling-node": "^1.2.6",
"@sentry/react": "^7.88.0",

View File

@ -0,0 +1,48 @@
const grayScale = {
gray100: '#000000',
gray90: '#141414',
gray85: '#171717',
gray80: '#1b1b1b',
gray75: '#1d1d1d',
gray70: '#222222',
gray65: '#292929',
gray60: '#333333',
gray55: '#4c4c4c',
gray50: '#666666',
gray45: '#818181',
gray40: '#999999',
gray35: '#b3b3b3',
gray30: '#cccccc',
gray25: '#d6d6d6',
gray20: '#ebebeb',
gray15: '#f1f1f1',
gray10: '#fcfcfc',
gray0: '#ffffff',
};
export const emailTheme = {
font: {
colors: {
highlighted: grayScale.gray60,
primary: grayScale.gray50,
inverted: grayScale.gray0,
},
weight: {
regular: 400,
bold: 600,
},
size: {
md: '13px',
lg: '16px',
},
},
background: {
colors: { highlight: grayScale.gray15 },
radialGradient: `radial-gradient(50% 62.62% at 50% 0%, #505050 0%, ${grayScale.gray60} 100%)`,
radialGradientHover: `radial-gradient(76.32% 95.59% at 50% 0%, #505050 0%, ${grayScale.gray60} 100%)`,
transparent: {
medium: 'rgba(0, 0, 0, 0.08)',
light: 'rgba(0, 0, 0, 0.04)',
},
},
};

View File

@ -0,0 +1,17 @@
import * as React from 'react';
import { Container, Html } from '@react-email/components';
import { BaseHead } from 'src/emails/components/BaseHead';
import { Logo } from 'src/emails/components/Logo';
export const BaseEmail = ({ children }) => {
return (
<Html lang="en">
<BaseHead />
<Container width={290}>
<Logo />
{children}
</Container>
</Html>
);
};

View File

@ -0,0 +1,22 @@
import { Font, Head } from '@react-email/components';
import * as React from 'react';
import { emailTheme } from 'src/emails/common-style';
export const BaseHead = () => {
return (
<Head>
<title>Twenty email</title>
<Font
fontFamily="Inter"
fallbackFontFamily="sans-serif"
webFont={{
url: 'https://fonts.googleapis.com/css2?family=Inter:wght@100;200;300;400;500;600;700;800;900&display=swap',
format: 'woff2',
}}
fontStyle="normal"
fontWeight={emailTheme.font.weight.regular}
/>
</Head>
);
};

View File

@ -0,0 +1,23 @@
import { Button } from '@react-email/button';
import * as React from 'react';
import { emailTheme } from 'src/emails/common-style';
const callToActionStyle = {
display: 'flex',
padding: '8px 32px',
borderRadius: '8px',
border: `1px solid ${emailTheme.background.transparent.light}`,
background: emailTheme.background.radialGradient,
boxShadow: `0px 2px 4px 0px ${emailTheme.background.transparent.light}, 0px 0px 4px 0px ${emailTheme.background.transparent.medium}`,
color: emailTheme.font.colors.inverted,
fontSize: emailTheme.font.size.md,
fontWeight: emailTheme.font.weight.bold,
};
export const CallToAction = ({ value, href }) => {
return (
<Button href={href} style={callToActionStyle}>
{value}
</Button>
);
};

View File

@ -0,0 +1,30 @@
import * as React from 'react';
import { Row } from '@react-email/row';
import { Text } from '@react-email/text';
import { Column } from '@react-email/components';
import { emailTheme } from 'src/emails/common-style';
const rowStyle = {
display: 'flex',
};
const highlightedStyle = {
borderRadius: '4px',
background: emailTheme.background.colors.highlight,
padding: '4px 8px',
margin: 0,
fontSize: emailTheme.font.size.lg,
fontWeight: emailTheme.font.weight.bold,
color: emailTheme.font.colors.highlighted,
};
export const HighlightedText = ({ value }) => {
return (
<Row style={rowStyle}>
<Column>
<Text style={highlightedStyle}>{value}</Text>
</Column>
</Row>
);
};

View File

@ -0,0 +1,17 @@
import { Img } from '@react-email/components';
const logoStyle = {
marginBottom: '40px',
};
export const Logo = () => {
return (
<Img
src="https://app.twenty.com/icons/windows11/Square150x150Logo.scale-100.png"
alt="Twenty logo"
width="40"
height="40"
style={logoStyle}
/>
);
};

View File

@ -0,0 +1,14 @@
import { Text } from '@react-email/text';
import * as React from 'react';
import { emailTheme } from 'src/emails/common-style';
const mainTextStyle = {
fontSize: emailTheme.font.size.md,
fontWeight: emailTheme.font.weight.regular,
color: emailTheme.font.colors.primary,
};
export const MainText = ({ children }) => {
return <Text style={mainTextStyle}>{children}</Text>;
};

View File

@ -0,0 +1,6 @@
import { Heading } from '@react-email/components';
import * as React from 'react';
export const Title = ({ value }) => {
return <Heading as="h1">{value}</Heading>;
};

View File

@ -0,0 +1,18 @@
import { Injectable } from '@nestjs/common';
import { SendMailOptions } from 'nodemailer';
import { MessageQueueJob } from 'src/integrations/message-queue/interfaces/message-queue-job.interface';
import { EmailSenderService } from 'src/integrations/email/email-sender.service';
@Injectable()
export class EmailSenderJob implements MessageQueueJob<SendMailOptions> {
constructor(private readonly emailSenderService: EmailSenderService) {}
async handle(data: SendMailOptions): Promise<void> {
process.stdout.write(`Sending email to ${data.to} ...`);
await this.emailSenderService.send(data);
console.log(' done!');
}
}

View File

@ -0,0 +1,16 @@
import { Inject, Injectable } from '@nestjs/common';
import { SendMailOptions } from 'nodemailer';
import { EmailDriver } from 'src/integrations/email/drivers/interfaces/email-driver.interface';
import { EMAIL_DRIVER } from 'src/integrations/email/email.constants';
@Injectable()
export class EmailSenderService implements EmailDriver {
constructor(@Inject(EMAIL_DRIVER) private driver: EmailDriver) {}
async send(sendMailOptions: SendMailOptions): Promise<void> {
await this.driver.send(sendMailOptions);
}
}

View File

@ -6,6 +6,7 @@ import { EMAIL_DRIVER } from 'src/integrations/email/email.constants';
import { LoggerDriver } from 'src/integrations/email/drivers/logger.driver';
import { SmtpDriver } from 'src/integrations/email/drivers/smtp.driver';
import { EmailService } from 'src/integrations/email/email.service';
import { EmailSenderService } from 'src/integrations/email/email-sender.service';
@Global()
export class EmailModule {
@ -22,8 +23,8 @@ export class EmailModule {
return {
module: EmailModule,
providers: [EmailService, provider],
exports: [EmailService],
providers: [EmailSenderService, EmailService, provider],
exports: [EmailSenderService, EmailService],
};
}
}

View File

@ -2,15 +2,22 @@ import { Inject, Injectable } from '@nestjs/common';
import { SendMailOptions } from 'nodemailer';
import { EmailDriver } from 'src/integrations/email/drivers/interfaces/email-driver.interface';
import { EMAIL_DRIVER } from 'src/integrations/email/email.constants';
import { MessageQueue } from 'src/integrations/message-queue/message-queue.constants';
import { MessageQueueService } from 'src/integrations/message-queue/services/message-queue.service';
import { EmailSenderJob } from 'src/integrations/email/email-sender.job';
@Injectable()
export class EmailService implements EmailDriver {
constructor(@Inject(EMAIL_DRIVER) private driver: EmailDriver) {}
export class EmailService {
constructor(
@Inject(MessageQueue.emailQueue)
private readonly messageQueueService: MessageQueueService,
) {}
async send(sendMailOptions: SendMailOptions): Promise<void> {
await this.driver.send(sendMailOptions);
await this.messageQueueService.add<SendMailOptions>(
EmailSenderJob.name,
sendMailOptions,
{ retryLimit: 3 },
);
}
}

View File

@ -6,8 +6,8 @@ import { exceptionHandlerModuleFactory } from 'src/integrations/exception-handle
import { fileStorageModuleFactory } from 'src/integrations/file-storage/file-storage.module-factory';
import { loggerModuleFactory } from 'src/integrations/logger/logger.module-factory';
import { messageQueueModuleFactory } from 'src/integrations/message-queue/message-queue.module-factory';
import { emailModuleFactory } from 'src/integrations/email/email.module-factory';
import { EmailModule } from 'src/integrations/email/email.module';
import { emailModuleFactory } from 'src/integrations/email/email.module-factory';
import { EnvironmentModule } from './environment/environment.module';
import { EnvironmentService } from './environment/environment.service';

View File

@ -10,6 +10,7 @@ import { ObjectMetadataModule } from 'src/metadata/object-metadata/object-metada
import { DataSourceModule } from 'src/metadata/data-source/data-source.module';
import { TypeORMModule } from 'src/database/typeorm/typeorm.module';
import { FetchWorkspaceMessagesModule } from 'src/workspace/messaging/services/fetch-workspace-messages.module';
import { EmailSenderJob } from 'src/integrations/email/email-sender.job';
@Module({
imports: [
@ -33,6 +34,10 @@ import { FetchWorkspaceMessagesModule } from 'src/workspace/messaging/services/f
provide: CallWebhookJob.name,
useClass: CallWebhookJob,
},
{
provide: EmailSenderJob.name,
useClass: EmailSenderJob,
},
],
})
export class JobsModule {

View File

@ -5,4 +5,5 @@ export enum MessageQueue {
messagingQueue = 'messaging-queue',
webhookQueue = 'webhook-queue',
cronQueue = 'cron-queue',
emailQueue = 'email-queue',
}

View File

@ -7,6 +7,7 @@ import { loggerModuleFactory } from 'src/integrations/logger/logger.module-facto
import { JobsModule } from 'src/integrations/message-queue/jobs.module';
import { MessageQueueModule } from 'src/integrations/message-queue/message-queue.module';
import { messageQueueModuleFactory } from 'src/integrations/message-queue/message-queue.module-factory';
import { IntegrationsModule } from 'src/integrations/integrations.module';
@Module({
imports: [
@ -20,6 +21,7 @@ import { messageQueueModuleFactory } from 'src/integrations/message-queue/messag
inject: [EnvironmentService],
}),
JobsModule,
IntegrationsModule,
],
})
export class QueueWorkerModule {}

View File

@ -22,6 +22,7 @@
"forceConsistentCasingInFileNames": false,
"noFallthroughCasesInSwitch": false,
"resolveJsonModule": true,
"types": ["jest", "node"]
"types": ["jest", "node"],
"jsx": "react-jsx"
}
}

297
yarn.lock
View File

@ -8416,6 +8416,13 @@ __metadata:
languageName: node
linkType: hard
"@one-ini/wasm@npm:0.1.1":
version: 0.1.1
resolution: "@one-ini/wasm@npm:0.1.1"
checksum: 54700e055037f1a63bfcc86d24822203b25759598c2c3e295d1435130a449108aebc119c9c2e467744767dbe0b6ab47a182c61aa1071ba7368f5e20ab197ba65
languageName: node
linkType: hard
"@open-draft/deferred-promise@npm:^2.1.0, @open-draft/deferred-promise@npm:^2.2.0":
version: 2.2.0
resolution: "@open-draft/deferred-promise@npm:2.2.0"
@ -9795,6 +9802,205 @@ __metadata:
languageName: node
linkType: hard
"@react-email/body@npm:0.0.4":
version: 0.0.4
resolution: "@react-email/body@npm:0.0.4"
peerDependencies:
react: 18.2.0
checksum: 756a184bc1284e8ce1828cd3abc0ea3aa24262e6c677b4e4de48fd6c298717fbc0164e7af7ac4c72262bc3138222ab7fc30c63b0520178315fece2bd78d12f98
languageName: node
linkType: hard
"@react-email/button@npm:0.0.11":
version: 0.0.11
resolution: "@react-email/button@npm:0.0.11"
peerDependencies:
react: 18.2.0
checksum: 91de881f33adec7f5ff311dc8259771fff638cbb736a851a6d4a7e634378555177eb112bc38abb18af7e4bc8ccc899f5bce5b20d409e377d289904d37a20c0ce
languageName: node
linkType: hard
"@react-email/column@npm:0.0.8":
version: 0.0.8
resolution: "@react-email/column@npm:0.0.8"
peerDependencies:
react: 18.2.0
checksum: 5a937dbe7a69c1db8078f84cf70b66224be187dafa78e683103ccbbdb5d3ad2267f7b92c4fbd73922e8c8dda2a4372f37c1a7efac38501cb356d20c028a87574
languageName: node
linkType: hard
"@react-email/components@npm:0.0.12":
version: 0.0.12
resolution: "@react-email/components@npm:0.0.12"
dependencies:
"@react-email/body": "npm:0.0.4"
"@react-email/button": "npm:0.0.11"
"@react-email/column": "npm:0.0.8"
"@react-email/container": "npm:0.0.10"
"@react-email/font": "npm:0.0.4"
"@react-email/head": "npm:0.0.6"
"@react-email/heading": "npm:0.0.9"
"@react-email/hr": "npm:0.0.6"
"@react-email/html": "npm:0.0.6"
"@react-email/img": "npm:0.0.6"
"@react-email/link": "npm:0.0.6"
"@react-email/preview": "npm:0.0.7"
"@react-email/render": "npm:0.0.9"
"@react-email/row": "npm:0.0.6"
"@react-email/section": "npm:0.0.10"
"@react-email/tailwind": "npm:0.0.13"
"@react-email/text": "npm:0.0.6"
peerDependencies:
react: 18.2.0
checksum: 1cc01198e5eeb42aac79b11e1a0ceaddd75d3164425899d6fc676ddbcd84cb869cebb0c6feb1f4383ba92d150c32e94d1fe5eec2ee74711b9309569e3d9bc6ac
languageName: node
linkType: hard
"@react-email/container@npm:0.0.10":
version: 0.0.10
resolution: "@react-email/container@npm:0.0.10"
peerDependencies:
react: 18.2.0
checksum: d074fad448f6033d232b890b9d1a9914d0f92908ba494feaa627828718f4fa6e639636f239eb8eeb8b597c220da9019e90cb72ee2b0328c2ec908fd73e48fc13
languageName: node
linkType: hard
"@react-email/font@npm:0.0.4":
version: 0.0.4
resolution: "@react-email/font@npm:0.0.4"
peerDependencies:
react: 18.2.0
checksum: 066a0016fa78267c83665d5e3846421f7d787361b11f4e712bd32353d0de947a72a42036fdfd35b75eff479ca77deba0f5c220bee43ace8594f7c6345c30a0bc
languageName: node
linkType: hard
"@react-email/head@npm:0.0.6":
version: 0.0.6
resolution: "@react-email/head@npm:0.0.6"
peerDependencies:
react: 18.2.0
checksum: aa2362c965c6f1fa2bcd41be04c1ce8695864153768b4ba4f4b102dd1e1adb28ba067c54f71ded592799aacfa348813cae0f442797b3448bb87feb4588443546
languageName: node
linkType: hard
"@react-email/heading@npm:0.0.9":
version: 0.0.9
resolution: "@react-email/heading@npm:0.0.9"
dependencies:
"@radix-ui/react-slot": "npm:1.0.2"
react: "npm:18.2.0"
checksum: d082d2a4d3db312cbaa0d62419cc1b29c6b6c3ee1467a220c97c9cb009be1af5f6409370b9caac054d8aee8d326fb6e894a72073ff429f05ed097ba471e12a43
languageName: node
linkType: hard
"@react-email/hr@npm:0.0.6":
version: 0.0.6
resolution: "@react-email/hr@npm:0.0.6"
peerDependencies:
react: 18.2.0
checksum: b965c176192a06b39d2c696a8b747cdf4060ba7229a7d45eaf1d5729c59920fefe90f43fbc5f8d203a8e5d77645ee62745d06ec5ec5c9b030815f0642a8ac4ab
languageName: node
linkType: hard
"@react-email/html@npm:0.0.6":
version: 0.0.6
resolution: "@react-email/html@npm:0.0.6"
peerDependencies:
react: 18.2.0
checksum: 9046476ccb20e405a64e33ca3a80f1ed7645bacec0601001fd36efd95497a5999daebf0ad141f0c08387190582e23ff2b2f7fc536461ec9b27d0b397a311fe4f
languageName: node
linkType: hard
"@react-email/img@npm:0.0.6":
version: 0.0.6
resolution: "@react-email/img@npm:0.0.6"
peerDependencies:
react: 18.2.0
checksum: 046f0b60bdf9c7c04a2033b39967de96e31ffedae8922f9f187ce39f6040deee33f02e5695f56c26cf1691cce7de937bcbd303c3804b9a824d0aabc71427a462
languageName: node
linkType: hard
"@react-email/link@npm:0.0.6":
version: 0.0.6
resolution: "@react-email/link@npm:0.0.6"
peerDependencies:
react: 18.2.0
checksum: 3e5eb2e222abe99bdcec6127a20d4ac8ed584189527a2bced119b09d08625b74f0ffa8a1de009974b4cd47249ade4081e108ca4fd65daa0ca811a7c6058cf838
languageName: node
linkType: hard
"@react-email/preview@npm:0.0.7":
version: 0.0.7
resolution: "@react-email/preview@npm:0.0.7"
peerDependencies:
react: 18.2.0
checksum: 9125597760e69d36b9caf1a94595913f52364642351728a5ff91b7d97fce26fad74bae5f1c9baade8831f4154d4c88c773adcbc1f9f1d602b4113c50bf21eb30
languageName: node
linkType: hard
"@react-email/render@npm:0.0.10":
version: 0.0.10
resolution: "@react-email/render@npm:0.0.10"
dependencies:
html-to-text: "npm:9.0.5"
pretty: "npm:2.0.0"
react: "npm:18.2.0"
react-dom: "npm:18.2.0"
checksum: ea0db578ef58734160ac512d04de5b09f558d192fbb74c0b9bdb3006e91d0d73ee7d77007f4740bef0d587f8b231f8dba5ef01e16e18d2deb37859ddd2e7d09a
languageName: node
linkType: hard
"@react-email/render@npm:0.0.9":
version: 0.0.9
resolution: "@react-email/render@npm:0.0.9"
dependencies:
html-to-text: "npm:9.0.5"
pretty: "npm:2.0.0"
react: "npm:18.2.0"
react-dom: "npm:18.2.0"
checksum: 128b3a7c65e77a14600e48fb24183e182de82b4ddc8ff11bb239c421fa68bd6eb8b063822b5abe9f8599e34ece315cdb961b4133c59285cebcc517d2ec0ae8da
languageName: node
linkType: hard
"@react-email/row@npm:0.0.6":
version: 0.0.6
resolution: "@react-email/row@npm:0.0.6"
peerDependencies:
react: 18.2.0
checksum: ca1d92094a5ccc35c93560538311c38989162e9f0235a71897296bc43f2c0ba31d53df9c7db1688f7be0d77ccc03a0094cc227c4d095787f3b3ea0c3d262e520
languageName: node
linkType: hard
"@react-email/section@npm:0.0.10":
version: 0.0.10
resolution: "@react-email/section@npm:0.0.10"
peerDependencies:
react: 18.2.0
checksum: d1af5dc3ff9a5bd2675a82f976df79ab5acded5ca305536df36ad84440b3828b021f8026d721e34fe92853bc3dfe8b9aa4603c25bf2f901b22049748ed6a7fbc
languageName: node
linkType: hard
"@react-email/tailwind@npm:0.0.13":
version: 0.0.13
resolution: "@react-email/tailwind@npm:0.0.13"
dependencies:
react: "npm:18.2.0"
react-dom: "npm:18.2.0"
peerDependencies:
react: 18.2.0
checksum: 5980a8593b699e9a0e0a8178120d206e74ccb525ba5fedb02e7048f8efff7ca41f82308584624a9701dc021d2ccfd8fa224f11a191d78e567a77f8d35bd47bb4
languageName: node
linkType: hard
"@react-email/text@npm:0.0.6":
version: 0.0.6
resolution: "@react-email/text@npm:0.0.6"
peerDependencies:
react: 18.2.0
checksum: 1e960838bfe81bf33b42db1aa1d84446fc36911b4d9e9a79570d9a43fe9cc29d9271f7e1b25448383d8fe950a73130f8bed5c65cd8dbc786a2e6fe02e0c16202
languageName: node
linkType: hard
"@react-hook/debounce@npm:^3.0.0":
version: 3.0.0
resolution: "@react-hook/debounce@npm:3.0.0"
@ -19889,7 +20095,18 @@ __metadata:
languageName: node
linkType: hard
"config-chain@npm:^1.1.11":
"condense-newlines@npm:^0.2.1":
version: 0.2.1
resolution: "condense-newlines@npm:0.2.1"
dependencies:
extend-shallow: "npm:^2.0.1"
is-whitespace: "npm:^0.3.0"
kind-of: "npm:^3.0.2"
checksum: 19485db92a5d4658b50ab250626ece0cebe57f73af126b348604309894ed9a2b05f88f1802a090fd1897156eda0af69d8f14446bc62f978e0d048b5135e91694
languageName: node
linkType: hard
"config-chain@npm:^1.1.11, config-chain@npm:^1.1.13":
version: 1.1.13
resolution: "config-chain@npm:1.1.13"
dependencies:
@ -21842,6 +22059,20 @@ __metadata:
languageName: node
linkType: hard
"editorconfig@npm:^1.0.3":
version: 1.0.4
resolution: "editorconfig@npm:1.0.4"
dependencies:
"@one-ini/wasm": "npm:0.1.1"
commander: "npm:^10.0.0"
minimatch: "npm:9.0.1"
semver: "npm:^7.5.3"
bin:
editorconfig: bin/editorconfig
checksum: ed6985959d7b34a56e1c09bef118758c81c969489b768d152c93689fce8403b0452462e934f665febaba3478eebc0fd41c0a36100783eaadf6d926c4abc87a3d
languageName: node
linkType: hard
"ee-first@npm:1.1.1":
version: 1.1.1
resolution: "ee-first@npm:1.1.1"
@ -24670,7 +24901,7 @@ __metadata:
languageName: node
linkType: hard
"glob@npm:^10.0.0, glob@npm:^10.2.2, glob@npm:^10.3.10, glob@npm:^10.3.7":
"glob@npm:^10.0.0, glob@npm:^10.2.2, glob@npm:^10.3.10, glob@npm:^10.3.3, glob@npm:^10.3.7":
version: 10.3.10
resolution: "glob@npm:10.3.10"
dependencies:
@ -27074,7 +27305,7 @@ __metadata:
languageName: node
linkType: hard
"is-buffer@npm:^1.1.0":
"is-buffer@npm:^1.1.0, is-buffer@npm:^1.1.5":
version: 1.1.6
resolution: "is-buffer@npm:1.1.6"
checksum: ae18aa0b6e113d6c490ad1db5e8df9bdb57758382b313f5a22c9c61084875c6396d50bbf49315f5b1926d142d74dfb8d31b40d993a383e0a158b15fea7a82234
@ -27635,6 +27866,13 @@ __metadata:
languageName: node
linkType: hard
"is-whitespace@npm:^0.3.0":
version: 0.3.0
resolution: "is-whitespace@npm:0.3.0"
checksum: 2f4ef13e0195170bbb587437133ef81ed9d6aec1c5e88f4c2b9055a18a1e70f75d9a9376f0cdae64f3c519e05e5f734d6b8f9682e5cb50384843480bade785ae
languageName: node
linkType: hard
"is-windows@npm:^0.2.0":
version: 0.2.0
resolution: "is-windows@npm:0.2.0"
@ -28608,6 +28846,22 @@ __metadata:
languageName: node
linkType: hard
"js-beautify@npm:^1.6.12":
version: 1.14.11
resolution: "js-beautify@npm:1.14.11"
dependencies:
config-chain: "npm:^1.1.13"
editorconfig: "npm:^1.0.3"
glob: "npm:^10.3.3"
nopt: "npm:^7.2.0"
bin:
css-beautify: js/bin/css-beautify.js
html-beautify: js/bin/html-beautify.js
js-beautify: js/bin/js-beautify.js
checksum: 23267f8e68a4cf190274906fbec98c4fa44025e1b7e1fa701480867c9ab6b2b90a1bb2b358cd487c34d735a1039c16bcb51f82d390bcc6384172cc540aa11c9b
languageName: node
linkType: hard
"js-cookie@npm:^2.2.1":
version: 2.2.1
resolution: "js-cookie@npm:2.2.1"
@ -29167,6 +29421,15 @@ __metadata:
languageName: node
linkType: hard
"kind-of@npm:^3.0.2":
version: 3.2.2
resolution: "kind-of@npm:3.2.2"
dependencies:
is-buffer: "npm:^1.1.5"
checksum: 7e34bc29d4b02c997f92f080de34ebb92033a96736bbb0bb2410e033a7e5ae6571f1fa37b2d7710018f95361473b816c604234197f4f203f9cf149d8ef1574d9
languageName: node
linkType: hard
"kind-of@npm:^6.0.0, kind-of@npm:^6.0.2":
version: 6.0.3
resolution: "kind-of@npm:6.0.3"
@ -32285,6 +32548,15 @@ __metadata:
languageName: node
linkType: hard
"minimatch@npm:9.0.1":
version: 9.0.1
resolution: "minimatch@npm:9.0.1"
dependencies:
brace-expansion: "npm:^2.0.1"
checksum: aa043eb8822210b39888a5d0d28df0017b365af5add9bd522f180d2a6962de1cbbf1bdeacdb1b17f410dc3336bc8d76fb1d3e814cdc65d00c2f68e01f0010096
languageName: node
linkType: hard
"minimatch@npm:9.0.3, minimatch@npm:^9.0.0, minimatch@npm:^9.0.1":
version: 9.0.3
resolution: "minimatch@npm:9.0.3"
@ -33222,7 +33494,7 @@ __metadata:
languageName: node
linkType: hard
"nopt@npm:^7.0.0":
"nopt@npm:^7.0.0, nopt@npm:^7.2.0":
version: 7.2.0
resolution: "nopt@npm:7.2.0"
dependencies:
@ -35916,6 +36188,17 @@ __metadata:
languageName: node
linkType: hard
"pretty@npm:2.0.0":
version: 2.0.0
resolution: "pretty@npm:2.0.0"
dependencies:
condense-newlines: "npm:^0.2.1"
extend-shallow: "npm:^2.0.1"
js-beautify: "npm:^1.6.12"
checksum: 2fcd72f331d0afae3893ba88a5c05f6fdd62b059cb309028aa3309fc8a90410d81dfe66ae95677bc6d6d4a68f3cc1a247c13e5872bd35686f99acb33acc51164
languageName: node
linkType: hard
"prettyjson@npm:^1.2.1":
version: 1.2.5
resolution: "prettyjson@npm:1.2.5"
@ -36728,7 +37011,7 @@ __metadata:
languageName: node
linkType: hard
"react-dom@npm:^18.2.0":
"react-dom@npm:18.2.0, react-dom@npm:^18.2.0":
version: 18.2.0
resolution: "react-dom@npm:18.2.0"
dependencies:
@ -37263,7 +37546,7 @@ __metadata:
languageName: node
linkType: hard
"react@npm:^18, react@npm:^18.2.0":
"react@npm:18.2.0, react@npm:^18, react@npm:^18.2.0":
version: 18.2.0
resolution: "react@npm:18.2.0"
dependencies:
@ -41529,6 +41812,8 @@ __metadata:
"@octokit/graphql": "npm:^7.0.2"
"@ptc-org/nestjs-query-core": "npm:^4.2.0"
"@ptc-org/nestjs-query-typeorm": "npm:4.2.1-alpha.2"
"@react-email/components": "npm:0.0.12"
"@react-email/render": "npm:0.0.10"
"@sentry/node": "npm:^7.66.0"
"@sentry/profiling-node": "npm:^1.2.6"
"@sentry/react": "npm:^7.88.0"