security: strip Node dev headers from images + lingui 5.9.5 (drops vulnerable esbuild) (#21448)

## Context

AWS Inspector flags the `prod-twenty` image (built from current main)
with 16 findings, and Dependabot alert 174 flags esbuild. This PR fixes
the OpenSSL scanner findings and the esbuild CVE. The typeorm bump
(CVE-2025-60542) was **pulled out of this PR** — see "typeorm status"
below.

## Changes

### Strip `/usr/local/include/node` from runtime stages
(`twenty-server`, `twenty-app-dev`)
15 OpenSSL CVEs (June 9 advisory, incl. CRITICAL CVE-2026-34182) are all
detected via **Node's bundled OpenSSL dev headers**: 3 GENERIC
`openssl/openssl` 3.5.6 detections per CVE at
`/usr/local/include/node/openssl/archs/linux-x86_64/{asm,asm_avx2,no-asm}/include/openssl/opensslv.h`.
The headers are only needed by node-gyp and native addons are compiled
in the build stages — nothing compiles at runtime. Dropping them clears
all 45 detection instances and permanently ends this class of finding
(third occurrence: 3.5.5 → 3.5.6 → 3.5.7). None of these CVEs are
reachable through Node (no CMS/PKCS#7 API, `pfx` is operator-supplied,
Node's QUIC uses ngtcp2, ASN.1 issues need ~2GB inputs).

**Follow-up (~June 17, 2026):** the `node` binary itself still
statically links OpenSSL 3.5.6 — invisible to the scanner after this PR
and unreachable in practice, but the real fix is bumping the pinned
`node:24-alpine` digest once the [announced June 17 Node.js security
releases](https://nodejs.org/en/blog/vulnerability/june-2026-security-releases)
ship a 24.x linking OpenSSL ≥ 3.5.7 (verify via
`deps/openssl/openssl/VERSION.dat` on the release tag — 24.16.0 is still
on 3.5.6). A dated TODO sits next to the cleanup in the Dockerfile.

### esbuild dev-server CORS CVE (Dependabot alert 174,
GHSA-67mh-4wv8-2f99)
`@lingui/cli@5.1.2` (pins `esbuild ^0.21.5`) was the last parent
resolving a vulnerable esbuild (≤ 0.24.2 lets any website send requests
to the dev server and read responses). Instead of a resolution override,
this bumps the lockstepped **lingui suite 5.1.2 → 5.9.5** (within-major;
lingui adopted `esbuild ^0.25.1` in 5.4.1), which:

- removes `esbuild@0.21.5` and all its platform packages from the
lockfile with no forced ranges;
- drops the `@lingui/core` lockstep resolution (its comment marked it
droppable on the next coordinated lingui bump — the tree now resolves a
single `@lingui/core@5.9.5`);
- `@lingui/swc-plugin` stays at `^5.11.0` (peers on `@lingui/core: 5`;
its 6.x line targets lingui 6).

**lingui 5.9.5 behavioral fallout handled here:**
- Translation functions now **throw without an active locale** (5.1.2
fell back silently). The global `i18n` singleton that backs server-side
`` t`…` `` calls only had a messages compiler set, never an activated
locale → activate the source locale in `I18nService.loadTranslations()`,
mirrored in the server jest setup (unit tests bypass Nest bootstrap).
- `msg`/`t` placeholders are now strictly typed (reject
`null`/`undefined`/`unknown`) → one server call site and 16 twenty-front
files adapted with minimal nullish-coalescing fixes that preserve
rendering.
- `.po`/compiled-catalog churn from the new extractor/compiler
(reference reordering, sorted keys — verified content-identical on
unchanged `.po` inputs) is intentionally not committed: the scheduled
i18n workflows regenerate those.

## typeorm status (pulled out)

typeorm 0.3.20 → 0.3.26 was originally in this PR but **made workspace
metadata sync intermittently lossy**: `example-app-postcard` failed
twice with a *different* field missing from the synced PostCard object
each run, and one integration shard's `DataSeedWorkspaceCommand` died
with "Could not find flat entity with universal identifier …" — versus
zero such failures on recent main. Local runs (db reset + seed, group-by
integration suite 19/19) pass, so it is a nondeterministic
CI-load-sensitive regression that needs dedicated debugging (typeorm
changed LIMIT/OFFSET 0 semantics, lazy count for `getManyAndCount`,
upsert WHERE construction, and topological-sort internals in that
range). The resolutions comment documents this as the blocker;
CVE-2025-60542 is MySQL-driver-only (`sqlstring`), so Postgres-only
Twenty is not exposed in the meantime.

## Verification

- `npx nx typecheck twenty-server` / `twenty-front` — clean (no cache)
- `npx nx test twenty-server` — full suite green
- `lingui:extract` + `lingui:compile` — clean for twenty-server /
twenty-emails / twenty-front
- `oxfmt --check` — clean for both packages
- Lockfile diff: lingui 5.9.5 entries, `esbuild@0.21.5` +
`@esbuild/*@0.21.5` platform packages removed, no typeorm changes
This commit is contained in:
Charles Bochet
2026-06-11 15:11:29 +02:00
committed by GitHub
parent 303c415dd1
commit 184c4948d6
27 changed files with 343 additions and 346 deletions
+1 -2
View File
@@ -28,7 +28,6 @@
"resolutions": {
"graphql": "16.8.1",
"graphql-redis-subscriptions/ioredis": "5.10.1",
"@lingui/core": "5.1.2",
"@types/qs": "6.9.16",
"@opentelemetry/api": "1.9.1",
"chokidar": "^3.6.0",
@@ -49,7 +48,7 @@
"googleapis-common/uuid": "11.1.1",
"@cyntler/react-doc-viewer/ajv": "8.20.0"
},
"//resolutions": "Each entry is load-bearing: it forces a version OUTSIDE some parent's declared range where no fixed upstream release exists; remove each once its blocker ships. graphql 16.8.1 -> singleton pin held below msw's ^16.12.0 dep and @nestjs/graphql's ^16.11.0 peer; drop after a validated repo-wide bump to latest 16.x; graphql-redis-subscriptions/ioredis 5.10.1 -> TS type-identity dedup: twenty-server passes its ioredis client into RedisPubSub, so this must equal the exact ioredis version pinned by twenty-server and bullmq (bump in lockstep); @lingui/core 5.1.2 -> lockstep with the @lingui/* 5.1.2 suite, drop in the next coordinated lingui bump; @types/qs 6.9.16 -> holdback below the 6.9.17 ParsedQs typing break (node-saml wants ^6.9.18); @opentelemetry/api 1.9.1 -> singleton guard for the NoopMeterProvider bug (#20231): ai 6.0.x pins 1.9.0 exact vs @sentry/node ^1.9.1, drop when workspace ai >=6.0.178 AND @scalar/agent-chat moves off ai 6.0.33; chokidar ^3 -> NestJS CLI watch needs fsevents on macOS, removed in chokidar 4/5 (#20316); tmp ^0.2.7 -> CVE, zapier-platform-cli 19 (latest) pins 0.2.5 and inquirer 7/8's external-editor wants ^0.0.33; make-fetch-happen ^15 + @electron/{rebuild,node-gyp}/tar ^7.5.16 -> tar CVE eviction for the @electron/rebuild 3.x toolchain (rebuild 3.x pins tar ^6, its node-gyp fork pins tar ^6.2.1 + mfh ^10), drop when electron-forge declares @electron/rebuild >=4; @angular-devkit/core 19.2.24 -> picomatch CVE, blocked on @nestjs/cli >11.0.23 fixing the dist/src output regression (repo held at 11.0.16); yeoman-environment 6.0.1 -> CVE, zapier-platform-cli 19 (latest) pins 4.4.3; webpack-dev-server 5.2.4 -> CVE, @electron-forge/plugin-webpack (incl. 8.x alphas) still declares ^4; express/qs + @cypress/request/qs 6.15.2 -> qs CVE for old express 4.22.0/4.22.1 pinned by @mintlify/previewing and verdaccio (verdaccio also pins @cypress/request 3.0.10; all other qs parents resolve safe naturally); next/postcss 8.5.15 -> postcss CVE, every stable next pins 8.4.31 exact (fix only in 16.3.0 canaries; @react-email/ui also pins next 16.2.6); <pkg>/uuid 11.1.1 -> uuid CVE for parents pinning uuid <11 with no fixed release (sockjs dormant since 2021; @cypress/request 3.0.10 via verdaccio; @ptc-org/nestjs-query-typeorm at latest; typeorm 0.3.20 patch:dep -- droppable by bumping to 0.3.29+ and re-rolling the patch; googleapis 105 -> common 8 drops uuid but needs the googleapis >=152 migration). Preserves the intentional uuid 13.x; @cyntler/react-doc-viewer/ajv 8.20.0 -> CVE, upstream (latest 1.17.1) pins ajv ^7 but never imports it, forcing v8 is safe",
"//resolutions": "Each entry is load-bearing: it forces a version OUTSIDE some parent's declared range where no fixed upstream release exists; remove each once its blocker ships. graphql 16.8.1 -> singleton pin held below msw's ^16.12.0 dep and @nestjs/graphql's ^16.11.0 peer; drop after a validated repo-wide bump to latest 16.x; graphql-redis-subscriptions/ioredis 5.10.1 -> TS type-identity dedup: twenty-server passes its ioredis client into RedisPubSub, so this must equal the exact ioredis version pinned by twenty-server and bullmq (bump in lockstep); @types/qs 6.9.16 -> holdback below the 6.9.17 ParsedQs typing break (node-saml wants ^6.9.18); @opentelemetry/api 1.9.1 -> singleton guard for the NoopMeterProvider bug (#20231): ai 6.0.x pins 1.9.0 exact vs @sentry/node ^1.9.1, drop when workspace ai >=6.0.178 AND @scalar/agent-chat moves off ai 6.0.33; chokidar ^3 -> NestJS CLI watch needs fsevents on macOS, removed in chokidar 4/5 (#20316); tmp ^0.2.7 -> CVE, zapier-platform-cli 19 (latest) pins 0.2.5 and inquirer 7/8's external-editor wants ^0.0.33; make-fetch-happen ^15 + @electron/{rebuild,node-gyp}/tar ^7.5.16 -> tar CVE eviction for the @electron/rebuild 3.x toolchain (rebuild 3.x pins tar ^6, its node-gyp fork pins tar ^6.2.1 + mfh ^10), drop when electron-forge declares @electron/rebuild >=4; @angular-devkit/core 19.2.24 -> picomatch CVE, blocked on @nestjs/cli >11.0.23 fixing the dist/src output regression (repo held at 11.0.16); yeoman-environment 6.0.1 -> CVE, zapier-platform-cli 19 (latest) pins 4.4.3; webpack-dev-server 5.2.4 -> CVE, @electron-forge/plugin-webpack (incl. 8.x alphas) still declares ^4; express/qs + @cypress/request/qs 6.15.2 -> qs CVE for old express 4.22.0/4.22.1 pinned by @mintlify/previewing and verdaccio (verdaccio also pins @cypress/request 3.0.10; all other qs parents resolve safe naturally); next/postcss 8.5.15 -> postcss CVE, every stable next pins 8.4.31 exact (fix only in 16.3.0 canaries; @react-email/ui also pins next 16.2.6); <pkg>/uuid 11.1.1 -> uuid CVE for parents pinning uuid <11 with no fixed release (sockjs dormant since 2021; @cypress/request 3.0.10 via verdaccio; @ptc-org/nestjs-query-typeorm at latest; typeorm 0.3.20 patch:dep -- droppable by bumping to 0.3.26+ and re-rolling the patch, but 0.3.26 made workspace metadata sync intermittently lossy (see #21448), debug before retrying; googleapis 105 -> common 8 drops uuid but needs the googleapis >=152 migration). Preserves the intentional uuid 13.x; @cyntler/react-doc-viewer/ajv 8.20.0 -> CVE, upstream (latest 1.17.1) pins ajv ^7 but never imports it, forcing v8 is safe",
"version": "0.2.1",
"nx": {},
"scripts": {
+13 -4
View File
@@ -135,8 +135,15 @@ LABEL org.opencontainers.image.description="Twenty server image (no frontend)."
# - example/ apps vendored inside dependencies, e.g. passport-microsoft's
# example/login ships a package-lock.json for an old Express demo
# (body-parser, ejs, express, ...) that is never installed or run.
# (The OpenSSL CVE is fixed properly by the Node base bump above, not by deletion.)
RUN rm -rf /usr/local/lib/node_modules/npm /usr/local/bin/npm /usr/local/bin/npx && \
# - the Node dev headers: only node-gyp needs them and native addons are
# compiled in the build stages; their vendored openssl/opensslv.h is what
# scanners fingerprint whenever OpenSSL patches ahead of Node releases.
# TODO(2026-06-17): the node binary statically links OpenSSL 3.5.6 (June 9
# OpenSSL advisory fixed in 3.5.7). Bump the pinned node:24-alpine base once
# the announced June 17, 2026 Node security release ships a 24.x linking
# OpenSSL >= 3.5.7 — check deps/openssl/openssl/VERSION.dat on the release tag.
RUN rm -rf /usr/local/lib/node_modules/npm /usr/local/bin/npm /usr/local/bin/npx \
/usr/local/include/node && \
find /app/node_modules -type d -name example -prune -exec rm -rf {} +
RUN mkdir -p /app/.local-storage /app/packages/twenty-server/.local-storage && \
@@ -249,8 +256,10 @@ COPY --from=twenty-server-build /app/packages/twenty-client-sdk/dist /app/packag
# Frontend static build
COPY --from=twenty-front-build /app/packages/twenty-front/build /app/packages/twenty-server/dist/front
# Source maps are not needed in the dev image (no Sentry)
RUN find /app/packages/twenty-server/dist -name '*.js.map' -delete
# Source maps are not needed in the dev image (no Sentry). Node dev headers are
# dropped for the same scanner-hygiene reasons as in twenty-server above.
RUN find /app/packages/twenty-server/dist -name '*.js.map' -delete \
&& rm -rf /usr/local/include/node
# s6 service definitions
COPY packages/twenty-docker/twenty-app-dev/rootfs/ /
+4 -4
View File
@@ -9,8 +9,8 @@
"build": "npx vite build"
},
"dependencies": {
"@lingui/core": "^5.1.2",
"@lingui/react": "^5.1.2",
"@lingui/core": "^5.9.5",
"@lingui/react": "^5.9.5",
"@react-email/components": "^0.5.3",
"twenty-shared": "workspace:*"
},
@@ -19,9 +19,9 @@
"react-dom": "^18.2.0 || ^19.0.0"
},
"devDependencies": {
"@lingui/cli": "^5.1.2",
"@lingui/cli": "^5.9.5",
"@lingui/swc-plugin": "^5.11.0",
"@lingui/vite-plugin": "^5.1.2",
"@lingui/vite-plugin": "^5.9.5",
"@react-email/ui": "6.5.0",
"@tiptap/core": "^3.4.2",
"@types/react": "^19",
+5 -5
View File
@@ -43,9 +43,9 @@
"@graphiql/react": "^0.23.0",
"@hello-pangea/dnd": "^16.2.0",
"@hookform/resolvers": "^5.2.2",
"@lingui/core": "^5.1.2",
"@lingui/detect-locale": "^5.2.0",
"@lingui/react": "^5.1.2",
"@lingui/core": "^5.9.5",
"@lingui/detect-locale": "^5.9.5",
"@lingui/react": "^5.9.5",
"@mantine/core": "^8.3.11",
"@mantine/hooks": "^8.3.11",
"@mantine/utils": "^6.0.22",
@@ -154,9 +154,9 @@
"@graphql-codegen/typed-document-node": "^6.1.8",
"@graphql-codegen/typescript": "^5.0.10",
"@graphql-codegen/typescript-operations": "^5.1.0",
"@lingui/cli": "^5.1.2",
"@lingui/cli": "^5.9.5",
"@lingui/swc-plugin": "^5.11.0",
"@lingui/vite-plugin": "^5.1.2",
"@lingui/vite-plugin": "^5.9.5",
"@playwright/test": "^1.60.0",
"@storybook-community/storybook-addon-cookie": "^5.0.0",
"@storybook/addon-coverage": "^3.0.0",
@@ -71,7 +71,7 @@ export const AIChatNoMoreBillingCreditsBanner = () => {
const message = isTrialing
? t`You've hit your usage limit. Subscribe for more usage.`
: isDefined(nextPrice)
? t`You've hit your usage limit. \nUpgrade to ${nextResourceCreditsAmount} credits for $${nextResourceCreditPrice}/${nextTierInterval}.`
? t`You've hit your usage limit. \nUpgrade to ${nextResourceCreditsAmount ?? ''} credits for $${nextResourceCreditPrice ?? ''}/${nextTierInterval ?? ''}.`
: t`You've hit your usage limit. \nReach to our support team to upgrade.`;
const buttonTitle = isTrialing
@@ -138,7 +138,7 @@ export const AIChatNoMoreBillingCreditsBanner = () => {
<ConfirmationModal
modalInstanceId={AI_CHAT_UPGRADE_CREDIT_PLAN_MODAL_ID}
title={t`Get more credits`}
subtitle={t`Upgrade to ${nextResourceCreditsAmount} credits for $${nextResourceCreditPrice}/${nextTierInterval}.`}
subtitle={t`Upgrade to ${nextResourceCreditsAmount ?? ''} credits for $${nextResourceCreditPrice ?? ''}/${nextTierInterval ?? ''}.`}
onConfirmClick={handleUpgradeConfirm}
confirmButtonText={t`Upgrade`}
confirmButtonAccent="blue"
@@ -25,7 +25,7 @@ export const LogicFunctionExecutionResult = ({
const isError =
logicFunctionTestData.output.status === LogicFunctionExecutionStatus.ERROR;
const duration = logicFunctionTestData.output.duration;
const duration = logicFunctionTestData.output.duration ?? 0;
const status: ExecutionStatus = {
isSuccess,
isError,
@@ -150,7 +150,7 @@ export const AdvancedFilterCompositeSubFieldSelectMenu = ({
});
}}
LeftIcon={getIcon(fieldMetadataItemUsedInDropdown.icon)}
text={t`Any ${fieldLabel} field`}
text={t`Any ${fieldLabel ?? ''} field`}
/>
</SelectableListItem>
)}
@@ -48,8 +48,8 @@ export const RecordGroupMenuItemDraggable = ({
{
Icon: recordGroupDefinition.isVisible ? IconEyeOff : IconEye,
ariaLabel: recordGroupDefinition.isVisible
? t`Hide group ${groupValue}`
: t`Show group ${groupValue}`,
? t`Hide group ${groupValue ?? ''}`
: t`Show group ${groupValue ?? ''}`,
dataTestId: recordGroupDefinition.isVisible
? `hide-group-${recordGroupDefinition.value?.toLowerCase().replace(' ', '-') ?? ''}`
: `show-group-${recordGroupDefinition.value?.toLowerCase().replace(' ', '-') ?? ''}`,
@@ -113,7 +113,7 @@ export const SettingsAdminChatThreadMessageList = ({
)}
{toolParts.map((part, index) => (
<StyledMessageContent key={index} isUser={false}>
{t`Tool call: ${part.toolName}`}
{t`Tool call: ${part.toolName ?? ''}`}
</StyledMessageContent>
))}
<StyledTimestamp>
@@ -22,10 +22,12 @@ const getExpandableContainerTitle = (
)?.label;
const unmatchedCount =
'matchedOptions' in column &&
column.matchedOptions?.filter((option) => !isDefined(option.value)).length;
'matchedOptions' in column
? column.matchedOptions.filter((option) => !isDefined(option.value))
.length
: 0;
return t`Match ${fieldLabel} (${unmatchedCount} Unmatched)`;
return t`Match ${fieldLabel ?? ''} (${unmatchedCount} Unmatched)`;
};
type UnmatchColumnProps = {
@@ -129,7 +129,7 @@ const parseAdditionalPhones = (additionalPhones?: any) => {
try {
return JSON.parse(additionalPhones);
} catch (error) {
logError(t`Error parsing additional phones: ${error}`);
logError(t`Error parsing additional phones: ${String(error)}`);
}
}
@@ -62,8 +62,6 @@ export const MobileBreadcrumb = ({
? previousLink.children
: '';
const linkText = previousLink.children;
return (
<StyledWrapper className={className}>
{previousLink?.href ? (
@@ -71,7 +69,7 @@ export const MobileBreadcrumb = ({
<IconChevronLeft size={theme.icon.size.md} />
<StyledLinkContainer>
<Link title={text} to={previousLink.href}>
{t`Back to ${linkText}`}
{t`Back to ${text}`}
</Link>
</StyledLinkContainer>
</>
@@ -250,7 +250,7 @@ export const WorkflowEditActionUpsertRecord = ({
}
placeholder={t`Object unique fields`}
readonly
hint={t`We match on these fields. If a ${objectLabelSingular} already exists, we update it. Otherwise, we create a new one.`}
hint={t`We match on these fields. If a ${objectLabelSingular ?? ''} already exists, we update it. Otherwise, we create a new one.`}
actionType="UPSERT_RECORD"
/>
)}
@@ -87,7 +87,7 @@ export const getDayOfMonthDescription = (
}
const lastDay = ordinalDays.pop();
const remainingDays = ordinalDays.join(', ');
return t`on the ${remainingDays} and ${lastDay} of the month`;
return t`on the ${remainingDays} and ${lastDay ?? ''} of the month`;
}
// Single day value
@@ -145,7 +145,7 @@ export const getDayOfWeekDescription = (
}
const lastDay = dayNames.pop();
const remainingDays = dayNames.join(', ');
return t`only on ${remainingDays} and ${lastDay}`;
return t`only on ${remainingDays} and ${lastDay ?? ''}`;
}
// Single day value
@@ -91,7 +91,7 @@ export const getHoursDescription = (
}
const lastTime = formattedTimes.pop();
const remainingTimes = formattedTimes.join(', ');
return t`at ${remainingTimes} and ${lastTime}`;
return t`at ${remainingTimes} and ${lastTime ?? ''}`;
}
const hourNum = parseInt(hours, 10);
@@ -52,7 +52,7 @@ export const getMinutesDescription = (
}
const lastValue = values.pop();
const remainingValues = values.join(', ');
return t`at minutes ${remainingValues} and ${lastValue}`;
return t`at minutes ${remainingValues} and ${lastValue ?? ''}`;
}
const minuteNum = parseInt(minutes, 10);
@@ -109,7 +109,7 @@ export const getMonthsDescription = (
}
const lastMonth = monthNames.pop();
const remainingMonths = monthNames.join(', ');
return t`only in ${remainingMonths} and ${lastMonth}`;
return t`only in ${remainingMonths} and ${lastMonth ?? ''}`;
}
// Single month value
@@ -424,7 +424,7 @@ export const SettingsAdminAiProviderDetail = () => {
<ConfirmationModal
modalInstanceId={REMOVE_PROVIDER_MODAL_ID}
title={t`Remove provider "${provider?.label ?? providerName}"`}
title={t`Remove provider "${provider?.label ?? providerName ?? ''}"`}
subtitle={t`This will disconnect all models from this provider. Models will no longer be available until a new provider is configured.`}
onConfirmClick={handleRemoveProvider}
confirmButtonText={t`Remove`}
@@ -155,7 +155,7 @@ export const SettingsApplicationDetailAboutTab = ({
title={
isUpgrading
? t`Upgrading...`
: t`Upgrade to ${latestAvailableVersion}`
: t`Upgrade to ${latestAvailableVersion ?? ''}`
}
variant={'secondary'}
accent={'blue'}
+4 -4
View File
@@ -42,9 +42,9 @@
"@graphql-yoga/nestjs": "2.1.0",
"@jrmdayn/googleapis-batcher": "^0.10.1",
"@lifeomic/axios-fetch": "^3.1.0",
"@lingui/conf": "5.1.2",
"@lingui/core": "^5.1.2",
"@lingui/format-po": "5.1.2",
"@lingui/conf": "5.9.5",
"@lingui/core": "^5.9.5",
"@lingui/format-po": "5.9.5",
"@microsoft/microsoft-graph-client": "3.0.7",
"@microsoft/microsoft-graph-types": "^2.40.0",
"@nestjs/axios": "3.1.2",
@@ -172,7 +172,7 @@
"zod": "^4.1.11"
},
"devDependencies": {
"@lingui/cli": "^5.1.2",
"@lingui/cli": "^5.9.5",
"@nestjs/cli": "^11.0.16",
"@nestjs/schematics": "^11.0.9",
"@nestjs/testing": "11.1.24",
+10 -2
View File
@@ -1,5 +1,13 @@
// Add Jest matchers for toThrowError and other missing methods
export {};
import { i18n } from '@lingui/core';
import { compileMessage } from '@lingui/message-utils/compileMessage';
// Lingui 5.9+ throws when a translation function runs without an active
// locale. Production activates the global singleton in I18nService; unit
// tests bypass Nest bootstrap, so mirror that here. The messages compiler
// makes t`...` fall back to the English source text.
i18n.setMessagesCompiler(compileMessage);
i18n.load('en', {});
i18n.activate('en');
declare global {
namespace jest {
@@ -112,7 +112,7 @@ export class GraphqlQueryFilterFieldParser {
throw new GraphqlQueryRunnerException(
`Invalid filter value for field ${key}. Expected non-empty array`,
GraphqlQueryRunnerExceptionCode.INVALID_QUERY_INPUT,
{ userFriendlyMessage: msg`Invalid filter value: "${value}"` },
{ userFriendlyMessage: msg`Invalid filter value: "${String(value)}"` },
);
}
const { sql, params } = computeWhereConditionParts({
@@ -254,7 +254,9 @@ export class GraphqlQueryFilterFieldParser {
throw new GraphqlQueryRunnerException(
`Invalid filter value for field ${subFieldKey}. Expected non-empty array`,
GraphqlQueryRunnerExceptionCode.INVALID_QUERY_INPUT,
{ userFriendlyMessage: msg`Invalid filter value: "${value}"` },
{
userFriendlyMessage: msg`Invalid filter value: "${String(value)}"`,
},
);
}
@@ -49,8 +49,12 @@ export class I18nService implements OnModuleInit {
async loadTranslations() {
// The global i18n singleton backs server-side t`…` calls and has no
// compiled catalog, so it needs a runtime message compiler.
// compiled catalog, so it needs a runtime message compiler. Since lingui
// 5.9 it also throws unless a locale is activated, so activate the source
// locale (t`…` then renders the English source text via the compiler).
i18n.setMessagesCompiler(compileMessage);
i18n.load(SOURCE_LOCALE, enMessages);
i18n.activate(SOURCE_LOCALE);
const messagesByLocale: Record<keyof typeof APP_LOCALES, Messages> = {
en: enMessages,
+1 -1
View File
@@ -17,7 +17,7 @@
"devDependencies": {
"@babel/preset-env": "^7.26.9",
"@babel/preset-typescript": "^7.24.6",
"@lingui/core": "^5.1.2",
"@lingui/core": "^5.9.5",
"@prettier/sync": "^0.5.2",
"@swc/core": "^1.15.11",
"@swc/jest": "^0.2.39",
+5 -5
View File
@@ -19,8 +19,8 @@
"@calcom/embed-react": "^1.5.3",
"@linaria/core": "^7.0.0",
"@linaria/react": "^7.0.1",
"@lingui/core": "^5.1.2",
"@lingui/react": "^5.1.2",
"@lingui/core": "^5.9.5",
"@lingui/react": "^5.9.5",
"@lottiefiles/dotlottie-react": "^0.18.10",
"@radix-ui/react-popover": "^1.1.15",
"@tabler/icons-react": "^3.41.1",
@@ -41,9 +41,9 @@
"zod": "^4.1.11"
},
"devDependencies": {
"@lingui/cli": "^5.1.2",
"@lingui/conf": "5.1.2",
"@lingui/format-po": "5.1.2",
"@lingui/cli": "^5.9.5",
"@lingui/conf": "5.9.5",
"@lingui/format-po": "5.9.5",
"@lingui/swc-plugin": "^5.11.0",
"@opennextjs/cloudflare": "^1.0.0",
"@swc/core": "^1.15.11",
+269 -294
View File
File diff suppressed because it is too large Load Diff