From 549c3faf71f2bd7775050255e5ee83459319bfa0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20Malfait?= Date: Mon, 27 Jan 2025 21:07:49 +0100 Subject: [PATCH] Add server translation (#9847) First proof of concept for server-side translation. The goal was to translate one metadata item: Screenshot 2025-01-26 at 08 18 41 --- package.json | 2 +- packages/twenty-front/lingui.config.ts | 12 +- .../modules/apollo/hooks/useApolloFactory.ts | 10 +- .../services/__tests__/apollo.factory.test.ts | 37 +++- .../modules/apollo/services/apollo.factory.ts | 12 ++ .../apollo/types/apolloManager.interface.ts | 2 + packages/twenty-server/.swcrc | 8 +- packages/twenty-server/jest.config.ts | 23 ++- packages/twenty-server/lingui.config.js | 25 +++ packages/twenty-server/project.json | 14 ++ packages/twenty-server/src/app.module.ts | 3 + .../hooks/use-cached-metadata.ts | 7 +- .../admin-panel/admin-panel.spec.ts | 10 +- .../app-token/app-token.entity.ts | 1 + .../auth/services/auth.service.spec.ts | 1 - .../engine/core-modules/i18n/i18n.module.ts | 10 ++ .../engine/core-modules/i18n/i18n.service.ts | 16 ++ .../engine/core-modules/i18n/locales/en.po | 41 +++++ .../engine/core-modules/i18n/locales/fr.po | 20 +++ .../core-modules/i18n/locales/generated/en.js | 1 + .../core-modules/i18n/locales/generated/fr.js | 1 + .../engine/guards/impersonate-guard.spec.ts | 2 - .../object-metadata.resolver.ts | 33 +++- .../object-metadata.service.ts | 16 ++ ...l-hydrate-request-from-token.middleware.ts | 1 - .../twenty-orm/custom.workspace-entity.ts | 2 +- .../decorators/workspace-entity.decorator.ts | 23 ++- .../company.workspace-entity.ts | 13 +- .../message-import-driver.exception.ts | 2 + .../standard-objects/note.workspace-entity.ts | 6 +- .../opportunity.workspace-entity.ts | 2 +- .../person.workspace-entity.ts | 8 +- .../standard-objects/task.workspace-entity.ts | 6 +- .../workspace-member.workspace-entity.ts | 6 +- yarn.lock | 167 +++++++++--------- 35 files changed, 412 insertions(+), 131 deletions(-) create mode 100644 packages/twenty-server/lingui.config.js create mode 100644 packages/twenty-server/src/engine/core-modules/i18n/i18n.module.ts create mode 100644 packages/twenty-server/src/engine/core-modules/i18n/i18n.service.ts create mode 100644 packages/twenty-server/src/engine/core-modules/i18n/locales/en.po create mode 100644 packages/twenty-server/src/engine/core-modules/i18n/locales/fr.po create mode 100644 packages/twenty-server/src/engine/core-modules/i18n/locales/generated/en.js create mode 100644 packages/twenty-server/src/engine/core-modules/i18n/locales/generated/fr.js diff --git a/package.json b/package.json index cc2b07dfe..3f880ff24 100644 --- a/package.json +++ b/package.json @@ -240,7 +240,7 @@ "@stylistic/eslint-plugin": "^1.5.0", "@swc-node/register": "1.8.0", "@swc/cli": "^0.3.12", - "@swc/core": "~1.3.100", + "@swc/core": "1.7.42", "@swc/helpers": "~0.5.2", "@testing-library/jest-dom": "^6.1.5", "@testing-library/react": "14.0.0", diff --git a/packages/twenty-front/lingui.config.ts b/packages/twenty-front/lingui.config.ts index 9f53c1417..3fb381aef 100644 --- a/packages/twenty-front/lingui.config.ts +++ b/packages/twenty-front/lingui.config.ts @@ -25,8 +25,12 @@ export default defineConfig({ ], catalogsMergePath: '/src/locales/generated/{locale}', compileNamespace: 'ts', - service: { - name: 'TranslationIO', - apiKey: process.env.TRANSLATION_IO_API_KEY ?? '', - }, + ...(process.env.TRANSLATION_IO_API_KEY + ? { + service: { + name: 'TranslationIO', + apiKey: process.env.TRANSLATION_IO_API_KEY, + }, + } + : {}), }); diff --git a/packages/twenty-front/src/modules/apollo/hooks/useApolloFactory.ts b/packages/twenty-front/src/modules/apollo/hooks/useApolloFactory.ts index 3e2352d88..368a2ee2a 100644 --- a/packages/twenty-front/src/modules/apollo/hooks/useApolloFactory.ts +++ b/packages/twenty-front/src/modules/apollo/hooks/useApolloFactory.ts @@ -1,7 +1,7 @@ import { InMemoryCache, NormalizedCacheObject } from '@apollo/client'; import { useMemo, useRef } from 'react'; import { useLocation, useNavigate } from 'react-router-dom'; -import { useRecoilState, useSetRecoilState } from 'recoil'; +import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'; import { currentUserState } from '@/auth/states/currentUserState'; import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState'; @@ -29,6 +29,7 @@ export const useApolloFactory = (options: Partial> = {}) => { const [currentWorkspace, setCurrentWorkspace] = useRecoilState( currentWorkspaceState, ); + const currentWorkspaceMember = useRecoilValue(currentWorkspaceMemberState); const setCurrentUser = useSetRecoilState(currentUserState); const setCurrentWorkspaceMember = useSetRecoilState( currentWorkspaceMemberState, @@ -61,6 +62,7 @@ export const useApolloFactory = (options: Partial> = {}) => { connectToDevTools: isDebugMode, // We don't want to re-create the client on token change or it will cause infinite loop initialTokenPair: tokenPair, + currentWorkspaceMember: currentWorkspaceMember, onTokenPairChange: (tokenPair) => { setTokenPair(tokenPair); }, @@ -105,5 +107,11 @@ export const useApolloFactory = (options: Partial> = {}) => { } }, [tokenPair]); + useUpdateEffect(() => { + if (isDefined(apolloRef.current)) { + apolloRef.current.updateWorkspaceMember(currentWorkspaceMember); + } + }, [currentWorkspaceMember]); + return apolloClient; }; diff --git a/packages/twenty-front/src/modules/apollo/services/__tests__/apollo.factory.test.ts b/packages/twenty-front/src/modules/apollo/services/__tests__/apollo.factory.test.ts index c50e93b46..0d7fc8818 100644 --- a/packages/twenty-front/src/modules/apollo/services/__tests__/apollo.factory.test.ts +++ b/packages/twenty-front/src/modules/apollo/services/__tests__/apollo.factory.test.ts @@ -21,12 +21,22 @@ jest.mock('@/auth/services/AuthService', () => { const mockOnError = jest.fn(); const mockOnNetworkError = jest.fn(); +const mockWorkspaceMember = { + id: 'workspace-member-id', + locale: 'en', + name: { + firstName: 'John', + lastName: 'Doe', + }, +}; + const createMockOptions = (): Options => ({ uri: 'http://localhost:3000', initialTokenPair: { accessToken: { token: 'mockAccessToken', expiresAt: '' }, refreshToken: { token: 'mockRefreshToken', expiresAt: '' }, }, + currentWorkspaceMember: mockWorkspaceMember, cache: new InMemoryCache(), isDebugMode: true, onError: mockOnError, @@ -50,13 +60,21 @@ const makeRequest = async () => { }); }; -describe('xApolloFactory', () => { +describe('ApolloFactory', () => { it('should create an instance of ApolloFactory', () => { const options = createMockOptions(); const apolloFactory = new ApolloFactory(options); expect(apolloFactory).toBeInstanceOf(ApolloFactory); }); + it('should initialize with the correct workspace member', () => { + const options = createMockOptions(); + const apolloFactory = new ApolloFactory(options); + expect(apolloFactory['currentWorkspaceMember']).toEqual( + mockWorkspaceMember, + ); + }); + it('should call onError when encountering "Unauthorized" error', async () => { const errors = [{ message: 'Unauthorized' }]; fetchMock.mockResponse(() => @@ -138,4 +156,21 @@ describe('xApolloFactory', () => { expect(mockOnNetworkError).toHaveBeenCalledWith(mockError); } }, 10000); + + it('should update workspace member when calling updateWorkspaceMember', () => { + const options = createMockOptions(); + const apolloFactory = new ApolloFactory(options); + + const newWorkspaceMember = { + id: 'new-workspace-member-id', + locale: 'fr', + name: { + firstName: 'John', + lastName: 'Doe', + }, + }; + + apolloFactory.updateWorkspaceMember(newWorkspaceMember); + expect(apolloFactory['currentWorkspaceMember']).toEqual(newWorkspaceMember); + }); }); diff --git a/packages/twenty-front/src/modules/apollo/services/apollo.factory.ts b/packages/twenty-front/src/modules/apollo/services/apollo.factory.ts index 6eca2c821..5096989f0 100644 --- a/packages/twenty-front/src/modules/apollo/services/apollo.factory.ts +++ b/packages/twenty-front/src/modules/apollo/services/apollo.factory.ts @@ -12,6 +12,7 @@ import { RetryLink } from '@apollo/client/link/retry'; import { createUploadLink } from 'apollo-upload-client'; import { renewToken } from '@/auth/services/AuthService'; +import { CurrentWorkspaceMember } from '@/auth/states/currentWorkspaceMemberState'; import { AuthTokenPair } from '~/generated/graphql'; import { isDefined } from '~/utils/isDefined'; import { logDebug } from '~/utils/logDebug'; @@ -28,6 +29,7 @@ export interface Options extends ApolloClientOptions { onTokenPairChange?: (tokenPair: AuthTokenPair) => void; onUnauthenticatedError?: () => void; initialTokenPair: AuthTokenPair | null; + currentWorkspaceMember: CurrentWorkspaceMember | null; extraLinks?: ApolloLink[]; isDebugMode?: boolean; } @@ -35,6 +37,7 @@ export interface Options extends ApolloClientOptions { export class ApolloFactory implements ApolloManager { private client: ApolloClient; private tokenPair: AuthTokenPair | null = null; + private currentWorkspaceMember: CurrentWorkspaceMember | null = null; constructor(opts: Options) { const { @@ -44,12 +47,14 @@ export class ApolloFactory implements ApolloManager { onTokenPairChange, onUnauthenticatedError, initialTokenPair, + currentWorkspaceMember, extraLinks, isDebugMode, ...options } = opts; this.tokenPair = initialTokenPair; + this.currentWorkspaceMember = currentWorkspaceMember; const buildApolloLink = (): ApolloLink => { const httpLink = createUploadLink({ @@ -64,6 +69,9 @@ export class ApolloFactory implements ApolloManager { authorization: this.tokenPair?.accessToken.token ? `Bearer ${this.tokenPair?.accessToken.token}` : '', + ...(this.currentWorkspaceMember?.locale + ? { 'x-locale': this.currentWorkspaceMember.locale } + : {}), }, }; }); @@ -157,6 +165,10 @@ export class ApolloFactory implements ApolloManager { this.tokenPair = tokenPair; } + updateWorkspaceMember(workspaceMember: CurrentWorkspaceMember | null) { + this.currentWorkspaceMember = workspaceMember; + } + getClient() { return this.client; } diff --git a/packages/twenty-front/src/modules/apollo/types/apolloManager.interface.ts b/packages/twenty-front/src/modules/apollo/types/apolloManager.interface.ts index 8878734e2..1f033c8c3 100644 --- a/packages/twenty-front/src/modules/apollo/types/apolloManager.interface.ts +++ b/packages/twenty-front/src/modules/apollo/types/apolloManager.interface.ts @@ -1,8 +1,10 @@ import { ApolloClient } from '@apollo/client'; +import { CurrentWorkspaceMember } from '@/auth/states/currentWorkspaceMemberState'; import { AuthTokenPair } from '~/generated/graphql'; export interface ApolloManager { getClient(): ApolloClient; updateTokenPair(tokenPair: AuthTokenPair | null): void; + updateWorkspaceMember(workspaceMember: CurrentWorkspaceMember | null): void; } diff --git a/packages/twenty-server/.swcrc b/packages/twenty-server/.swcrc index ad272e265..ff20e40b1 100644 --- a/packages/twenty-server/.swcrc +++ b/packages/twenty-server/.swcrc @@ -1,4 +1,3 @@ - { "$schema": "https://json.schemastore.org/swcrc", "sourceMaps": true, @@ -8,7 +7,12 @@ "decorators": true, "dynamicImport": true }, - "baseUrl": "./../../" + "baseUrl": "./../../", + "experimental": { + "plugins": [ + ["@lingui/swc-plugin", {}] + ] + } }, "minify": false } diff --git a/packages/twenty-server/jest.config.ts b/packages/twenty-server/jest.config.ts index f25c9e66d..0bd9083d4 100644 --- a/packages/twenty-server/jest.config.ts +++ b/packages/twenty-server/jest.config.ts @@ -1,6 +1,4 @@ -import { JestConfigWithTsJest } from 'ts-jest'; - -const jestConfig: JestConfigWithTsJest = { +const jestConfig = { // to enable logs, comment out the following line silent: true, clearMocks: true, @@ -10,7 +8,24 @@ const jestConfig: JestConfigWithTsJest = { transformIgnorePatterns: ['/node_modules/'], testRegex: '.*\\.spec\\.ts$', transform: { - '^.+\\.(t|j)s$': 'ts-jest', + '^.+\\.(t|j)s$': [ + '@swc/jest', + { + jsc: { + parser: { + syntax: 'typescript', + tsx: false, + decorators: true, + }, + transform: { + decoratorMetadata: true, + }, + experimental: { + plugins: [['@lingui/swc-plugin', {}]], + }, + }, + }, + ], }, moduleNameMapper: { '^src/(.*)': '/src/$1', diff --git a/packages/twenty-server/lingui.config.js b/packages/twenty-server/lingui.config.js new file mode 100644 index 000000000..91d346db4 --- /dev/null +++ b/packages/twenty-server/lingui.config.js @@ -0,0 +1,25 @@ +import { defineConfig } from '@lingui/cli'; + +export default defineConfig({ + sourceLocale: 'en', + locales: ['en', 'fr'], + extractorParserOptions: { + tsExperimentalDecorators: true, + }, + catalogs: [ + { + path: '/src/engine/core-modules/i18n/locales/{locale}', + include: ['src'], + }, + ], + catalogsMergePath: + '/src/engine/core-modules/i18n/locales/generated/{locale}', + ...(process.env.TRANSLATION_IO_API_KEY_BACKEND + ? { + service: { + name: 'TranslationIO', + apiKey: process.env.TRANSLATION_IO_API_KEY_BACKEND, + }, + } + : {}), +}); diff --git a/packages/twenty-server/project.json b/packages/twenty-server/project.json index 72cc73a18..b873fa607 100644 --- a/packages/twenty-server/project.json +++ b/packages/twenty-server/project.json @@ -209,6 +209,20 @@ } }, "defaultConfiguration": "seed" + }, + "lingui:extract": { + "executor": "nx:run-commands", + "options": { + "cwd": "{projectRoot}", + "command": "lingui extract --overwrite" + } + }, + "lingui:compile": { + "executor": "nx:run-commands", + "options": { + "cwd": "{projectRoot}", + "command": "lingui compile --typescript" + } } } } diff --git a/packages/twenty-server/src/app.module.ts b/packages/twenty-server/src/app.module.ts index 491582085..fa45e996d 100644 --- a/packages/twenty-server/src/app.module.ts +++ b/packages/twenty-server/src/app.module.ts @@ -27,6 +27,7 @@ import { WorkspaceCacheStorageModule } from 'src/engine/workspace-cache-storage/ import { ModulesModule } from 'src/modules/modules.module'; import { CoreEngineModule } from './engine/core-modules/core-engine.module'; +import { I18nModule } from './engine/core-modules/i18n/i18n.module'; @Module({ imports: [ @@ -51,6 +52,8 @@ import { CoreEngineModule } from './engine/core-modules/core-engine.module'; CoreGraphQLApiModule, MetadataGraphQLApiModule, RestApiModule, + // I18n module for translations + I18nModule, // Conditional modules ...AppModule.getConditionalModules(), ], diff --git a/packages/twenty-server/src/engine/api/graphql/graphql-config/hooks/use-cached-metadata.ts b/packages/twenty-server/src/engine/api/graphql/graphql-config/hooks/use-cached-metadata.ts index d8b7de5d6..256220d12 100644 --- a/packages/twenty-server/src/engine/api/graphql/graphql-config/hooks/use-cached-metadata.ts +++ b/packages/twenty-server/src/engine/api/graphql/graphql-config/hooks/use-cached-metadata.ts @@ -1,3 +1,4 @@ +import { isDefined } from 'class-validator'; import { Plugin } from 'graphql-yoga'; export type CacheMetadataPluginConfig = { @@ -12,8 +13,12 @@ export function useCachedMetadata(config: CacheMetadataPluginConfig): Plugin { const workspaceMetadataVersion = serverContext.req.workspaceMetadataVersion ?? '0'; const operationName = getOperationName(serverContext); + const locale = serverContext.req.headers['x-locale'] ?? ''; + const localeCacheKey = isDefined(serverContext.req.headers['x-locale']) + ? `:${locale}` + : ''; - return `graphql:operations:${operationName}:${workspaceId}:${workspaceMetadataVersion}`; + return `graphql:operations:${operationName}:${workspaceId}:${workspaceMetadataVersion}${localeCacheKey}`; }; const getOperationName = (serverContext: any) => diff --git a/packages/twenty-server/src/engine/core-modules/admin-panel/admin-panel.spec.ts b/packages/twenty-server/src/engine/core-modules/admin-panel/admin-panel.spec.ts index 24f55ef5e..c27042f15 100644 --- a/packages/twenty-server/src/engine/core-modules/admin-panel/admin-panel.spec.ts +++ b/packages/twenty-server/src/engine/core-modules/admin-panel/admin-panel.spec.ts @@ -1,18 +1,16 @@ import { Test, TestingModule } from '@nestjs/testing'; import { getRepositoryToken } from '@nestjs/typeorm'; -import { expect, jest } from '@jest/globals'; - -import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; -import { User } from 'src/engine/core-modules/user/user.entity'; import { AdminPanelService } from 'src/engine/core-modules/admin-panel/admin-panel.service'; -import { FeatureFlagEntity } from 'src/engine/core-modules/feature-flag/feature-flag.entity'; -import { LoginTokenService } from 'src/engine/core-modules/auth/token/services/login-token.service'; import { AuthException, AuthExceptionCode, } from 'src/engine/core-modules/auth/auth.exception'; +import { LoginTokenService } from 'src/engine/core-modules/auth/token/services/login-token.service'; import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum'; +import { FeatureFlagEntity } from 'src/engine/core-modules/feature-flag/feature-flag.entity'; +import { User } from 'src/engine/core-modules/user/user.entity'; +import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; const UserFindOneMock = jest.fn(); const WorkspaceFindOneMock = jest.fn(); diff --git a/packages/twenty-server/src/engine/core-modules/app-token/app-token.entity.ts b/packages/twenty-server/src/engine/core-modules/app-token/app-token.entity.ts index 4c2f2f1fc..d555de7e9 100644 --- a/packages/twenty-server/src/engine/core-modules/app-token/app-token.entity.ts +++ b/packages/twenty-server/src/engine/core-modules/app-token/app-token.entity.ts @@ -16,6 +16,7 @@ import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/ import { BeforeCreateOneAppToken } from 'src/engine/core-modules/app-token/hooks/before-create-one-app-token.hook'; import { User } from 'src/engine/core-modules/user/user.entity'; import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; + export enum AppTokenType { RefreshToken = 'REFRESH_TOKEN', CodeChallenge = 'CODE_CHALLENGE', diff --git a/packages/twenty-server/src/engine/core-modules/auth/services/auth.service.spec.ts b/packages/twenty-server/src/engine/core-modules/auth/services/auth.service.spec.ts index 09aaaf786..6812aa94a 100644 --- a/packages/twenty-server/src/engine/core-modules/auth/services/auth.service.spec.ts +++ b/packages/twenty-server/src/engine/core-modules/auth/services/auth.service.spec.ts @@ -1,7 +1,6 @@ import { Test, TestingModule } from '@nestjs/testing'; import { getRepositoryToken } from '@nestjs/typeorm'; -import { expect, jest } from '@jest/globals'; import bcrypt from 'bcrypt'; import { Repository } from 'typeorm'; diff --git a/packages/twenty-server/src/engine/core-modules/i18n/i18n.module.ts b/packages/twenty-server/src/engine/core-modules/i18n/i18n.module.ts new file mode 100644 index 000000000..a837b4804 --- /dev/null +++ b/packages/twenty-server/src/engine/core-modules/i18n/i18n.module.ts @@ -0,0 +1,10 @@ +import { Global, Module } from '@nestjs/common'; + +import { I18nService } from 'src/engine/core-modules/i18n/i18n.service'; + +@Global() +@Module({ + providers: [I18nService], + exports: [I18nService], +}) +export class I18nModule {} diff --git a/packages/twenty-server/src/engine/core-modules/i18n/i18n.service.ts b/packages/twenty-server/src/engine/core-modules/i18n/i18n.service.ts new file mode 100644 index 000000000..566d7d086 --- /dev/null +++ b/packages/twenty-server/src/engine/core-modules/i18n/i18n.service.ts @@ -0,0 +1,16 @@ +import { Injectable, OnModuleInit } from '@nestjs/common'; + +import { i18n } from '@lingui/core'; + +import { messages as enMessages } from 'src/engine/core-modules/i18n/locales/generated/en.js'; +import { messages as frMessages } from 'src/engine/core-modules/i18n/locales/generated/fr.js'; + +@Injectable() +export class I18nService implements OnModuleInit { + async onModuleInit() { + i18n.load('fr', frMessages); + i18n.load('en', enMessages); + + i18n.activate('en'); + } +} diff --git a/packages/twenty-server/src/engine/core-modules/i18n/locales/en.po b/packages/twenty-server/src/engine/core-modules/i18n/locales/en.po new file mode 100644 index 000000000..b4b361726 --- /dev/null +++ b/packages/twenty-server/src/engine/core-modules/i18n/locales/en.po @@ -0,0 +1,41 @@ +msgid "" +msgstr "" +"POT-Creation-Date: 2025-01-25 21:24+0100\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: @lingui/cli\n" +"Language: en\n" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: \n" +"Last-Translator: \n" +"Language-Team: \n" +"Plural-Forms: \n" + +#. js-lingui-explicit-id +#: src/modules/company/standard-objects/company.workspace-entity.ts:56 +#~ msgid "Company" +#~ msgstr "Company" + +#. js-lingui-explicit-id +#: src/modules/company/standard-objects/company.workspace-entity.ts:57 +#~ msgid "Companies" +#~ msgstr "Companies" + +#. js-lingui-explicit-id +#: src/modules/company/standard-objects/company.workspace-entity.ts:58 +#~ msgid "A company" +#~ msgstr "A company" + +#: src/modules/company/standard-objects/company.workspace-entity.ts:58 +msgid "A company" +msgstr "A company" + +#: src/modules/company/standard-objects/company.workspace-entity.ts:57 +msgid "Companies" +msgstr "Companies" + +#: src/modules/company/standard-objects/company.workspace-entity.ts:56 +msgid "Company" +msgstr "Company" diff --git a/packages/twenty-server/src/engine/core-modules/i18n/locales/fr.po b/packages/twenty-server/src/engine/core-modules/i18n/locales/fr.po new file mode 100644 index 000000000..387e7ffa0 --- /dev/null +++ b/packages/twenty-server/src/engine/core-modules/i18n/locales/fr.po @@ -0,0 +1,20 @@ +msgid "" +msgstr "" +"POT-Creation-Date: 2025-01-26 21:19+0100\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: @lingui/cli\n" +"Language: fr\n" + +#: src/modules/company/standard-objects/company.workspace-entity.ts:58 +msgid "A company" +msgstr "Une entreprise" + +#: src/modules/company/standard-objects/company.workspace-entity.ts:57 +msgid "Companies" +msgstr "Entreprises" + +#: src/modules/company/standard-objects/company.workspace-entity.ts:56 +msgid "Company" +msgstr "Entreprise" diff --git a/packages/twenty-server/src/engine/core-modules/i18n/locales/generated/en.js b/packages/twenty-server/src/engine/core-modules/i18n/locales/generated/en.js new file mode 100644 index 000000000..3829ff5a8 --- /dev/null +++ b/packages/twenty-server/src/engine/core-modules/i18n/locales/generated/en.js @@ -0,0 +1 @@ +/*eslint-disable*/module.exports={messages:JSON.parse("{\"Company\":[\"Company\"],\"Companies\":[\"Companies\"],\"A company\":[\"A company\"],\"kZR6+h\":[\"A company\"],\"s2QZS6\":[\"Companies\"],\"7i8j3G\":[\"Company\"]}")}; \ No newline at end of file diff --git a/packages/twenty-server/src/engine/core-modules/i18n/locales/generated/fr.js b/packages/twenty-server/src/engine/core-modules/i18n/locales/generated/fr.js new file mode 100644 index 000000000..4df2bba0c --- /dev/null +++ b/packages/twenty-server/src/engine/core-modules/i18n/locales/generated/fr.js @@ -0,0 +1 @@ +/*eslint-disable*/module.exports={messages:JSON.parse("{\"Company\":[\"Entreprise\"],\"Companies\":[\"Entreprises\"],\"A company\":[\"Une entreprise\"],\"kZR6+h\":[\"Une entreprise\"],\"s2QZS6\":[\"Entreprises\"],\"7i8j3G\":[\"Entreprise\"]}")}; \ No newline at end of file diff --git a/packages/twenty-server/src/engine/guards/impersonate-guard.spec.ts b/packages/twenty-server/src/engine/guards/impersonate-guard.spec.ts index 5135bd417..e5d107779 100644 --- a/packages/twenty-server/src/engine/guards/impersonate-guard.spec.ts +++ b/packages/twenty-server/src/engine/guards/impersonate-guard.spec.ts @@ -1,8 +1,6 @@ import { ExecutionContext } from '@nestjs/common'; import { GqlExecutionContext } from '@nestjs/graphql'; -import { expect, jest } from '@jest/globals'; - import { ImpersonateGuard } from 'src/engine/guards/impersonate-guard'; describe('ImpersonateGuard', () => { diff --git a/packages/twenty-server/src/engine/metadata-modules/object-metadata/object-metadata.resolver.ts b/packages/twenty-server/src/engine/metadata-modules/object-metadata/object-metadata.resolver.ts index 83d9a1548..37e069640 100644 --- a/packages/twenty-server/src/engine/metadata-modules/object-metadata/object-metadata.resolver.ts +++ b/packages/twenty-server/src/engine/metadata-modules/object-metadata/object-metadata.resolver.ts @@ -1,5 +1,12 @@ import { UseGuards } from '@nestjs/common'; -import { Args, Mutation, Resolver } from '@nestjs/graphql'; +import { + Args, + Context, + Mutation, + Parent, + ResolveField, + Resolver, +} from '@nestjs/graphql'; import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorator'; @@ -22,6 +29,30 @@ export class ObjectMetadataResolver { private readonly beforeUpdateOneObject: BeforeUpdateOneObject, ) {} + @ResolveField(() => String, { nullable: true }) + async labelPlural( + @Parent() objectMetadata: ObjectMetadataDTO, + @Context() context, + ): Promise { + return this.objectMetadataService.resolveTranslatableString( + objectMetadata, + 'labelPlural', + context.req.headers['x-locale'], + ); + } + + @ResolveField(() => String, { nullable: true }) + async labelSingular( + @Parent() objectMetadata: ObjectMetadataDTO, + @Context() context, + ): Promise { + return this.objectMetadataService.resolveTranslatableString( + objectMetadata, + 'labelSingular', + context.req.headers['x-locale'], + ); + } + @Mutation(() => ObjectMetadataDTO) async deleteOneObject( @Args('input') input: DeleteOneObjectInput, diff --git a/packages/twenty-server/src/engine/metadata-modules/object-metadata/object-metadata.service.ts b/packages/twenty-server/src/engine/metadata-modules/object-metadata/object-metadata.service.ts index 4021a3c73..ddb8acb3c 100644 --- a/packages/twenty-server/src/engine/metadata-modules/object-metadata/object-metadata.service.ts +++ b/packages/twenty-server/src/engine/metadata-modules/object-metadata/object-metadata.service.ts @@ -3,6 +3,7 @@ import { InjectRepository } from '@nestjs/typeorm'; import console from 'console'; +import { i18n } from '@lingui/core'; import { Query, QueryOptions } from '@ptc-org/nestjs-query-core'; import { TypeOrmQueryService } from '@ptc-org/nestjs-query-typeorm'; import { isDefined } from 'class-validator'; @@ -14,6 +15,7 @@ import { DataSourceService } from 'src/engine/metadata-modules/data-source/data- import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity'; import { IndexMetadataService } from 'src/engine/metadata-modules/index-metadata/index-metadata.service'; import { DeleteOneObjectInput } from 'src/engine/metadata-modules/object-metadata/dtos/delete-object.input'; +import { ObjectMetadataDTO } from 'src/engine/metadata-modules/object-metadata/dtos/object-metadata.dto'; import { UpdateOneObjectInput } from 'src/engine/metadata-modules/object-metadata/dtos/update-object.input'; import { ObjectMetadataException, @@ -533,4 +535,18 @@ export class ObjectMetadataService extends TypeOrmQueryService { + if (objectMetadata.isCustom) { + return objectMetadata[labelKey]; + } + + i18n.activate(locale); + + return i18n._(objectMetadata[labelKey]); + } } diff --git a/packages/twenty-server/src/engine/middlewares/graphql-hydrate-request-from-token.middleware.ts b/packages/twenty-server/src/engine/middlewares/graphql-hydrate-request-from-token.middleware.ts index 8bd394712..4a986e832 100644 --- a/packages/twenty-server/src/engine/middlewares/graphql-hydrate-request-from-token.middleware.ts +++ b/packages/twenty-server/src/engine/middlewares/graphql-hydrate-request-from-token.middleware.ts @@ -42,7 +42,6 @@ export class GraphQLHydrateRequestFromTokenMiddleware const excludedOperations = [ 'GetClientConfig', - 'GetCurrentUser', 'GetWorkspaceFromInviteHash', 'Track', 'CheckUserExists', diff --git a/packages/twenty-server/src/engine/twenty-orm/custom.workspace-entity.ts b/packages/twenty-server/src/engine/twenty-orm/custom.workspace-entity.ts index 637d9be07..0ea5a04d9 100644 --- a/packages/twenty-server/src/engine/twenty-orm/custom.workspace-entity.ts +++ b/packages/twenty-server/src/engine/twenty-orm/custom.workspace-entity.ts @@ -150,5 +150,5 @@ export class CustomWorkspaceEntity extends BaseWorkspaceEntity { @WorkspaceIsNullable() @WorkspaceIsSystem() @WorkspaceFieldIndex({ indexType: IndexType.GIN }) - [SEARCH_VECTOR_FIELD.name]: any; + searchVector: any; } diff --git a/packages/twenty-server/src/engine/twenty-orm/decorators/workspace-entity.decorator.ts b/packages/twenty-server/src/engine/twenty-orm/decorators/workspace-entity.decorator.ts index 002752642..1cc876514 100644 --- a/packages/twenty-server/src/engine/twenty-orm/decorators/workspace-entity.decorator.ts +++ b/packages/twenty-server/src/engine/twenty-orm/decorators/workspace-entity.decorator.ts @@ -1,3 +1,5 @@ +import { MessageDescriptor } from '@lingui/core'; + import { metadataArgsStorage } from 'src/engine/twenty-orm/storage/metadata-args.storage'; import { BASE_OBJECT_STANDARD_FIELD_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids'; import { convertClassNameToObjectMetadataName } from 'src/engine/workspace-manager/workspace-sync-metadata/utils/convert-class-to-object-metadata-name.util'; @@ -6,9 +8,9 @@ import { TypedReflect } from 'src/utils/typed-reflect'; interface WorkspaceEntityOptions { standardId: string; namePlural: string; - labelSingular: string; - labelPlural: string; - description?: string; + labelSingular: MessageDescriptor | string; // Todo: remove string when translations are added + labelPlural: MessageDescriptor | string; // Todo: remove string when translations are added + description?: MessageDescriptor | string; // Todo: remove string when translations are added icon?: string; shortcut?: string; labelIdentifierStandardId?: string; @@ -38,9 +40,18 @@ export function WorkspaceEntity( standardId: options.standardId, nameSingular: objectName, namePlural: options.namePlural, - labelSingular: options.labelSingular, - labelPlural: options.labelPlural, - description: options.description, + labelSingular: + typeof options.labelSingular === 'string' + ? options.labelSingular + : (options.labelSingular?.message ?? ''), + labelPlural: + typeof options.labelPlural === 'string' + ? options.labelPlural + : (options.labelPlural?.message ?? ''), + description: + typeof options.description === 'string' + ? options.description + : (options.description?.message ?? ''), labelIdentifierStandardId: options.labelIdentifierStandardId ?? BASE_OBJECT_STANDARD_FIELD_IDS.id, imageIdentifierStandardId: options.imageIdentifierStandardId ?? null, diff --git a/packages/twenty-server/src/modules/company/standard-objects/company.workspace-entity.ts b/packages/twenty-server/src/modules/company/standard-objects/company.workspace-entity.ts index d93ea689d..2a9e29a87 100644 --- a/packages/twenty-server/src/modules/company/standard-objects/company.workspace-entity.ts +++ b/packages/twenty-server/src/modules/company/standard-objects/company.workspace-entity.ts @@ -1,3 +1,4 @@ +import { msg } from '@lingui/core/macro'; import { FieldMetadataType } from 'twenty-shared'; import { Relation } from 'src/engine/workspace-manager/workspace-sync-metadata/interfaces/relation.interface'; @@ -52,9 +53,9 @@ export const SEARCH_FIELDS_FOR_COMPANY: FieldTypeAndNameMetadata[] = [ @WorkspaceEntity({ standardId: STANDARD_OBJECT_IDS.company, namePlural: 'companies', - labelSingular: 'Company', - labelPlural: 'Companies', - description: 'A company', + labelSingular: msg`Company`, + labelPlural: msg`Companies`, + description: msg`A company`, icon: STANDARD_OBJECT_ICONS.company, shortcut: 'C', labelIdentifierStandardId: COMPANY_STANDARD_FIELD_IDS.name, @@ -67,7 +68,7 @@ export class CompanyWorkspaceEntity extends BaseWorkspaceEntity { description: 'The company name', icon: 'IconBuildingSkyscraper', }) - [NAME_FIELD_NAME]: string; + name: string; @WorkspaceField({ standardId: COMPANY_STANDARD_FIELD_IDS.domainName, @@ -78,7 +79,7 @@ export class CompanyWorkspaceEntity extends BaseWorkspaceEntity { icon: 'IconLink', }) @WorkspaceIsUnique() - [DOMAIN_NAME_FIELD_NAME]?: LinksMetadata; + domainName: LinksMetadata; @WorkspaceField({ standardId: COMPANY_STANDARD_FIELD_IDS.employees, @@ -294,5 +295,5 @@ export class CompanyWorkspaceEntity extends BaseWorkspaceEntity { @WorkspaceIsNullable() @WorkspaceIsSystem() @WorkspaceFieldIndex({ indexType: IndexType.GIN }) - [SEARCH_VECTOR_FIELD.name]: any; + searchVector: any; } diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/exceptions/message-import-driver.exception.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/exceptions/message-import-driver.exception.ts index a0fc4cb04..8d0d0473f 100644 --- a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/exceptions/message-import-driver.exception.ts +++ b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/exceptions/message-import-driver.exception.ts @@ -2,8 +2,10 @@ import { CustomException } from 'src/utils/custom-exception'; export class MessageImportDriverException extends CustomException { code: MessageImportDriverExceptionCode; + constructor(message: string, code: MessageImportDriverExceptionCode) { super(message, code); + this.code = code; } } diff --git a/packages/twenty-server/src/modules/note/standard-objects/note.workspace-entity.ts b/packages/twenty-server/src/modules/note/standard-objects/note.workspace-entity.ts index bff384c2d..86c502aab 100644 --- a/packages/twenty-server/src/modules/note/standard-objects/note.workspace-entity.ts +++ b/packages/twenty-server/src/modules/note/standard-objects/note.workspace-entity.ts @@ -68,7 +68,7 @@ export class NoteWorkspaceEntity extends BaseWorkspaceEntity { description: 'Note title', icon: 'IconNotes', }) - [TITLE_FIELD_NAME]: string; + title: string; @WorkspaceField({ standardId: NOTE_STANDARD_FIELD_IDS.body, @@ -78,7 +78,7 @@ export class NoteWorkspaceEntity extends BaseWorkspaceEntity { icon: 'IconFilePencil', }) @WorkspaceIsNullable() - [BODY_FIELD_NAME]: string | null; + body: string | null; @WorkspaceField({ standardId: NOTE_STANDARD_FIELD_IDS.createdBy, @@ -155,5 +155,5 @@ export class NoteWorkspaceEntity extends BaseWorkspaceEntity { @WorkspaceIsNullable() @WorkspaceIsSystem() @WorkspaceFieldIndex({ indexType: IndexType.GIN }) - [SEARCH_VECTOR_FIELD.name]: any; + searchVector: any; } diff --git a/packages/twenty-server/src/modules/opportunity/standard-objects/opportunity.workspace-entity.ts b/packages/twenty-server/src/modules/opportunity/standard-objects/opportunity.workspace-entity.ts index a3038d2ba..5ee7bab52 100644 --- a/packages/twenty-server/src/modules/opportunity/standard-objects/opportunity.workspace-entity.ts +++ b/packages/twenty-server/src/modules/opportunity/standard-objects/opportunity.workspace-entity.ts @@ -248,5 +248,5 @@ export class OpportunityWorkspaceEntity extends BaseWorkspaceEntity { @WorkspaceIsNullable() @WorkspaceIsSystem() @WorkspaceFieldIndex({ indexType: IndexType.GIN }) - [SEARCH_VECTOR_FIELD.name]: any; + searchVector: any; } diff --git a/packages/twenty-server/src/modules/person/standard-objects/person.workspace-entity.ts b/packages/twenty-server/src/modules/person/standard-objects/person.workspace-entity.ts index 9ba4c3139..e894590d3 100644 --- a/packages/twenty-server/src/modules/person/standard-objects/person.workspace-entity.ts +++ b/packages/twenty-server/src/modules/person/standard-objects/person.workspace-entity.ts @@ -73,7 +73,7 @@ export class PersonWorkspaceEntity extends BaseWorkspaceEntity { icon: 'IconUser', }) @WorkspaceIsNullable() - [NAME_FIELD_NAME]: FullNameMetadata | null; + name: FullNameMetadata | null; @WorkspaceField({ standardId: PERSON_STANDARD_FIELD_IDS.emails, @@ -83,7 +83,7 @@ export class PersonWorkspaceEntity extends BaseWorkspaceEntity { icon: 'IconMail', }) @WorkspaceIsUnique() - [EMAILS_FIELD_NAME]: EmailsMetadata; + emails: EmailsMetadata; @WorkspaceField({ standardId: PERSON_STANDARD_FIELD_IDS.linkedinLink, @@ -112,7 +112,7 @@ export class PersonWorkspaceEntity extends BaseWorkspaceEntity { description: 'Contact’s job title', icon: 'IconBriefcase', }) - [JOB_TITLE_FIELD_NAME]: string; + jobTitle: string; @WorkspaceField({ standardId: PERSON_STANDARD_FIELD_IDS.phone, @@ -304,5 +304,5 @@ export class PersonWorkspaceEntity extends BaseWorkspaceEntity { @WorkspaceIsNullable() @WorkspaceIsSystem() @WorkspaceFieldIndex({ indexType: IndexType.GIN }) - [SEARCH_VECTOR_FIELD.name]: any; + searchVector: any; } diff --git a/packages/twenty-server/src/modules/task/standard-objects/task.workspace-entity.ts b/packages/twenty-server/src/modules/task/standard-objects/task.workspace-entity.ts index 73557f806..1c645ea3f 100644 --- a/packages/twenty-server/src/modules/task/standard-objects/task.workspace-entity.ts +++ b/packages/twenty-server/src/modules/task/standard-objects/task.workspace-entity.ts @@ -70,7 +70,7 @@ export class TaskWorkspaceEntity extends BaseWorkspaceEntity { description: 'Task title', icon: 'IconNotes', }) - [TITLE_FIELD_NAME]: string; + title: string; @WorkspaceField({ standardId: TASK_STANDARD_FIELD_IDS.body, @@ -80,7 +80,7 @@ export class TaskWorkspaceEntity extends BaseWorkspaceEntity { icon: 'IconFilePencil', }) @WorkspaceIsNullable() - [BODY_FIELD_NAME]: string | null; + body: string | null; @WorkspaceField({ standardId: TASK_STANDARD_FIELD_IDS.dueAt, @@ -207,5 +207,5 @@ export class TaskWorkspaceEntity extends BaseWorkspaceEntity { @WorkspaceIsNullable() @WorkspaceIsSystem() @WorkspaceFieldIndex({ indexType: IndexType.GIN }) - [SEARCH_VECTOR_FIELD.name]: any; + searchVector: any; } diff --git a/packages/twenty-server/src/modules/workspace-member/standard-objects/workspace-member.workspace-entity.ts b/packages/twenty-server/src/modules/workspace-member/standard-objects/workspace-member.workspace-entity.ts index 4e39f9ff8..233c278ca 100644 --- a/packages/twenty-server/src/modules/workspace-member/standard-objects/workspace-member.workspace-entity.ts +++ b/packages/twenty-server/src/modules/workspace-member/standard-objects/workspace-member.workspace-entity.ts @@ -88,7 +88,7 @@ export class WorkspaceMemberWorkspaceEntity extends BaseWorkspaceEntity { description: 'Workspace member name', icon: 'IconCircleUser', }) - [NAME_FIELD_NAME]: FullNameMetadata; + name: FullNameMetadata; @WorkspaceField({ standardId: WORKSPACE_MEMBER_STANDARD_FIELD_IDS.colorScheme, @@ -126,7 +126,7 @@ export class WorkspaceMemberWorkspaceEntity extends BaseWorkspaceEntity { description: 'Related user email address', icon: 'IconMail', }) - [USER_EMAIL_FIELD_NAME]: string; + userEmail: string; @WorkspaceField({ standardId: WORKSPACE_MEMBER_STANDARD_FIELD_IDS.userId, @@ -351,5 +351,5 @@ export class WorkspaceMemberWorkspaceEntity extends BaseWorkspaceEntity { @WorkspaceIsNullable() @WorkspaceIsSystem() @WorkspaceFieldIndex({ indexType: IndexType.GIN }) - [SEARCH_VECTOR_FIELD.name]: any; + searchVector: any; } diff --git a/yarn.lock b/yarn.lock index 9fe620698..3729e0e8a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -15192,9 +15192,9 @@ __metadata: languageName: node linkType: hard -"@swc/core-darwin-arm64@npm:1.3.107": - version: 1.3.107 - resolution: "@swc/core-darwin-arm64@npm:1.3.107" +"@swc/core-darwin-arm64@npm:1.7.42": + version: 1.7.42 + resolution: "@swc/core-darwin-arm64@npm:1.7.42" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard @@ -15206,9 +15206,9 @@ __metadata: languageName: node linkType: hard -"@swc/core-darwin-x64@npm:1.3.107": - version: 1.3.107 - resolution: "@swc/core-darwin-x64@npm:1.3.107" +"@swc/core-darwin-x64@npm:1.7.42": + version: 1.7.42 + resolution: "@swc/core-darwin-x64@npm:1.7.42" conditions: os=darwin & cpu=x64 languageName: node linkType: hard @@ -15220,9 +15220,9 @@ __metadata: languageName: node linkType: hard -"@swc/core-linux-arm-gnueabihf@npm:1.3.107": - version: 1.3.107 - resolution: "@swc/core-linux-arm-gnueabihf@npm:1.3.107" +"@swc/core-linux-arm-gnueabihf@npm:1.7.42": + version: 1.7.42 + resolution: "@swc/core-linux-arm-gnueabihf@npm:1.7.42" conditions: os=linux & cpu=arm languageName: node linkType: hard @@ -15234,9 +15234,9 @@ __metadata: languageName: node linkType: hard -"@swc/core-linux-arm64-gnu@npm:1.3.107": - version: 1.3.107 - resolution: "@swc/core-linux-arm64-gnu@npm:1.3.107" +"@swc/core-linux-arm64-gnu@npm:1.7.42": + version: 1.7.42 + resolution: "@swc/core-linux-arm64-gnu@npm:1.7.42" conditions: os=linux & cpu=arm64 & libc=glibc languageName: node linkType: hard @@ -15248,9 +15248,9 @@ __metadata: languageName: node linkType: hard -"@swc/core-linux-arm64-musl@npm:1.3.107": - version: 1.3.107 - resolution: "@swc/core-linux-arm64-musl@npm:1.3.107" +"@swc/core-linux-arm64-musl@npm:1.7.42": + version: 1.7.42 + resolution: "@swc/core-linux-arm64-musl@npm:1.7.42" conditions: os=linux & cpu=arm64 & libc=musl languageName: node linkType: hard @@ -15262,9 +15262,9 @@ __metadata: languageName: node linkType: hard -"@swc/core-linux-x64-gnu@npm:1.3.107": - version: 1.3.107 - resolution: "@swc/core-linux-x64-gnu@npm:1.3.107" +"@swc/core-linux-x64-gnu@npm:1.7.42": + version: 1.7.42 + resolution: "@swc/core-linux-x64-gnu@npm:1.7.42" conditions: os=linux & cpu=x64 & libc=glibc languageName: node linkType: hard @@ -15276,9 +15276,9 @@ __metadata: languageName: node linkType: hard -"@swc/core-linux-x64-musl@npm:1.3.107": - version: 1.3.107 - resolution: "@swc/core-linux-x64-musl@npm:1.3.107" +"@swc/core-linux-x64-musl@npm:1.7.42": + version: 1.7.42 + resolution: "@swc/core-linux-x64-musl@npm:1.7.42" conditions: os=linux & cpu=x64 & libc=musl languageName: node linkType: hard @@ -15290,9 +15290,9 @@ __metadata: languageName: node linkType: hard -"@swc/core-win32-arm64-msvc@npm:1.3.107": - version: 1.3.107 - resolution: "@swc/core-win32-arm64-msvc@npm:1.3.107" +"@swc/core-win32-arm64-msvc@npm:1.7.42": + version: 1.7.42 + resolution: "@swc/core-win32-arm64-msvc@npm:1.7.42" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard @@ -15304,9 +15304,9 @@ __metadata: languageName: node linkType: hard -"@swc/core-win32-ia32-msvc@npm:1.3.107": - version: 1.3.107 - resolution: "@swc/core-win32-ia32-msvc@npm:1.3.107" +"@swc/core-win32-ia32-msvc@npm:1.7.42": + version: 1.7.42 + resolution: "@swc/core-win32-ia32-msvc@npm:1.7.42" conditions: os=win32 & cpu=ia32 languageName: node linkType: hard @@ -15318,9 +15318,9 @@ __metadata: languageName: node linkType: hard -"@swc/core-win32-x64-msvc@npm:1.3.107": - version: 1.3.107 - resolution: "@swc/core-win32-x64-msvc@npm:1.3.107" +"@swc/core-win32-x64-msvc@npm:1.7.42": + version: 1.7.42 + resolution: "@swc/core-win32-x64-msvc@npm:1.7.42" conditions: os=win32 & cpu=x64 languageName: node linkType: hard @@ -15332,6 +15332,52 @@ __metadata: languageName: node linkType: hard +"@swc/core@npm:1.7.42": + version: 1.7.42 + resolution: "@swc/core@npm:1.7.42" + dependencies: + "@swc/core-darwin-arm64": "npm:1.7.42" + "@swc/core-darwin-x64": "npm:1.7.42" + "@swc/core-linux-arm-gnueabihf": "npm:1.7.42" + "@swc/core-linux-arm64-gnu": "npm:1.7.42" + "@swc/core-linux-arm64-musl": "npm:1.7.42" + "@swc/core-linux-x64-gnu": "npm:1.7.42" + "@swc/core-linux-x64-musl": "npm:1.7.42" + "@swc/core-win32-arm64-msvc": "npm:1.7.42" + "@swc/core-win32-ia32-msvc": "npm:1.7.42" + "@swc/core-win32-x64-msvc": "npm:1.7.42" + "@swc/counter": "npm:^0.1.3" + "@swc/types": "npm:^0.1.13" + peerDependencies: + "@swc/helpers": "*" + dependenciesMeta: + "@swc/core-darwin-arm64": + optional: true + "@swc/core-darwin-x64": + optional: true + "@swc/core-linux-arm-gnueabihf": + optional: true + "@swc/core-linux-arm64-gnu": + optional: true + "@swc/core-linux-arm64-musl": + optional: true + "@swc/core-linux-x64-gnu": + optional: true + "@swc/core-linux-x64-musl": + optional: true + "@swc/core-win32-arm64-msvc": + optional: true + "@swc/core-win32-ia32-msvc": + optional: true + "@swc/core-win32-x64-msvc": + optional: true + peerDependenciesMeta: + "@swc/helpers": + optional: true + checksum: 10c0/bf5e242ad4098b5f02c084460fdaac486b420fb3b2ee8e96271e471c100ac6f446cc27c3840eed106130be149e20c8165df23ba5fe1a89364650c07aaa507177 + languageName: node + linkType: hard + "@swc/core@npm:^1.3.18, @swc/core@npm:^1.5.7": version: 1.7.6 resolution: "@swc/core@npm:1.7.6" @@ -15378,53 +15424,7 @@ __metadata: languageName: node linkType: hard -"@swc/core@npm:~1.3.100": - version: 1.3.107 - resolution: "@swc/core@npm:1.3.107" - dependencies: - "@swc/core-darwin-arm64": "npm:1.3.107" - "@swc/core-darwin-x64": "npm:1.3.107" - "@swc/core-linux-arm-gnueabihf": "npm:1.3.107" - "@swc/core-linux-arm64-gnu": "npm:1.3.107" - "@swc/core-linux-arm64-musl": "npm:1.3.107" - "@swc/core-linux-x64-gnu": "npm:1.3.107" - "@swc/core-linux-x64-musl": "npm:1.3.107" - "@swc/core-win32-arm64-msvc": "npm:1.3.107" - "@swc/core-win32-ia32-msvc": "npm:1.3.107" - "@swc/core-win32-x64-msvc": "npm:1.3.107" - "@swc/counter": "npm:^0.1.1" - "@swc/types": "npm:^0.1.5" - peerDependencies: - "@swc/helpers": ^0.5.0 - dependenciesMeta: - "@swc/core-darwin-arm64": - optional: true - "@swc/core-darwin-x64": - optional: true - "@swc/core-linux-arm-gnueabihf": - optional: true - "@swc/core-linux-arm64-gnu": - optional: true - "@swc/core-linux-arm64-musl": - optional: true - "@swc/core-linux-x64-gnu": - optional: true - "@swc/core-linux-x64-musl": - optional: true - "@swc/core-win32-arm64-msvc": - optional: true - "@swc/core-win32-ia32-msvc": - optional: true - "@swc/core-win32-x64-msvc": - optional: true - peerDependenciesMeta: - "@swc/helpers": - optional: true - checksum: 10c0/1f5c3b42443f7437e8b46621db6078babf292cc0855d83b2c45f43fd57a7af098243d9f5e2cdebc5fd5219ec8d9c0429cc17601497d7e301336d104618f775b2 - languageName: node - linkType: hard - -"@swc/counter@npm:^0.1.1, @swc/counter@npm:^0.1.3": +"@swc/counter@npm:^0.1.3": version: 0.1.3 resolution: "@swc/counter@npm:0.1.3" checksum: 10c0/8424f60f6bf8694cfd2a9bca45845bce29f26105cda8cf19cdb9fd3e78dc6338699e4db77a89ae449260bafa1cc6bec307e81e7fb96dbf7dcfce0eea55151356 @@ -15481,7 +15481,7 @@ __metadata: languageName: node linkType: hard -"@swc/types@npm:^0.1.12, @swc/types@npm:^0.1.5": +"@swc/types@npm:^0.1.12": version: 0.1.12 resolution: "@swc/types@npm:0.1.12" dependencies: @@ -15490,6 +15490,15 @@ __metadata: languageName: node linkType: hard +"@swc/types@npm:^0.1.13": + version: 0.1.17 + resolution: "@swc/types@npm:0.1.17" + dependencies: + "@swc/counter": "npm:^0.1.3" + checksum: 10c0/29f5c8933a16042956f1adb7383e836ed7646cbf679826e78b53fdd0c08e8572cb42152e527b6b530a9bd1052d33d0972f90f589761ccd252c12652c9b7a72fc + languageName: node + linkType: hard + "@szmarczak/http-timer@npm:^1.1.2": version: 1.1.2 resolution: "@szmarczak/http-timer@npm:1.1.2" @@ -45891,7 +45900,7 @@ __metadata: "@stylistic/eslint-plugin": "npm:^1.5.0" "@swc-node/register": "npm:1.8.0" "@swc/cli": "npm:^0.3.12" - "@swc/core": "npm:~1.3.100" + "@swc/core": "npm:1.7.42" "@swc/helpers": "npm:~0.5.2" "@swc/jest": "npm:^0.2.29" "@tabler/icons-react": "npm:^2.44.0"