Commit Graph

12654 Commits

Author SHA1 Message Date
Félix Malfait
69a7e614ff fix: restore isCustom gate in metadata label resolvers (#21432)
## Context

#21228 removed the stored `isCustom` column and, with it, the `isCustom`
early-return in `resolveObjectMetadataStandardOverride` /
`resolveFieldMetadataStandardOverride`, on the assumption that falling
through the `standardOverrides` checks was equivalent.

It isn't: custom object/field labels now reach the Lingui lookup. A
custom label that collides with a standard catalog string (e.g. a custom
field labeled "Status") gets translated for non-English locales against
the user's intent, and every other custom label pays a hash + catalog
miss — and, in production, an "Uncompiled message detected" warning
(#21415) — on each metadata resolution.

## Fix

Restore the gate. `isCustom` is no longer stored, so call sites that
build the resolver input from flat entities (dataloader,
minimal-metadata, view controller, command-menu-item navigation context)
derive it via `belongsToTwentyStandardApp`; GraphQL resolvers keep
passing DTOs, which already carry the derived value.

## Testing

- Unit tests for both resolvers, including a new regression test: a
custom label matching a standard catalog entry is returned verbatim,
Lingui never called.

---------

Co-authored-by: Weiko <corentin@twenty.com>
2026-06-11 16:10:53 +00:00
Weiko
cfb9772179 feat(server): convert view to overridable entity (#21436)
## Context

Every entity created as a side effect of object creation must support
the overridable pattern (`isActive` + `overrides` + override routing)
before we can re-own side effects to their true application. Starting
with View.

viewField, viewFieldGroup, pageLayoutTab and pageLayoutWidget already
extend `OverridableEntity`. This PR brings `view` to the same pattern.

## What this does

- `ViewEntity` now extends `OverridableEntity<ViewOverrides>` (adds
`isActive` boolean + `overrides` jsonb). All editable view properties
are overridable; the 3 fieldMetadata foreign keys are converted to/from
universal identifiers like viewField's `viewFieldGroupId`.
- **Update**: mutations on a view not owned by the caller (e.g. standard
views like "All Companies") are written into `overrides` instead of
mutating the row. Reads merge overrides in
the DTO.
- **Delete/destroy**: views not owned by the caller are deactivated
(`isActive = false`) instead of deleted.
~~- **INDEX invariant**: `key = INDEX` views can only be created via
object-creation side effect. The API now rejects creating, deleting or
destroying INDEX views (object-deletion cascade is unaffected). This was
not really needed for this migration but was flagged during
implementation.~~
- **Front**: views with `isActive = false` are filtered out of the views
selector.
- Fast instance command adds the two columns
(`2-12-instance-command-fast-...-view-overridable-entity.ts`).

## Notes

- Custom (caller-owned) views behave exactly as before: direct updates,
soft delete.
- View-group side effects (kanban groups) are computed on the
override-merged view so overridden `mainGroupByFieldMetadataId` works.
2026-06-11 16:00:52 +00:00
DeviSriSaiCharan
947a4d5253 Fix: prevent unexpected navigation when destroying record from side panel (#21391)
Fixes: #21243 

# Issue
When a user is on a specific record's page (for example, looking at a
Person) and opens a related record (like a Note) in the right-side
panel, clicking "Permanently Delete" on that Note would abruptly
redirect the user to the main "Notes" list. This breaks the user's
workflow, as they typically want to remain on the parent Person page
after deleting a sub-record.

# Root Cause
Inside the `DestroyRecordsCommand` and `DeleteRecordsCommand`
components, the application was programmed to unconditionally trigger a
`navigateApp` redirect to the deleted object's index page upon
successful deletion. The code did not account for whether the deletion
was triggered from the main index page or from inside a contextual side
panel.

# How we fixed it
I introduced a new `isInSidePanel` flag into the
`HeadlessCommandContextApi`.
The command menu now detects if the delete action originated from inside
a side panel and passes this flag down the execution chain. If `true`,
the `DestroyRecordsCommand` simply closes the panel
(`closeSidePanelMenu()`) and keeps the user exactly where they were.

## After that fix, I encountered another issue (UI not updating
automatically)
Because the app now correctly kept the user on the Person page, a new
bug surfaced: the "Note" chip inside the relation table did not
disappear immediately. The user had to manually refresh the page to see
the deletion.

This happened because:
1. The Apollo optimistic cache occasionally failed to trace deeply
nested morph-relationships back to the parent.
2. A standard local React `useState` fallback was insufficient. The
table component aggressively unmounts and remounts relation cells
whenever a user hovers over them to display interactive controls, which
would wipe the local React state clean and cause the "ghost chip" to
reappear.

##How I fixed that issue (and optimized it)
I built an event-driven fallback using global Jotai state to permanently
hide the chips:

1. **Precision ID Broadcasting**: I updated the `useDestroyManyRecords`
and `useIncrementalDestroyManyRecords` hooks to extract and broadcast
only the *confirmed* destroyed IDs directly from the Apollo mutation
response, preventing false-positive UI removals if the backend performed
a partial delete.
2. **Batch Optimizations**: During bulk deletions, events are now
broadcasted per-batch rather than accumulating thousands of IDs in
memory until the end, keeping the UI instantly responsive and memory
bounded.
3. **Per-Cell State Scoping (`atomFamily`)**: Instead of a leaky global
array, we used Jotai's `atomFamily` to dynamically generate a unique
Noticeboard for every specific table cell (`${recordId}-${fieldName}`).
This ensures the hidden-state survives mouse-hover unmounts while
remaining cleanly isolated.
4. **Defensive Filtering**: The `RelationFromManyFieldDisplay` component
was updated to defensively guard against `undefined` array entries and
strictly match deleted IDs only against valid foreign keys (ending in
`'Id'`), preventing false-positive removals if a UUID happened to be
pasted into a description field.
5. **SSE Resilience**: We hardened the Server-Sent Event (SSE) listeners
with optional chaining and null-filtering to ensure malformed backend
payloads do not crash the real-time event pipeline.


# Screen Recording


https://github.com/user-attachments/assets/19fc8a3f-ba28-43c2-b1f3-a91127cdad97

---------

Co-authored-by: bosiraphael <raphael.bosi@gmail.com>
Co-authored-by: Raphaël Bosi <71827178+bosiraphael@users.noreply.github.com>
2026-06-11 15:40:56 +00:00
Charles Bochet
deb956f4fe security: bump wait-on 7.2.0 -> 9.0.10 to drop vulnerable joi (Dependabot alert 1437) (#21457)
## Context

Two open Dependabot alerts; this PR fixes one with a parent bump (no
resolutions), the other is dismissed with analysis (see below).

## joi RangeError DoS (alert 1437, fixed in joi 18.2.1)

`joi@17.13.3`'s only parent is `wait-on@7.2.0` (twenty-sdk
devDependency, used purely as a CLI: `yarn start`'s `wait-on tcp:3000`
and CI's `wait-on http://localhost:3000/healthz --timeout --interval`).
Bumping **wait-on 7.2.0 → 9.0.10** (which depends on `joi ^18.2.1`)
evicts joi 17 from the lockfile entirely — no forced ranges.

Verified: twenty-sdk builds; wait-on 9 smoke-tested with both invocation
shapes used in the repo (`tcp:PORT`, `http://… --timeout --interval`).

## @cyntler/react-doc-viewer TXTRenderer "XSS" (alert 1436) — dismissed
as inaccurate

CVE-2026-30691 claims arbitrary JS execution via a crafted .txt because
TXTRenderer "casts raw data as a ReactNode". Verified against the
installed 1.17.1 dist: the renderer is `children:
currentDocument?.fileData` where the txt fileLoader produces `fileData`
via `FileReader.readAsText` — i.e. **always a string rendered as a React
child, which React HTML-escapes**. There is no
`dangerouslySetInnerHTML`/eval in the path (the only
`dangerouslySetInnerHTML` occurrence in the bundle is styled-components'
prop whitelist regex). String children cannot execute script in React;
the advisory's premise is wrong, and consistently upstream has published
no fix. Alert dismissed as *inaccurate* with this analysis.

Longer-term, `@cyntler/react-doc-viewer` remains a liability (stale
since 2025-09, already needs an ajv resolution) — replacing it with
first-party preview renderers is tracked separately.
2026-06-11 17:42:34 +02:00
Rich Roberts
41cdd83367 fix(ai): correct RICH_TEXT and MORPH_RELATION record filter operators (#21106)
## Problem

The AI find-records tool generates filter schemas via
`generateFieldFilterZodSchema`. `RICH_TEXT` currently shares the `TEXT`
case, so the agent is told it can use scalar text operators
(`like`/`ilike`/`startsWith`/`endsWith`/`eq`/…) directly on a rich-text
field.

But `RICH_TEXT` is a **composite** type (`markdown` + `blocknote`
sub-fields, see `rich-text.composite-type.ts`). Applying a scalar
operator to the composite root throws at query time:

```
ERROR [FindRecordsService] Failed to find records: Object person doesn't have any "ilike" field.
ERROR [FindRecordsService] Failed to find records: Sub field "ilike" not found for composite type: RICH_TEXT
```

`FindRecordsService` catches and returns `success: false`, so the agent
retries mid-turn — burning latency/tokens — and can **never** search
rich-text body content (note bodies, `about`, etc.).

## Fix

Give `RICH_TEXT` its own case in the filter-schema generator that
exposes the `markdown` and `blocknote` sub-fields, each carrying the
text operators — mirroring the existing composite patterns for `EMAILS`
(`primaryEmail`), `PHONES` (`primaryPhoneNumber`), `LINKS`
(`primaryLinkUrl`), `FULL_NAME`, and `ADDRESS`.

So the agent now emits:

```jsonc
{ "noteBody": { "markdown": { "ilike": "%onboarding%" } } }   // valid composite sub-field filter
```

instead of:

```jsonc
{ "noteBody": { "ilike": "%onboarding%" } }                   // throws on composite root
```

This both **stops the throw** and **makes rich-text content actually
searchable** (the original intent). `TEXT` keeps its existing root-level
scalar operators unchanged.

## Test

Added `__tests__/field-filters.zod-schema.spec.ts`:
- `RICH_TEXT` routes pattern operators onto `markdown` / `blocknote`
- root-level scalar operators on `RICH_TEXT` are no longer accepted
- `TEXT` root-level operators unchanged

## Notes

- No DB/schema migration; render/tool-schema layer only.
- Reproduced against `twentycrm/twenty:latest`; the faulting code is
unchanged on `main` as of this PR.

---------

Co-authored-by: Rich Roberts <rich.roberts@talentpipe.ai>
Co-authored-by: Etienne <45695613+etiennejouan@users.noreply.github.com>
2026-06-11 17:13:26 +02:00
Charles Bochet
503c689f37 security: upgrade typeorm to 0.3.26 (CVE-2025-60542) (#21456)
## Context

Retry of the typeorm upgrade that was pulled out of #21448 after CI
showed "intermittently lossy metadata sync". **The investigation
exonerated typeorm**: the postcard/seed failures were a pre-existing bug
in `@ptc-org/nestjs-query-typeorm`'s batched relation paging (global
LIMIT across parents) that scan-order luck had been hiding — reproduced
byte-for-byte on typeorm **0.3.20** against a frozen repro DB. That bug
is fixed in #21455, which this PR is stacked on (base branch =
`charles/fix-nestjs-query-batch-relation-paging`; will retarget to main
when it merges).

## Changes

- typeorm `0.3.20` → `0.3.26`
([CVE-2025-60542](https://github.com/advisories/GHSA-q2pj-6v73-8rgj),
MEDIUM). The CVE lives in TypeORM's MySQL path
(`sqlstring`/`stringifyObjects`); Postgres-only Twenty never exercises
it — this is scanner hygiene + staying current.
- The local yarn patch (`PickKeysByType` + `DeleteResult.generatedMaps`)
applies **verbatim** to 0.3.26 (verified against the pristine tarball) —
renamed to `typeorm+0.3.26.patch`.
- `WorkspaceRepository.query` restricted override adapted to the generic
`query<T = any>()` base signature introduced in 0.3.24 (one-line change,
still throws `RAW_SQL_NOT_ALLOWED`).
- 0.3.26 ships `uuid ^11` natively → the scoped `typeorm/uuid`
resolution from #21441 and its `//resolutions` comment clause (including
the now-disproven "lossy sync" warning) are removed.

## Why we're confident this time

The original failure signature was fully understood, not just retried:
- On a frozen failing DB, **all fieldMetadata rows + workspace columns
were intact** — only the batched metadata API read was truncated (`LIMIT
501` over 558 rows, no ORDER BY).
- Same DB, typeorm 0.3.20: identical truncation, identical SQL → not a
typeorm regression.
- With #21455 applied: postcard install/uninstall stress loop **12/12
green on typeorm 0.3.26** (previously failed within 1–2 iterations), API
returns 558/558 fields.

## Verification

- `npx nx typecheck twenty-server` — clean
- Full `twenty-server` unit suite — green (5651 passed)
- `group-by-resolver` integration suite — 19/19 on a fresh 0.3.26-seeded
test DB
- Postcard app-sync stress loop — 12/12 on this exact stack
- Lockfile: typeorm 0.3.26 + new `sql-highlight` dep, `esbuild`/uuid
entries untouched
2026-06-11 16:41:22 +02:00
Charles Bochet
d75685b8dc fix(metadata): nestjs-query batched relation queries truncate results across parents (#21455)
## TL;DR

The metadata API silently drops relation rows whenever a batched
relation query exceeds the requested page size. A dev-seeded workspace
already has **558 fieldMetadata rows across 31 objects**, so
`objects(paging:{first:50}) { fields(paging:{first:500}) }` executes:

```sql
SELECT DISTINCT ... FROM core."fieldMetadata" fields
WHERE workspaceId = $1 AND objectMetadataId IN (...31 ids...)
LIMIT 501 OFFSET 0   -- no ORDER BY
```

…and returns exactly **501 of 558** fields — ~57 rows dropped, and
*which object loses which field is scan-order-dependent*. This is what
made `example-app-postcard` CI flap with "PostCard object missing field
X" (different X per run).

## Root cause

`@ptc-org/nestjs-query-typeorm`'s `batchQueryRelations` (the DataLoader
batch path behind every `@CursorConnection`) applies the **per-parent**
page size as a **single global LIMIT** on the batched query, then groups
rows per parent in memory. Any batch whose combined relation rows exceed
`first + 1` truncates arbitrary parents. This affects production
metadata reads, not just CI — any workspace with enough fields/objects
loses rows in `objects.fields`-style connections.

## Fix

Yarn patch on `@ptc-org/nestjs-query-typeorm@9.4.0` (same vehicle as the
existing `nestjs-query-graphql` patch):
- `RelationQueryBuilder.batchSelect`: only apply LIMIT/OFFSET when the
batch has a **single parent**; multi-parent batches stay **bounded**
with `parents × (offset + limit)` — the upper bound a correct per-parent
pager can ever need, so it cannot wrongly truncate while still guarding
against unbounded fetches on high-cardinality relations;
- `batchQueryRelations`: enforce paging **per parent** by slicing after
`mapRelations` (preserves the `first + 1` hasNextPage probe semantics).

## Verification

- On a frozen repro DB (postcard installed, 558 fields): unpatched
returns 501 fields with `postCard` missing `deliveredAt`; patched
returns **558/558** with the full `postCard` field set. Reproduced
identically on typeorm 0.3.20 and 0.3.26 — pre-existing bug, **not** a
typeorm regression (this unblocks the typeorm upgrade that was reverted
from #21448).
- Postcard install/uninstall stress loop: unpatched fails within 1–2
iterations; patched **12/12 green**.
- `npx nx typecheck twenty-server` clean, full `twenty-server` unit
suite green (5651 passed).

## Related

#21435 chases the **same CI symptom** (postcard randomly missing a
freshly synced field) at a different layer — a workspace-cache write
racing invalidation. The two are complementary: the repro behind this PR
survives a **cold server restart + `redis-cli FLUSHALL`** with all rows
intact in Postgres, which no cache race can explain — the truncation
happens on the DB read itself (`LIMIT 501` over 558 matching rows,
captured via `log_statement=all`). Both fixes are likely needed for the
postcard job to be fully reliable.

## Notes

Worth upstreaming to `@ptc-org/nestjs-query` eventually; the proper
upstream fix is per-parent windowed pagination (`ROW_NUMBER() OVER
(PARTITION BY parentId)`), but the in-memory per-parent slice is correct
and proportionate at metadata-API scale.
2026-06-11 16:39:14 +02:00
martmull
d0884bd708 Fix missing datetime filter type (#21451)
Currently datetime fields are only typed to be filtered by string

Add a proper typing to match gql filters

## Before
<img width="750" height="492" alt="image"
src="https://github.com/user-attachments/assets/ff3a5423-3bb0-4295-84c9-e404489354f6"
/>

## After
<img width="537" height="511" alt="image"
src="https://github.com/user-attachments/assets/d8c8219f-b7de-41b0-96cb-5adbfda7a91d"
/>
2026-06-11 13:47:08 +00:00
Thomas Trompette
0ac4f237c0 fix(server): stop redundant lambda rebuilds causing build-lock acquisition failures (#21442)
## Context

`Lambda invocation failed for function '<id>' during build: Failed to
acquire lock for key: lambda-build:<id>` fires ~1000 times/day in
production.

## Root cause

`LambdaExecutorManagerService.buildExecutor` re-checks `canSkip` inside
the `lambda-build:<functionId>` lock, but the re-check reuses the
`flatApplication` snapshot captured when the request started. `canSkip`
depends on `!flatApplication.isSdkLayerStale`, so:

1. An app sync/install regenerates the SDK client and sets
`isSdkLayerStale = true`
2. All in-flight executions of the function fail `canSkip` and queue on
the lock
3. The first holder rebuilds and `markSdkLayerFresh` clears the flag in
DB + workspace cache
4. Queued waiters can't see that fix — their in-memory snapshot still
says stale — so **each waiter redoes the full rebuild serially**
(download SDK archive, delete + republish layer, update function config,
wait for update)
5. The lock is held back-to-back for minutes; everyone deeper in the
queue exhausts the 120s retry budget and throws

The local driver already handles this correctly
(`LocalLayerManagerService` refreshes the flat application from the
workspace cache inside its lock); the lambda driver missed it.

## Fix

- Refresh `flatApplication` from the workspace cache inside the lock
before re-checking `canSkip`, so waiters skip in ~100ms once the first
holder finishes
- Degrade gracefully on lock-acquisition timeout: re-check build status
with fresh data and proceed with the invocation if the executor is
already usable, instead of failing the run (introduces a typed
`CacheLockAcquisitionError` so only that case is caught)

## Test plan

- [x] `cache-lock.service.spec.ts` passes
- [x] `lint:diff-with-main` + typecheck pass
- [ ] Monitor `Failed to acquire lock for key: lambda-build:*` error
rate in production after deploy
2026-06-11 13:26:38 +00:00
github-actions[bot]
796f763bb7 i18n - website translations (#21453)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2026-06-11 15:35:27 +02:00
Charles Bochet
184c4948d6 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
2026-06-11 15:11:29 +02:00
Etienne
303c415dd1 fix(ai) - add logs + remove dashboard building (#21440)
- add logs for thread finishing without agent message
- add logs to monitor toolCall token usage
- remove dashboard building via AI (before fixing it)
- fix Anthropic compute
2026-06-11 12:45:25 +00:00
Etienne
a6fcbf58e4 fix(billing) - enable upgrade if invoice already paid (#21450)
Had an issue concerning a user with credits, then invoice automatically
paid. Upgrade failed
2026-06-11 12:45:01 +00:00
Raphaël Bosi
7da6f25aaf Replace random remote images in stories to stop flaky Argos diffs (#21447)
### Problem
`TabButton` and `AvatarOrIcon` stories (in both `twenty-ui` and
`twenty-ui-deprecated`) used random remote image URLs —
`picsum.photos/192/192` and `i.pravatar.cc/300`. Each Argos run fetched
a different image, producing false-positive pixel diffs.

This is the image counterpart to #21412, which froze framer-motion
animations for the same reason.

### Fix
Replace all 6 random URLs with `AVATAR_URL_MOCK` — a fixed base64 data
URI already used across avatar stories. It's deterministic and
network-free, so screenshots are now stable across runs.

- `twenty-ui` / `twenty-ui-deprecated` → `TabButton.stories.tsx` (×2
each)
- `twenty-ui` / `twenty-ui-deprecated` → `AvatarOrIcon.stories.tsx` (×1
each)

Note: this changes the rendered image content, so it adds new baselines
(one-time Argos approval, not flakiness).
2026-06-11 11:41:01 +00:00
Rashad Karanouh
f7463886a6 feat(partners): partner role row-level security (RLS) with scoped edits (#21386)
## Summary
Adds an external **Partner** self-service role that sees and edits only
its own
records via row-level security (RLS), so a validated partner can sign in
and manage
just the deals they're matched on.

## What's included
- **`partnerUser` relation** on Partner, Person, Company, Opportunity (+
inverse
  relations on Workspace Member) — the login member a record belongs to.
- **RLS predicates** scoping each of those objects to "partnerUser IS
the current
workspace member", plus a self-scope on Workspace Member so member-typed
relations
resolve without exposing the internal team roster. Applied out-of-band
via
  `yarn rls:configure` (the app manifest cannot ship RLS predicates).
- **Assign / unassign cascade** (`on-opportunity-partner-assigned` logic
function):
assigning a Partner to an Opportunity stamps `partnerUser` onto the
Opportunity +
its Company + People; removing the Partner clears it (and cascades to
the
  Company/People when no other deal of that member still uses them).
- **Partner role permissions**
  - Partner profile: full read/update.
- Opportunity: read all; **update `stage` and `amount` only** (every
other
    user-facing field locked).
  - Company / Person: read-only.
  - Workspace Member: read-only, RLS-scoped to self.
- **`partnerUser` column** added to the Validated Partners view so the
login member
  can be assigned inline.

## Install / upgrade note
After install or reinstall, run `yarn rls:configure` (`:prod` variant
for prod) to
(re)apply the RLS predicates and verify the field-permission locks.
Manifest sync
handles object/field permissions; predicates are applied by this script.

## Platform gaps found (for the eng team)
1. **Manifest sync doesn't invalidate the roles-permissions Redis
cache.** Permission
changes deployed via `yarn twenty dev --once` persist to the DB but
aren't reflected
   in the cached snapshot used for enforcement until

`engine:workspace:metadata:permissions:roles-permissions:<workspaceId>:{data,hash}`
   is flushed. Relevant on any real workspace when permissions change.
2. **Locking a server-injected field silently breaks all updates.** The
`*.updateOne`
pre-query hook writes `updatedBy` into every update, so
`canUpdateFieldValue:false`
on `updatedBy` makes the permission check reject *every* record update
with
`PERMISSION_DENIED`. Field-permission lock lists must exclude
server-managed/injected
fields (`updatedBy`; and `position`, co-written with `stage` on kanban
drag).

## Version
Minor bump → `0.5.0` (new role, new fields, new behaviour;
backwards-compatible).

## Testing
- Verified locally as a partner user: edits own profile; edits
Opportunity stage +
amount; Company/Person read-only; sees only matched deals; unassigning a
partner
  removes the deal (and its company/people) from the partner's view.
- `yarn rls:configure` passes (5 predicates upserted; 24 Opportunity
fields locked,
  stage + amount editable).
- Lint clean.
2026-06-11 11:37:35 +00:00
Charles Bochet
166f7ee0d2 chore(deps): prune yarn resolutions down to load-bearing entries (#21446)
## Context

Audit of all 28 `resolutions` entries in the root package.json against
yarn.lock dependency graphs and the npm registry, to remove every entry
that is no longer forcing anything a normal resolution wouldn't do —
resolutions are hard to maintain and silently freeze versions.

Net result: **28 → 22 entries**, two small dependency bumps replace
pins, and every remaining entry now has its blocker + removal condition
documented in `//resolutions`.

## Removed — dead weight (re-resolution lands on the same safe versions)

| Entry | Why it was dead |
|---|---|
| `type-fest: 4.10.1` | Stale 2024 dedup pin that semver-overrode ~16 of
19 declared ranges (forced `^0.13`/`^0.20`/`^0.21` consumers up four
majors, `^5.x` consumers down one). Types-only; each parent now resolves
its own compatible copy. |
| `typescript: 5.9.3` | No-op: every range (`^5.9.3`, `5.9.3`, `~5.9.2`)
resolves to 5.9.3 naturally. Only the electron-forge scaffolding
template regains its own nested `~5.4.5` (never builds this repo). |
| `node-gyp: ^12.4.0` | All requesters are Yarn-injected `node-gyp:
latest` = 12.4.0 today. The tar-6-era node-gyp versions it evicted have
no requesting parent left. |
| `cacache: ^20.0.0` | All four parents (arborist, metavuln-calculator,
make-fetch-happen 15, pacote 21) already declare `^20`. Guarded by the
kept `make-fetch-happen: ^15` resolution. |
| `pacote/tar: ^7.5.16` | The original target (pacote 11/15 via zapier)
is gone; the only pacote left is 21.5.0 which declares `tar ^7.4.3`
natively. |

## Removed — replaced by a parent upgrade

- **`nodemailer: 8.0.10`** → `imapflow` 1.2.1 → **1.3.6** (ships patched
nodemailer 8.0.10 exact; 1.4.0 is blocked by the 3-day npm age gate).
twenty-server's own `^8.0.5` range was already safe.
- **`node-ical/uuid: 11.1.1`** → `node-ical` ^0.20.1 → **^0.21.0**,
which drops uuid (and axios) entirely. The uuid removal happened at
0.21.0 — not in the 0.26 rrule-temporal type overhaul that #21441
flagged as the blocker.

## Narrowed — `qs: 6.15.2` global → two scoped entries

Only three lockfile entries actually request vulnerable qs ranges:
`express@4.22.0` (pinned by `@mintlify/previewing`), `express@4.22.1` +
`@cypress/request@3.0.10` (pinned by verdaccio 6.7.2, latest). Replaced
the global pin with `express/qs` + `@cypress/request/qs`, so the 12+
healthy parents (express 4.22.2/5.x, body-parser, stripe, …) are no
longer frozen and will pick up future qs releases naturally.

## Re-pinned — `graphql-redis-subscriptions/ioredis`

Changed `^5.6.0` → exact `5.10.1` and documented why: this must equal
the exact ioredis version pinned by twenty-server and bullmq. Without
it, graphql-redis-subscriptions' `^5.3.2` resolves to a second ioredis
copy and `RedisPubSub`'s publisher/subscriber types reject the server's
client (caught by twenty-server typecheck during this work — bump it in
lockstep with the ioredis pin).

## Kept (all load-bearing, now documented inline)

graphql (singleton below msw's `^16.12.0`), @lingui/core (suite
lockstep), @types/qs (6.9.17 typing-break holdback), @opentelemetry/api
(NoopMeterProvider singleton, #20231), chokidar v3 (NestJS CLI fsevents,
#20316), tmp (zapier-platform-cli pins 0.2.5), make-fetch-happen + the
two @electron tar entries (blocked on electron-forge adopting
@electron/rebuild 4), @angular-devkit/core (blocked on a fixed
@nestjs/cli > 11.0.23), yeoman-environment, webpack-dev-server,
next/postcss (fix only in next 16.3.0 canaries), the remaining uuid
pins, and react-doc-viewer/ajv.

## Follow-ups (separate PRs)

- `typeorm` 0.3.20 → 0.3.30: re-roll the 46-line patch; clears the
`typeorm/uuid` resolution **and** the open high-severity
GHSA-q2pj-6v73-8rgj (SQL injection in `repository.save/update`, fixed in
0.3.26).
- googleapis 105 → ≥152 migration clears `googleapis-common/uuid`.

## Verification

- `yarn install` clean; lockfile contains **no** vulnerable qs
(≤6.15.1)/tar 6/uuid <11/nodemailer <8.0.4/postcss 8.4.31/tmp <0.2.6
entries
- `npx nx typecheck twenty-server` ✓ and `npx nx typecheck twenty-front`
✓
- CalDAV + IMAP unit tests (node-ical/imapflow consumers): 9 suites, 121
tests ✓
- `yarn npm audit --all`: only pre-existing typeorm finding remains (see
follow-up)
2026-06-11 13:41:03 +02:00
Charles Bochet
1ccede2309 security: scoped ajv 8.20.0 resolution for react-doc-viewer (Dependabot alert 481) (#21445)
Closes the **final** open Dependabot alert — ajv
[481](https://github.com/twentyhq/twenty/security/dependabot/481) — with
a scoped resolution.

### Why a resolution (no parent path)
`ajv >= 7.0.0 < 8.18.0` is pulled **only** by
`@cyntler/react-doc-viewer`, which pins `ajv ^7.2.4`. Its **latest
(1.17.1) still pins `^7`** — there is no react-doc-viewer version on ajv
8 (no 1.18/2.0) — so it can't be closed by upgrading the parent.

### Why it's completely safe
`@cyntler/react-doc-viewer` **never imports ajv** — zero references in
its dist; ajv is a declared-but-unused dependency. So forcing it to ajv
8 has **no functional impact**, and the ajv 7→8 breaking-change concern
is moot. Scoped to `@cyntler/react-doc-viewer/ajv` so it touches nothing
else.

### Verification
- `yarn install --immutable` ✓; every ajv now resolves to **8.18.0 /
8.20.0** (safe) or `6.12.x` (outside the advisory range) — no vulnerable
ajv remains.

Documented in the top-level `//resolutions` note with a removal trigger.
**This was the last open alert.**
2026-06-11 12:37:45 +02:00
Charles Bochet
462dd3b0e9 security: uuid CVE — bump bullmq/msal/blocknote + scoped resolutions for the rest (Dependabot alert 1289) (#21441)
Closes the uuid Dependabot alert —
[1289](https://github.com/twentyhq/twenty/security/dependabot/1289) — by
**upgrading the parents that bump cleanly** and **scope-resolving only
the ones that genuinely can't**.

`uuid < 11.1.1` (buffer-bounds check in v3/v5/v6) is pulled by ~9
transitives.

### Bumped (parent upgrade — drops uuid<11, no behavior change;
typecheck verified)
- **bullmq** 5.40.0 → 5.78.0 — also aligned **ioredis** 5.6.0 → 5.10.1
(bullmq pins it) and fixed the renamed `Job.returnValue→returnvalue` /
`stackTrace→stacktrace` (now `string[]|null`) in
`admin-panel-queue.service.ts`.
- **@azure/msal-node** ^3.8.4 → ^5.2.3 (5.2.4 was age-gate-quarantined).
- **@blocknote/** ×5 ^0.47.3 → ^0.51.4.

### Scope-resolved to uuid 11.1.1 (no clean bump exists)
- **sockjs** (latest; pinned by webpack-dev-server) and
**@ptc-org/nestjs-query-typeorm** (9.4.0 *is* latest, pins `^10`) — no
version drops uuid.
- **typeorm** — a `patch:` dep / ORM core, too risky to bump.
- **node-ical** 0.26 (type-model overhaul → caldav-parser rewrite) and
**googleapis** 173 (Gmail/OAuth, 105→173) — large breaking migrations;
**deferred to dedicated PRs**.
- **@cypress/request** — transitive (cypress isn't a direct dep).

Resolutions are **per-package** and preserve the intentional **uuid
13.x** (twenty-sdk / create-twenty-app).

### Verification
- `twenty-server` typecheck ✓ (0 errors), `twenty-front` typecheck ✓ (0
errors).
- `yarn install --immutable` ✓; every uuid resolves to **11.1.1** or
**13.0.2**.
- bullmq/msal/typeorm runtime exercised by the **server integration
tests**; @blocknote by the **storybook tests** in CI.
2026-06-11 12:26:26 +02:00
Charles Bochet
6af06c1062 security: postcss CVE via styled-components bump + next/postcss resolution (Dependabot alert 1061) (#21438)
Closes the postcss Dependabot alert —
[1061](https://github.com/twentyhq/twenty/security/dependabot/1061) — by
**bumping one parent** and using a **scoped resolution** only where
there's no other path.

postcss `< 8.5.10` (XSS via unescaped `</style>` in stringify output)
was bundled by two parents:

### styled-components → bumped (no resolution)
`styled-components 6.4.2` (in-range for our `^6.1.0`; we were on a stale
6.3.12) **dropped its postcss dependency entirely**, so a plain `yarn
up` removes the 8.4.49 copy — no override. (styled-components is only
used in one component-renderer Storybook showcase; product UI is on
Linaria.) Re-built `twenty-front-component-renderer` to confirm the
bump.

### next → scoped resolution (no parent path)
`next` pins postcss `8.4.31` **exact on every stable release** —
next@latest (16.2.9) still pins it; the fix (8.5.10) exists only in the
**16.3.0 canary** (unreleased). So `next/postcss: 8.5.15` is the only
mechanism, scoped to next and documented in the top-level
`//resolutions` note with a removal trigger.

### Verification
- Every postcss now resolves `>= 8.5.14` (safe); `yarn install
--immutable` ✓.
- postcss is build-time and **CI doesn't build twenty-website on regular
PRs**, so verified locally: **twenty-website** (Next production build) ✓
and **twenty-front-component-renderer** ✓.
2026-06-11 11:19:50 +02:00
Raphaël Bosi
6756ea628d Add People Data Labs enrichment logic functions (#21254)
## Add People Data Labs enrichment logic functions

Implements the **enrichment logic functions** that call the PDL API and
map responses onto standard + `pdl*` fields.

- **Logic functions** — shared core powers bulk workflow actions
(`enrich-person` / `enrich-company`) and single-record AI tools. Each
guards a TTL, matches via identifier, fills standard fields only when
empty, always writes `pdl*` fields, and normalizes SELECT values against
field options.
- **Data model** — PDL employer now links via the **standard `company`
relation** (find-or-create, fill-only-if-empty); removed the dedicated
`pdlCurrentCompany` relation, six `pdlJobCompany*` scalars, and the
`pdl*` indexes.

**Not functional yet:** the post-install workflow seeding
(`seed-enrichment-workflow.ts`) is left in place but blocked — it needs
workflow-builder mutations that the CRUD-only app SDK schema doesn't
expose.

**Deferred:** metering/billing and auto-enrichment triggers.



https://github.com/user-attachments/assets/a570a484-ae79-4c57-a274-e068fdab78e4



https://github.com/user-attachments/assets/6edfb5e1-cada-4892-87e0-b485eb359692
2026-06-11 09:06:20 +00:00
github-actions[bot]
28ab160b48 i18n - website translations (#21439)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2026-06-11 11:02:43 +02:00
Raphaël Bosi
d596c26f46 Migrate twenty UI (#21407)
## Migrate all `twenty-ui-deprecated` components into `twenty-ui`

Ports all **192 components** and **70 stories** into the new `twenty-ui`
package with full public-API parity (export diff: 0 missing / 0 extra
across all 13 modules; story titles byte-identical for the Argos
cross-package diff).

- **Styling:** Linaria → SCSS Modules, `var(--t-*)` tokens, `data-*`
state. Canonical pattern in `Button.module.scss`.
- **Behavior:** Base UI where mapped (Checkbox, Radio, Modal→Dialog,
Tooltip drops `react-tooltip`, JSON tree→Collapsible); framer kept only
where animation is the public contract.
- **Fixed an inert axe gate** in `.storybook/vitest.setup.ts` (a11y
addon annotations were never registered). Now live; 119 stories carry
`a11y: 'todo'` overrides pending a fix pass.
2026-06-11 11:02:28 +02:00
Abdullah.
c05c34aaec [Website] Convert remaining images to WebP and compress some current ones. (#21404)
As title. Also deleted some unused assets that are no longer required.
2026-06-11 08:47:44 +00:00
Charles Bochet
2da28cb03e security: upgrade express 4.22.2 + qs 6.15.2 resolution for dev-tool holdouts (Dependabot alert 1305) (#21434)
Closes the qs Dependabot alert —
[1305](https://github.com/twentyhq/twenty/security/dependabot/1305) — by
**upgrading express where possible** and using a **documented qs
resolution** only for the irreducible dev-only holdouts.

### What's vulnerable
`qs 6.14.x` (the `qs.stringify` DoS) is pulled by `express 4.22.1` /
`body-parser 1.x`. The fix is `qs 6.15.2`, and there's **no backport to
the 6.14 line**.

### Upgrade what we can (no resolution)
`express 4.22.2` / `body-parser 1.20.5` moved to the patched `qs
~6.15.1`. So this PR bumps the app + in-range tooling to **express
4.22.2**:
- twenty-server's stale direct pin `4.22.1 → 4.22.2` (its runtime HTTP
is already express 5.2.1 via `@nestjs/platform-express`; this just
patches the redundant direct dep — typecheck passes),
- nx / electron-forge / webpack-dev-server / companion follow via `yarn
up -R express body-parser`.

### Resolution only for the two holdouts
Two **dev-only** tools pin express *exactly* with no patched release on
a line we can use, so they still drag in `qs 6.14.2`:
- **verdaccio** `4.22.1` — express 5 only landed in the **v7 beta**
([verdaccio#5680](https://github.com/verdaccio/verdaccio/issues/5680),
[#2479](https://github.com/verdaccio/verdaccio/issues/2479)), **not**
backported to the 6.x line we use.
- **@mintlify/previewing** `4.22.0` — closed-source, latest still pins
4.22.0, no movement.

A `qs: 6.15.2` resolution covers those, documented with a top-level
`//resolutions` note and a removal trigger.

### Verification
- `yarn install --immutable` ✓; every `qs` resolves to `6.15.2`; express
app/tooling on `4.22.2` (only verdaccio/mintlify remain on old express,
neutralized by the resolution).
- `twenty-server` typecheck ✓ (90 files import express types;
4.22.1→4.22.2 is a patch).
- Non-exploitable in prod regardless (express/body-parser use
`qs.parse`, not the vulnerable `stringify`).
2026-06-11 10:46:04 +02:00
machinagod
233a6f9fb1 fix(server): register Lingui message compiler to stop "Uncompiled message detected" log flood (#21416)
## Summary

Fixes #21415.

Server-side `` t`…` `` macro calls (e.g. `FlatEntityMapsException`)
resolve against Lingui's global `i18n` singleton, which `I18nService`
never loads a catalog into and never registers a messages compiler on.
With no `_messageCompiler` set, every such lookup logs `Uncompiled
message detected!` and falls back to the raw string — flooding server
logs (enough to hit hosting log rate limits) and disabling ICU
interpolation on those messages. The warning is emitted unconditionally
by `@lingui/core` (not `NODE_ENV`-gated).

## Changes

Register `compileMessage` via `setMessagesCompiler` in
`I18nService.loadTranslations()`:
- on the global `i18n` singleton (used by the `t` macro), and
- on each per-locale instance.

`@lingui/message-utils` is already a transitive **runtime** dependency
of `@lingui/core` (`@lingui/core@5.1.2 → @lingui/message-utils@^5.1.2`),
so no new dependency is added.

## Testing

Deployed on a real instance: before, the server emitted hundreds of
`Uncompiled message detected` lines/sec (saturating the log rate limit);
after, steady-state shows `0`, and the worker (which shares the code
path) likewise shows `0`. App behaviour is unchanged — messages already
rendered via fallback; this silences the warning and enables ICU on the
affected messages.

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
Co-authored-by: Félix Malfait <felix@twenty.com>
2026-06-11 10:05:24 +02:00
nitin
20c83e1f86 fix(kanban): preserve scroll on board re-init + propagate same-column reorders via SSE (#20637)
closes
https://discord.com/channels/1130383047699738754/1504130730840821860


https://github.com/user-attachments/assets/d5833031-01c6-4e46-b699-c29c42435a53





## Summary

Fixes two related issues with the kanban (board view) collaboration
experience:

1. **Scroll-to-top on every data change** —
`triggerRecordBoardInitialQuery` always scrolled the board to the top,
even when re-initializing for a single-record data change (SSE echo of
your own mutation, a collaborator's update). Scroll reset only makes
sense when the dataset itself changes (filter / sort / group).
2. **Same-column reorders by other users did not propagate** — the
server's diff function stripped `FieldMetadataType.POSITION`, so
position-only updates produced empty `updatedFields` and short-circuited
event emission entirely. SSE clients never received them.

## What's in here

- **Frontend** — `useTriggerRecordBoardInitialQuery` now exposes a
`triggerRecordBoardInitialQueryWithoutScrollReset` variant; data-driven
re-inits in `RecordBoardDataChangedEffect` use it, while genuine filter
/ sort / group changes keep the scroll-resetting
`triggerRecordBoardInitialQuery`. `getRecordBoardEffectsForUpdateInputs`
classifies each update as `trigger-initial-query` / `reposition-records`
/ `none`. For position- or group-only changes we skip the re-query and
reposition records in place in the store
(`useRepositionRecordsOnBoard`), which avoids the flicker and preserves
scroll.
- **Server** — removes `POSITION` from `objectRecordChangedValues`'
strip list, so position-only updates emit a non-empty diff and flow
through SSE. Position is now treated as a field like any other across
all event consumers (SSE, webhooks, workflows, logic functions); a
trigger with an explicit field filter still excludes it.
2026-06-11 07:26:51 +00:00
Charles Bochet
9100fb1e9f security: scoped resolution for webpack-dev-server 5.2.4 (Dependabot alerts 1237/691/692) (#21420)
Closes 3 webpack-dev-server Dependabot alerts —
[1237](https://github.com/twentyhq/twenty/security/dependabot/1237),
[691](https://github.com/twentyhq/twenty/security/dependabot/691),
[692](https://github.com/twentyhq/twenty/security/dependabot/692) — with
a **scoped** resolution:
`@electron-forge/plugin-webpack/webpack-dev-server: 5.2.4`.

(These numbers are Dependabot **alert** IDs, not issue/PR numbers —
written without `#` to avoid cross-linking unrelated issues, per review
feedback.)

### Why a resolution (the one place it's unavoidable)
webpack-dev-server is pulled **only** by
`@electron-forge/plugin-webpack` (twenty-companion's build tooling), and
**no electron-forge release uses webpack-dev-server 5** — not even
`8.0.0-alpha.9` still pins `^4`. There is no parent-upgrade path, so a
resolution is the only mechanism.

### Scoped, not global
Per review feedback, the resolution targets
`@electron-forge/plugin-webpack/webpack-dev-server` rather than a global
override — it only forces v5 under electron-forge (the sole consumer),
limiting blast radius. Verified the scoped syntax is honored: removing
it reverts the lockfile to `webpack-dev-server@npm:^4.0.0`; with it, the
lock pins `webpack-dev-server@npm:5.2.4` and `yarn install --immutable`
passes.

### Why it's safe (constructor did NOT change v4→v5)
`@electron-forge/plugin-webpack@7` calls `new
WebpackDevServer(this.devServerOptions(), compiler)`.
webpack-dev-server's constructor is `constructor(options, compiler)` in
**both v4 and v5** (verified in `lib/Server.js:331` and
`types/lib/Server.d.ts:1179`). The `(compiler, options)` → `(options,
compiler)` swap happened at **v3 → v4**, not v4 → v5. The plugin passes
only options unchanged in v5 (`hot`, `devMiddleware.writeToDisk`,
`historyApiFallback`, `port`, `setupExitSignals`, `static`, `headers`)
and uses none of the hooks v5 removed.

### Scope / verification
- `yarn install --immutable` ✓; webpack-dev-server resolves to 5.2.4
only (was 4.15.2), no vulnerable copy remains.
- Only exercised by `electron-forge start` (dev HMR); production
`make`/`package` builds don't use it, and **twenty-companion has no CI
workflow**, so this can't affect CI.
- Residual manual check (not CI-covered): `yarn start:electron` in
twenty-companion still boots the dev server.
2026-06-11 09:38:16 +02:00
Félix Malfait
9d7f0c405f fix(twenty-front): new layout fast-follows — command menu, field options & logs (#21429)
Fast-follows for the new layout — the remaining open sub-issues of
twentyhq/core-team-issues#2478.

## Changes

- **Command menu items should not have extra right padding**
(twentyhq/core-team-issues#2500)
`SidePanelList` set `width: calc(100% - spacing[4])` on top of its own
8px left/right padding. Under the global `box-sizing: border-box`, that
extra `-16px` shrinks the list and, because it's left-aligned, dumps the
whole gap on the right. Switched to `width: 100%` so item highlights
inset 8px symmetrically. This is shared by every side-panel list — they
all had the same right-only gutter, so they're all corrected the same
way.

- **Field options should not be cropped and should keep row gaps**
(twentyhq/core-team-issues#2503)
The option row used a fixed `height: spacing[6]`, so under border-box
the `6px` vertical padding was absorbed and consecutive rows sat flush.
Changed `height` → `min-height` so the padding separates the rows again.

- **Logs table with filters should use Background secondary**
(twentyhq/core-team-issues#2505)
The Logs filter card and the upgrade card defaulted to a transparent
background, showing the white page through. Passed
`backgroundColor={themeCssVariables.background.secondary}`, matching
`SettingsTableCard`. The results table stays on the primary surface, per
the Figma reference.

- **Command menu back chevron** (twentyhq/core-team-issues#2504)
`SidePanelTopBar` showed a back chevron whenever the nav stack had more
than one entry. A command-menu page is the root of a fresh command-menu
session, so it now only shows the chevron when it was opened from
another command-menu page. Every other side-panel page keeps standard
history-based back navigation, so workflow / page-layout / record stacks
are unaffected.

## Verification
- oxlint (`--type-aware`, full `src/`): 0 errors
- oxfmt: clean
- tsgo typecheck: no errors in the changed files (the one reported error
is pre-existing in `RestPlayground.tsx`, which this PR does not touch)
- Verified live at apple.localhost:
- #2500 — command menu item highlight insets measured 8px left / 8px
right (was 8 / 24)
- #2503 — option rows render at 38px tall with ~14px gaps, text no
longer cropped
- #2505 — filter + upgrade cards compute to background-secondary;
results table stays on primary
- #2504 — direct command menu shows the close-X with no chevron; pages
opened from the command menu still show the chevron

## Open question for review (#2504)
The issue also describes the page-header three-dots toggle: *"the three
dots icon button should remain visible if the side panel is on a page
that is not a child of the command menu (AI chat, or a page opened
directly)."* Today that toggle morphs to an X for any non-command-menu
side-panel page (e.g. Ask AI). Honoring that touches the shared
`SidePanelToggleButton`, and there's a related decision: search / Ask AI
opened from the command menu currently reset the nav stack rather than
push, so they don't get a "back to command menu" chevron. I left those
out here since they're a behavior change to a shared control with a
product call attached — happy to follow up once you confirm the intended
toggle behavior.

Closes twentyhq/core-team-issues#2500
Closes twentyhq/core-team-issues#2503
Closes twentyhq/core-team-issues#2505
Refs twentyhq/core-team-issues#2504
Refs twentyhq/core-team-issues#2478
2026-06-11 08:58:23 +02:00
dependabot[bot]
f55a45f4e0 chore(deps-dev): bump storybook from 10.2.13 to 10.4.3 (#21428)
Bumps
[storybook](https://github.com/storybookjs/storybook/tree/HEAD/code/core)
from 10.2.13 to 10.4.3.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/storybookjs/storybook/releases">storybook's
releases</a>.</em></p>
<blockquote>
<h2>v10.4.3</h2>
<h2>10.4.3</h2>
<ul>
<li>Addon Docs: Fix Primary and Controls blocks not rendering in custom
MDX pages - <a
href="https://redirect.github.com/storybookjs/storybook/pull/34496">#34496</a>,
thanks <a
href="https://github.com/NYCU-Chung"><code>@​NYCU-Chung</code></a>!</li>
<li>Core: Respect !dev tag on MDX docs in sidebar - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35031">#35031</a>,
thanks <a
href="https://github.com/JReinhold"><code>@​JReinhold</code></a>!</li>
<li>React: Add support for resolving subcomponents attached as
properties of a parent component - <a
href="https://redirect.github.com/storybookjs/storybook/pull/34967">#34967</a>,
thanks <a
href="https://github.com/yatishgoel"><code>@​yatishgoel</code></a>!</li>
<li>UI: Prevent docs page scroll reset on HMR re-render - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35021">#35021</a>,
thanks <a
href="https://github.com/LongTangGithub"><code>@​LongTangGithub</code></a>!</li>
</ul>
<h2>v10.4.2</h2>
<h2>10.4.2</h2>
<ul>
<li>Bug: Fix Windows command resolution for non-Node package managers -
<a
href="https://redirect.github.com/storybookjs/storybook/pull/33534">#33534</a>,
thanks <a
href="https://github.com/copilot-swe-agent"><code>@​copilot-swe-agent</code></a>!</li>
<li>Build: Upgrade type-fest to latest version 5.6.0 - <a
href="https://redirect.github.com/storybookjs/storybook/pull/34791">#34791</a>,
thanks <a
href="https://github.com/tobiasdiez"><code>@​tobiasdiez</code></a>!</li>
<li>CSF: Fix parsing of string literal export names - <a
href="https://redirect.github.com/storybookjs/storybook/pull/34901">#34901</a>,
thanks <a
href="https://github.com/shilman"><code>@​shilman</code></a>!</li>
<li>Publish: Add npm provenance attestations - <a
href="https://redirect.github.com/storybookjs/storybook/pull/34936">#34936</a>,
thanks <a
href="https://github.com/copilot-swe-agent"><code>@​copilot-swe-agent</code></a>!</li>
</ul>
<h2>v10.4.1</h2>
<h2>10.4.1</h2>
<ul>
<li>Angular: Detect model() signal outputs (type inference + compodoc
autodocs + runtime binding) - <a
href="https://redirect.github.com/storybookjs/storybook/pull/34833">#34833</a>,
thanks <a
href="https://github.com/valentinpalkovic"><code>@​valentinpalkovic</code></a>!</li>
<li>Build: Upgrade type-fest to latest version 5.6.0 - <a
href="https://redirect.github.com/storybookjs/storybook/pull/34791">#34791</a>,
thanks <a
href="https://github.com/tobiasdiez"><code>@​tobiasdiez</code></a>!</li>
<li>CLI: Run `npx expo install --fix` after init for Expo projects - <a
href="https://redirect.github.com/storybookjs/storybook/pull/34803">#34803</a>,
thanks <a
href="https://github.com/ndelangen"><code>@​ndelangen</code></a>!</li>
<li>CLI: Support `peerDependencies` in framework detection for component
libraries - <a
href="https://redirect.github.com/storybookjs/storybook/pull/34516">#34516</a>,
thanks <a
href="https://github.com/zhyd1997"><code>@​zhyd1997</code></a>!</li>
<li>Next.js: Add useLinkStatus mock to next/link export mock - <a
href="https://redirect.github.com/storybookjs/storybook/pull/34593">#34593</a>,
thanks <a
href="https://github.com/philwolstenholme"><code>@​philwolstenholme</code></a>!</li>
<li>Vue3: Specify a specific version for non-dev dependency - <a
href="https://redirect.github.com/storybookjs/storybook/pull/34794">#34794</a>,
thanks <a
href="https://github.com/ScopeyNZ"><code>@​ScopeyNZ</code></a>!</li>
</ul>
<h2>v10.4.0</h2>
<h2>10.4.0</h2>
<blockquote>
<p><em>AI-assisted setup, change-aware review, and stronger framework
support</em></p>
</blockquote>
<p>Storybook 10.4 contains hundreds of fixes and improvements
including:</p>
<ul>
<li>🤖 Agentic Setup: New CLI workflow for AI-assisted Storybook setup
and onboarding</li>
<li>🔍 Change review: Sidebar filtering to highlight new, modified, and
related stories based on git changes</li>
<li>🧭 Sidebar review tools: Status filtering, URL-persisted filters, and
clearer review signals in the sidebar</li>
<li>⚛️ TanStack React: New `@storybook/tanstack-react` framework with
routing and server function support</li>
<li>🧩 React MCP: Faster, more accurate component docgen powered by the
TypeScript Language Server</li>
<li>📱 React Native: Zero config RN project initialization</li>
<li>🤝 Sharing: Easily publish and share your local Storybook with
teammates, powered by Chromatic</li>
</ul>
<!-- raw HTML omitted -->
<ul>
<li>A11y: Add aria-live announcements via
<code>@​react-aria/live-announcer</code> - <a
href="https://redirect.github.com/storybookjs/storybook/pull/33970">#33970</a>,
thanks <a
href="https://github.com/copilot-swe-agent"><code>@​copilot-swe-agent</code></a>!</li>
<li>A11y: Improve boolean control contrast in forced colors mode - <a
href="https://redirect.github.com/storybookjs/storybook/pull/34204">#34204</a>,
thanks <a
href="https://github.com/anchmelev"><code>@​anchmelev</code></a>!</li>
<li>Actions: Fix state mutation and keep newest actions when limit
reached - <a
href="https://redirect.github.com/storybookjs/storybook/pull/34286">#34286</a>,
thanks <a
href="https://github.com/Sidnioulz"><code>@​Sidnioulz</code></a>!</li>
<li>Addon-Docs: Add Reset story button to re-render stories in docs - <a
href="https://redirect.github.com/storybookjs/storybook/pull/34086">#34086</a>,
thanks <a
href="https://github.com/6810779s"><code>@​6810779s</code></a>!</li>
<li>Addon-Docs: Avoid rerendering static Source blocks - <a
href="https://redirect.github.com/storybookjs/storybook/pull/34206">#34206</a>,
thanks <a
href="https://github.com/anchmelev"><code>@​anchmelev</code></a>!</li>
<li>Addon-Vitest: Use Vitest's provide-API for injecting values - <a
href="https://redirect.github.com/storybookjs/storybook/pull/34518">#34518</a>,
thanks <a
href="https://github.com/JReinhold"><code>@​JReinhold</code></a>!</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md">storybook's
changelog</a>.</em></p>
<blockquote>
<h2>10.4.3</h2>
<ul>
<li>Addon Docs: Fix Primary and Controls blocks not rendering in custom
MDX pages - <a
href="https://redirect.github.com/storybookjs/storybook/pull/34496">#34496</a>,
thanks <a
href="https://github.com/NYCU-Chung"><code>@​NYCU-Chung</code></a>!</li>
<li>Core: Respect !dev tag on MDX docs in sidebar - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35031">#35031</a>,
thanks <a
href="https://github.com/JReinhold"><code>@​JReinhold</code></a>!</li>
<li>React: Add support for resolving subcomponents attached as
properties of a parent component - <a
href="https://redirect.github.com/storybookjs/storybook/pull/34967">#34967</a>,
thanks <a
href="https://github.com/yatishgoel"><code>@​yatishgoel</code></a>!</li>
<li>UI: Prevent docs page scroll reset on HMR re-render - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35021">#35021</a>,
thanks <a
href="https://github.com/LongTangGithub"><code>@​LongTangGithub</code></a>!</li>
</ul>
<h2>10.4.2</h2>
<ul>
<li>Bug: Fix Windows command resolution for non-Node package managers -
<a
href="https://redirect.github.com/storybookjs/storybook/pull/33534">#33534</a>,
thanks <a
href="https://github.com/copilot-swe-agent"><code>@​copilot-swe-agent</code></a>!</li>
<li>Build: Upgrade type-fest to latest version 5.6.0 - <a
href="https://redirect.github.com/storybookjs/storybook/pull/34791">#34791</a>,
thanks <a
href="https://github.com/tobiasdiez"><code>@​tobiasdiez</code></a>!</li>
<li>CSF: Fix parsing of string literal export names - <a
href="https://redirect.github.com/storybookjs/storybook/pull/34901">#34901</a>,
thanks <a
href="https://github.com/shilman"><code>@​shilman</code></a>!</li>
<li>Publish: Add npm provenance attestations - <a
href="https://redirect.github.com/storybookjs/storybook/pull/34936">#34936</a>,
thanks <a
href="https://github.com/copilot-swe-agent"><code>@​copilot-swe-agent</code></a>!</li>
</ul>
<h2>10.4.1</h2>
<ul>
<li>Angular: Detect model() signal outputs (type inference + compodoc
autodocs + runtime binding) - <a
href="https://redirect.github.com/storybookjs/storybook/pull/34833">#34833</a>,
thanks <a
href="https://github.com/valentinpalkovic"><code>@​valentinpalkovic</code></a>!</li>
<li>Build: Upgrade type-fest to latest version 5.6.0 - <a
href="https://redirect.github.com/storybookjs/storybook/pull/34791">#34791</a>,
thanks <a
href="https://github.com/tobiasdiez"><code>@​tobiasdiez</code></a>!</li>
<li>CLI: Run <code>npx expo install --fix</code> after init for Expo
projects - <a
href="https://redirect.github.com/storybookjs/storybook/pull/34803">#34803</a>,
thanks <a
href="https://github.com/ndelangen"><code>@​ndelangen</code></a>!</li>
<li>CLI: Support <code>peerDependencies</code> in framework detection
for component libraries - <a
href="https://redirect.github.com/storybookjs/storybook/pull/34516">#34516</a>,
thanks <a
href="https://github.com/zhyd1997"><code>@​zhyd1997</code></a>!</li>
<li>Next.js: Add useLinkStatus mock to next/link export mock - <a
href="https://redirect.github.com/storybookjs/storybook/pull/34593">#34593</a>,
thanks <a
href="https://github.com/philwolstenholme"><code>@​philwolstenholme</code></a>!</li>
<li>Vue3: Specify a specific version for non-dev dependency - <a
href="https://redirect.github.com/storybookjs/storybook/pull/34794">#34794</a>,
thanks <a
href="https://github.com/ScopeyNZ"><code>@​ScopeyNZ</code></a>!</li>
</ul>
<h2>10.4.0</h2>
<blockquote>
<p><em>AI-assisted setup, change-aware review, and stronger framework
support</em></p>
</blockquote>
<p>Storybook 10.4 contains hundreds of fixes and improvements
including:</p>
<ul>
<li>🤖 Agentic Setup: New CLI workflow for AI-assisted Storybook setup
and onboarding</li>
<li>🔍 Change review: Sidebar filtering to highlight new, modified, and
related stories based on git changes</li>
<li>🧭 Sidebar review tools: Status filtering, URL-persisted filters, and
clearer review signals in the sidebar</li>
<li>⚛️ TanStack React: New <code>@storybook/tanstack-react</code>
framework with routing and server function support</li>
<li>🧩 React MCP: Faster, more accurate component docgen powered by the
TypeScript Language Server</li>
<li>📱 React Native: Zero config RN project initialization</li>
<li>🤝 Sharing: Easily publish and share your local Storybook with
teammates, powered by Chromatic</li>
</ul>
<!-- raw HTML omitted -->
<ul>
<li>A11y: Add aria-live announcements via
<code>@​react-aria/live-announcer</code> - <a
href="https://redirect.github.com/storybookjs/storybook/pull/33970">#33970</a>,
thanks <a
href="https://github.com/copilot-swe-agent"><code>@​copilot-swe-agent</code></a>!</li>
<li>A11y: Improve boolean control contrast in forced colors mode - <a
href="https://redirect.github.com/storybookjs/storybook/pull/34204">#34204</a>,
thanks <a
href="https://github.com/anchmelev"><code>@​anchmelev</code></a>!</li>
<li>Actions: Fix state mutation and keep newest actions when limit
reached - <a
href="https://redirect.github.com/storybookjs/storybook/pull/34286">#34286</a>,
thanks <a
href="https://github.com/Sidnioulz"><code>@​Sidnioulz</code></a>!</li>
<li>Addon-Docs: Add Reset story button to re-render stories in docs - <a
href="https://redirect.github.com/storybookjs/storybook/pull/34086">#34086</a>,
thanks <a
href="https://github.com/6810779s"><code>@​6810779s</code></a>!</li>
<li>Addon-Docs: Avoid rerendering static Source blocks - <a
href="https://redirect.github.com/storybookjs/storybook/pull/34206">#34206</a>,
thanks <a
href="https://github.com/anchmelev"><code>@​anchmelev</code></a>!</li>
<li>Addon-Vitest: Use Vitest's provide-API for injecting values - <a
href="https://redirect.github.com/storybookjs/storybook/pull/34518">#34518</a>,
thanks <a
href="https://github.com/JReinhold"><code>@​JReinhold</code></a>!</li>
<li>Agentic Setup: Add --extensive for an extra prompt - <a
href="https://redirect.github.com/storybookjs/storybook/pull/34730">#34730</a>,
thanks <a
href="https://github.com/Sidnioulz"><code>@​Sidnioulz</code></a>!</li>
<li>Agentic Setup: Allow failed stories to persist - <a
href="https://redirect.github.com/storybookjs/storybook/pull/34717">#34717</a>,
thanks <a
href="https://github.com/Sidnioulz"><code>@​Sidnioulz</code></a>!</li>
<li>Agentic Setup: Keep sample content if users want onboarding - <a
href="https://redirect.github.com/storybookjs/storybook/pull/34704">#34704</a>,
thanks <a
href="https://github.com/Sidnioulz"><code>@​Sidnioulz</code></a>!</li>
<li>Agentic Setup: Rework ai-init-opt-in logic - <a
href="https://redirect.github.com/storybookjs/storybook/pull/34739">#34739</a>,
thanks <a
href="https://github.com/Sidnioulz"><code>@​Sidnioulz</code></a>!</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="624e6187fd"><code>624e618</code></a>
Bump version from &quot;10.4.2&quot; to &quot;10.4.3&quot; [skip
ci]</li>
<li><a
href="c898822822"><code>c898822</code></a>
Merge pull request <a
href="https://github.com/storybookjs/storybook/tree/HEAD/code/core/issues/34496">#34496</a>
from NYCU-Chung/fix/docs-blocks-custom-mdx</li>
<li><a
href="c920fd08c7"><code>c920fd0</code></a>
Merge pull request <a
href="https://github.com/storybookjs/storybook/tree/HEAD/code/core/issues/35021">#35021</a>
from LongTangGithub/fix/docs-hmr-scroll-to-top</li>
<li><a
href="1750494e9f"><code>1750494</code></a>
Merge pull request <a
href="https://github.com/storybookjs/storybook/tree/HEAD/code/core/issues/35031">#35031</a>
from storybookjs/jeppe/fix-mdx-no-dev-tag</li>
<li><a
href="298dea20c6"><code>298dea2</code></a>
Bump version from &quot;10.4.1&quot; to &quot;10.4.2&quot; [skip
ci]</li>
<li><a
href="40c81c8187"><code>40c81c8</code></a>
Merge pull request <a
href="https://github.com/storybookjs/storybook/tree/HEAD/code/core/issues/33534">#33534</a>
from storybookjs/copilot/fix-pnpm-command-resolutio...</li>
<li><a
href="bfb942b532"><code>bfb942b</code></a>
Merge pull request <a
href="https://github.com/storybookjs/storybook/tree/HEAD/code/core/issues/34901">#34901</a>
from storybookjs/shilman/fix-csf-export-as-string-l...</li>
<li><a
href="fad8dd2b63"><code>fad8dd2</code></a>
Merge pull request <a
href="https://github.com/storybookjs/storybook/tree/HEAD/code/core/issues/34791">#34791</a>
from tobiasdiez/type-fest-upgrade</li>
<li><a
href="cc19ae1a21"><code>cc19ae1</code></a>
Bump version from &quot;10.4.0&quot; to &quot;10.4.1&quot; [skip
ci]</li>
<li><a
href="f8c16d115c"><code>f8c16d1</code></a>
Bump version from &quot;10.4.0-beta.0&quot; to &quot;10.4.0&quot; [skip
ci]</li>
<li>Additional commits viewable in <a
href="https://github.com/storybookjs/storybook/commits/v10.4.3/code/core">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=storybook&package-manager=npm_and_yarn&previous-version=10.2.13&new-version=10.4.3)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-11 07:34:34 +02:00
dependabot[bot]
dac36b83ce chore(deps-dev): bump @types/aws-lambda from 8.10.161 to 8.10.162 (#21427)
Bumps
[@types/aws-lambda](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/aws-lambda)
from 8.10.161 to 8.10.162.
<details>
<summary>Commits</summary>
<ul>
<li>See full diff in <a
href="https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/aws-lambda">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=@types/aws-lambda&package-manager=npm_and_yarn&previous-version=8.10.161&new-version=8.10.162)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-11 07:29:44 +02:00
dependabot[bot]
f8b0dca3f9 chore(deps): bump @tabler/icons-react from 3.31.0 to 3.44.0 (#21426)
Bumps
[@tabler/icons-react](https://github.com/tabler/tabler-icons/tree/HEAD/packages/icons-react)
from 3.31.0 to 3.44.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/tabler/tabler-icons/releases">@​tabler/icons-react's
releases</a>.</em></p>
<blockquote>
<h2>Release 3.44.0</h2>
<!-- raw HTML omitted -->
<h3>18 new icons:</h3>
<ul>
<li><code>outline/code-ai</code></li>
<li><code>outline/email-stamp</code></li>
<li><code>outline/foodsteps</code></li>
<li><code>outline/git-pull-request-conflict</code></li>
<li><code>outline/noise-reduction</code></li>
<li><code>outline/photo-alt</code></li>
<li><code>outline/pointer-2</code></li>
<li><code>outline/pointer-collaboration-2</code></li>
<li><code>outline/pointer-collaboration</code></li>
<li><code>outline/roulette</code></li>
<li><code>outline/scan-cube</code></li>
<li><code>outline/sketching</code></li>
<li><code>outline/sparkle-2</code></li>
<li><code>outline/sparkle-highlight</code></li>
<li><code>outline/sparkle</code></li>
<li><code>outline/sphere-2</code></li>
<li><code>outline/text-scan-ai</code></li>
<li><code>outline/vignette</code></li>
</ul>
<p>Fixed icons: <code>outline/air-balloon</code>,
<code>outline/body-scan</code>, <code>outline/chart-sankey</code>,
<code>outline/ear-scan</code>, <code>outline/grid-scan</code>,
<code>outline/line-scan</code>, <code>outline/object-scan</code>,
<code>outline/photo-scan</code>, <code>outline/route-scan</code>,
<code>outline/scan-eye</code>, <code>outline/scan-letter-a</code>,
<code>outline/scan-letter-t</code>, <code>outline/scan-position</code>,
<code>outline/scan-traces</code>, <code>outline/scan</code>,
<code>outline/text-scan-2</code>, <code>outline/user-scan</code>,
<code>outline/zoom-scan</code></p>
<h2>Release 3.43.0</h2>
<!-- raw HTML omitted -->
<h3>18 new icons:</h3>
<ul>
<li><code>outline/acorn</code></li>
<li><code>outline/acrobatic</code></li>
<li><code>outline/banana</code></li>
<li><code>outline/brand-audible</code></li>
<li><code>outline/building-eiffel-tower</code></li>
<li><code>outline/car-door</code></li>
<li><code>outline/car-lifter</code></li>
<li><code>outline/chocolate</code></li>
<li><code>outline/dumbbell</code></li>
<li><code>outline/exercise-ball</code></li>
<li><code>outline/flood</code></li>
<li><code>outline/hula-hoop</code></li>
<li><code>outline/leaf-maple</code></li>
<li><code>outline/notdef</code></li>
<li><code>outline/rugby</code></li>
<li><code>outline/taiwan-dollar</code></li>
<li><code>outline/target-2</code></li>
<li><code>outline/unicycle</code></li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="6d128ed935"><code>6d128ed</code></a>
Release 3.44.0</li>
<li><a
href="e40738b644"><code>e40738b</code></a>
Release 3.43.0</li>
<li><a
href="076f4a9e4d"><code>076f4a9</code></a>
Release 3.42.0</li>
<li><a
href="9b27b65e3d"><code>9b27b65</code></a>
Release 3.41.1</li>
<li><a
href="ebad60b50a"><code>ebad60b</code></a>
Update homepage links in documentation and package files to point to the
new ...</li>
<li><a
href="8ed617b1b7"><code>8ed617b</code></a>
Update README files to wrap images in anchor tags linking to the Tabler
Icons...</li>
<li><a
href="ef6e875bbe"><code>ef6e875</code></a>
Update dependencies in pnpm-lock.yaml and package.json files (<a
href="https://github.com/tabler/tabler-icons/tree/HEAD/packages/icons-react/issues/1497">#1497</a>)</li>
<li><a
href="6cbe8857c8"><code>6cbe885</code></a>
Release 3.41.0</li>
<li><a
href="19d735e620"><code>19d735e</code></a>
Add JSDoc with previews in icons-react (<a
href="https://github.com/tabler/tabler-icons/tree/HEAD/packages/icons-react/issues/1472">#1472</a>)</li>
<li><a
href="e4ca37707b"><code>e4ca377</code></a>
Release 3.40.0</li>
<li>Additional commits viewable in <a
href="https://github.com/tabler/tabler-icons/commits/v3.44.0/packages/icons-react">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=@tabler/icons-react&package-manager=npm_and_yarn&previous-version=3.31.0&new-version=3.44.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-11 07:29:32 +02:00
Joseph Chiang
941c9e7586 fix: match relation field filters in optimistic & RLS record matchers (#21301)
Closes #21345.

## What

It should be caused by the GraphQL optimistic query.

`isRecordMatchingFilter` (front, Apollo optimistic cache) and
`isRecordMatchingRLSRowLevelPermissionPredicate` (server, RLS) now
handle a view filter that targets a **relation field object** (e.g. an
"is (not) empty" filter on a relation) by matching against the related
record id, instead of throwing.


<img width="3436" height="2250" alt="CleanShot 2026-06-08 at 06 44
01@2x"
src="https://github.com/user-attachments/assets/1dccbd1e-133c-4f4a-a0a9-7ccd02a9a0ae"
/>

<img width="1496" height="380" alt="CleanShot 2026-06-08 at 06 45 34@2x"
src="https://github.com/user-attachments/assets/e5c2071e-69df-4d99-bb7d-66d503a175b6"
/>


## Why

Both matchers only implemented the relation **join column** branch
(`fooId`) and threw `Not implemented yet, use UUID filter instead on the
corresponding "fooId" field` for the relation field itself (`foo`). In
practice the UI still stores relation filters keyed on the relation
object, so any view with such a filter made every create/update/delete
on that object throw: the optimistic effect re-evaluates all active view
filters against the changed record and hits the unimplemented branch.

Repro: add a self-relation field on People (e.g. "Referred By"), put it
in a view filter as "is not empty", then edit any Person. The optimistic
update throws.

## Behaviour change

| Scenario | Before | After |
|---|---|---|
| View filter on relation object (`referredBy is not empty`), then edit
a record | Throws `Not implemented yet...` | Record matched by related
id; update succeeds |
| Filter on relation join column (`referredById`) | Worked | Unchanged |

## Test plan

```bash
cd packages/twenty-front && npx jest isRecordMatchingFilter
cd packages/twenty-server && npx jest is-record-matching-rls-row-level-permission-predicate
```

- [x] Front: relation `is empty` / `is not empty` / `in` match by
related id; join-column path still passes (20/20)
- [x] Server: relation `is empty` / `is not empty` match by related id
(9/9)
- [x] `lint:diff-with-main` clean on both packages

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
2026-06-11 07:29:10 +02:00
Gami
61b76b681e fix: i18n missing hardcoded strings in settings (#21424)
## What

Two user-visible strings in the Settings area were never wrapped with
Lingui, so they were excluded from i18n extraction and shipped
untranslated regardless of the selected language:

- **"Remote"** — the type chip shown for remote objects in **Settings →
Data Model** (`SettingsItemTypeTag`)
- **"Done"** — the confirm button of the fields configuration group
rename input (`FieldsConfigurationGroupRenameInput`)

## Changes

- Wrap the `Chip` `label` with the `t` macro in
`SettingsItemTypeTag.tsx` (the `placeholder` / `placeholderColorSeed`
props are intentionally left as-is — they drive the avatar initial and
color hash, not display text).
- Wrap the `Button` `title` with the existing `t` from `useLingui()` in
`FieldsConfigurationGroupRenameInput.tsx`.
- Add the corresponding source entries to `en.po` so Crowdin can
propagate the translations to all supported locales.

Both follow i18n patterns already used throughout the codebase — these
two were simply missed.

## Screenshots

Both components rendered via Storybook (source `en` locale) after the
change — the strings now resolve through Lingui's `t` macro without
breaking rendering:

![i18n settings
strings](https://raw.githubusercontent.com/AmilGael/twenty/pr-assets/.github/pr-assets/i18n-hardcoded-strings-settings.png)

## How to test

1. Switch the workspace language to a non-English locale.
2. Go to **Settings → Data Model** with a remote object present → the
type chip reads "Remote" translated.
3. Rename a fields configuration group → the confirm button reads "Done"
translated.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-11 04:38:58 +00:00
Charles Bochet
615c3d8dbe security: drop end-of-life apollo-server-core (#735, #736) (#21418)
Closes the `apollo-server-core` alerts (**#735**, **#736**) by
**removing the dependency** — no Apollo migration, no resolution.

### Why these were flagged "no patch available"
`apollo-server-core` is **Apollo Server v3, which is end-of-life** (per
its npm deprecation notice). No patched release of this package will
ever exist — the CVE fix lives only in the renamed `@apollo/server` v4
package.

### Why we can just drop it
twenty-server **doesn't use Apollo Server** — its GraphQL runtime is
**GraphQL Yoga** (`YogaDriver`). `apollo-server-core` was imported for
one thing only: the `gql` template tag in **6 integration test files**.
`gql` from `graphql-tag` is identical (apollo-server-core merely
re-exports it), `graphql-tag` is **already a direct dependency**, and
**15 other twenty-server tests already import `gql` from it**.

### Change
- Swapped `import { gql } from 'apollo-server-core'` → `import { gql }
from 'graphql-tag'` in the 6 test files.
- Removed `apollo-server-core` from
`packages/twenty-server/package.json`.
- Result: `apollo-server-core` (and its transitive surface) is gone from
`yarn.lock` entirely.

### Verification
- `yarn install --immutable` ✓
- No `apollo-server-core` references remain in source or lockfile
- Integration tests (which exercise the swapped `gql` imports) run in CI
2026-06-10 19:08:07 +02:00
Charles Bochet
1b26061992 security: close ws & file-type alerts via parent upgrades (no resolution) (#21417)
Closes 3 more Dependabot alerts via **parent upgrades only — no
`resolutions`**.

### Closes
- **ws #1238** (`>= 8.0.0, < 8.20.1`)
- **file-type #622** (`>= 13.0.0, < 21.3.1`) and **#635** (`>= 20.0.0,
<= 21.3.1`)

### How
- **ws** — two parents pinned vulnerable copies; both have safe
releases:
  - `wrangler`/`miniflare`: `ws 8.18.0 → 8.20.1`
  - `socket.io`/`engine.io`/`socket.io-adapter`: `ws ~8.17.1 → ~8.20.1`
  - Every `ws` now resolves `>= 8.20.1` (or non-vulnerable 6.x/7.x).
- **file-type** — bumped `@swc/cli ^0.7.10 → ^0.8.1`, which pulls the
newer `@xhmikosr` bin-wrapper → downloader → decompress/archive-type
chain (`file-type ^20.5.0 → ^21.3.x`). `@swc/cli` is a devDependency
that isn't directly invoked (nx's swc compiler uses `@swc/core`), so
this is dev-tooling-only with no runtime impact.

### Verification
- `yarn install --immutable` ✓ (passes the hardened 3-day age gate)
- No vulnerable `ws` (`< 8.20.1`) or `file-type` (`20.x`) remains in
`yarn.lock`

### Remaining open alerts (not closeable without a `resolution` or a
major migration)
- `apollo-server-core` (#735/#736) — needs Apollo Server 3 → 4
- `webpack-dev-server` (#1237/#691/#692) —
`@electron-forge/plugin-webpack` only pins `^4` (no stable v5 consumer);
dev-tooling
- `qs` (#1305) — `express`/`body-parser` pin `~6.14`, which has no
patched release
- `uuid` (#1289) — spread across many `^8`/`^9`/`^10` transitives; v11
is a breaking jump
- `postcss` (#1061) — bundled exact by `next` and `styled-components`
- `ajv` (#481) — `@cyntler/react-doc-viewer` pins `^7` (latest still
does)
2026-06-10 19:03:28 +02:00
Charles Bochet
868cb02e45 security: close lodash CVEs (#824/#823/#385) via parent upgrades, no resolution (#21414)
Closes the remaining lodash Dependabot alerts **without any
`resolutions` override** — by upgrading the parent packages that pinned
the vulnerable lodash. Every `lodash` in the tree now resolves to
**4.18.1**.

### Closes
- **#824 — `_.template` code injection (HIGH)**
- #823 / #385 — prototype pollution in `_.unset` / `_.omit`

### What changed (4 parents pinned vulnerable lodash 4.17.x; all
upgraded, no override)
- **`@stoplight/spectral-functions`** → 1.10.2 (in-range; now uses
`lodash ^4.18.1`)
- **`zapier-platform-core`** 15.5.1 → 19.0.0 — aligns with the
already-present `zapier-platform-cli ^19` (they were mismatched). v19
tightened the `Bundle` types, so 3 call sites now type their bundle as
`Bundle<InputData>` and the test bundle includes the new `meta` fields.
- **`@graphql-codegen`** → `cli 6.3.1`, `typescript 5.0.10`,
`typescript-operations 5.1.0`, `typed-document-node 6.1.8`. These depend
on `@graphql-codegen/plugin-helpers ^6.3.0`, the release that dropped
lodash. (Stayed on the 6.x/5.x line on purpose — 7.x changes generated
output far more.)

### About the generated-file changes — they are cosmetic, not real
changes
The codegen bump touches one generated file. **Verified there is zero
semantic change:**

- Only `src/generated-metadata/graphql.ts` changes.
`src/generated/graphql.ts` (data) and `src/generated-admin/graphql.ts`
(admin) are **byte-identical**.
- Same 1,638 type declarations before and after — none added, none
removed.
- After stripping whitespace and union pipes, the file is
**byte-for-byte identical** — no type, field, or union member changed.

The entire diff is one formatting change from
`typescript-operations@5.x`: multi-member union types are now printed
multi-line with a leading `|` instead of on one line — which TypeScript
treats identically:
```ts
// before
payload?: { …ObjectMetadata… } | { …Path… } | null
// after
payload?:
    | { …ObjectMetadata… }
    | { …Path… }
   | null
```
Only metadata is affected because only its operations select GraphQL
union types. To keep generated types otherwise behavior-identical,
`defaultScalarType: 'any'` was added to the three codegen configs
(codegen 6 would otherwise default unmapped scalars to `unknown`).

### Verification
- `twenty-front` typecheck ✓, `twenty-zapier` typecheck ✓
- `yarn install --immutable` ✓ (passes the hardened 3-day age gate)
- CI green — including the `graphql:generate` freshness check, which
regenerates against the canonical schema and confirms the committed
output is exactly what codegen produces
- No `lodash@4.17.x` remains anywhere in `yarn.lock`

Supersedes #21411 (which closed these via a one-line resolution).
2026-06-10 18:12:46 +02:00
Charles Bochet
6e147a548b security: clear twenty-apps & seed-dependencies CVE alerts (#21410)
Clears the Oneleet/dependency CVE alerts from the `twenty-apps`
example/internal app lockfiles and the application-package
`seed-dependencies` template — all via parent/direct dependency
upgrades, **no `resolutions` overrides**.

## Lock refresh (non-breaking, within existing ranges)
- **postcss** 8.5.8/8.5.9 → 8.5.15 — CVE-2026-41305 — postcard,
hello-world, self-hosting
- **ip-address** 10.1.0 → 10.2.0 — CVE-2026-42338 — postcard,
hello-world, self-hosting, twenty-for-twenty
- **yaml** 1.10.2 → 1.10.3 — CVE-2026-33532 — call-recording

## seed-dependencies (direct/parent bumps)
- **uuid** `^10.0.0 → ^11.1.1` (direct) — CVE-2026-41907
- **body-parser** `^1.20.4 → ^1.20.5`, which pulls **qs** 6.15.2 —
CVE-2026-8723
- **socks** 2.8.3 → 2.8.9 (refresh), which pulls **ip-address** 10.2.0 —
CVE-2026-42338

## twenty-for-twenty
- **resend** 6.12.0 → 6.12.4 (refresh): 6.12.4 drops the `svix` dep that
pulled the vulnerable **uuid** 10.0.0, leaving only uuid 13.0.2 —
CVE-2026-41907

All flagged packages were transitive (except the direct seed-deps
`uuid`); no app source changes.
2026-06-10 17:02:51 +02:00
Charles Bochet
a825dcf2cc security: clear 8 Dependabot alerts via transitive/parent bumps (no resolutions) (#21409)
Clears 8 Dependabot alerts via in-range transitive/parent bumps and one
dead-dependency removal. **No `resolutions` overrides** were used —
every fix is a real version bump within existing semver ranges or a
parent upgrade.

### Root `yarn.lock`
- **react-router** 6.30.3 → 6.30.4 (open redirect via protocol-relative
URL) — pulled through react-router-dom, ranges unchanged — alert #1382
- **yaml** 2.8.1 → 2.9.0 (stack overflow on deeply nested collections) —
alert #734
- **uuid** `^13.0.0` → 13.0.2 in twenty-sdk + create-twenty-app (buffer
bounds check) — alert #1164
- **ip-address** `^9.0.5` dropped by bumping **socks** 2.8.3 → 2.8.9
(now depends on `ip-address ^10.1.1`, which is unaffected) — alert #1171

### `seed-dependencies` lockfile
- **uuid** `^10.0.0` → `^11.1.1` (direct dep; removed now-redundant
`@types/uuid` since uuid v11 ships its own types) — alert #1287
- **ip-address** `^9.0.5` dropped via the same socks bump — alert #1170

### `twenty-for-twenty` lockfile
- **resend** bumped to 6.12.4 (`^6.12.0` range kept), which drops its
`svix@1.90.0 → uuid@^10` transitive chain — alert #1278

### `twenty-companion`
- Removed the unused **simplemde** dependency. The note editor loads
SimpleMDE from a CDN `<script>` tag and never imports the npm package;
`easymde` (its maintained fork) is already a dependency — alert #690

### Not addressed here
The remaining alerts can't be closed without `resolutions` overrides
(deliberately avoided in this PR) or a larger migration:
- **qs** (#1305, #1304), **lodash** (#824 high / #823 / #385), **ws**
(#1238), **postcss** (#1061) — vulnerable copies are pinned exact /
bundled by parents (express, body-parser, @nestjs/*, next,
styled-components, zapier) with no in-range patch.
- **webpack-dev-server** (#1237/#692/#691) — pinned by
`@electron-forge/plugin-webpack` (still on v4); dev-tooling only.
- **uuid <11.1.1** (#1289) — spread across `^3`/`^8`/`^9` transitive
ranges; reaching v11 is a breaking jump.
- **apollo-server-core** (#735/#736) — requires an Apollo Server 3 → 4
migration.
2026-06-10 17:02:18 +02:00
Charles Bochet
e357116024 fix(ui): freeze framer-motion in Argos runs to stop flaky visual diffs (#21412)
## Problem

twenty-ui Argos visual tests are flaky — `Loader`,
`CircularProgressBar`, `ProgressBar` get flagged as changed with no
relevant code change.

## Cause

Argos screenshots use Playwright's `animations: "disabled"`, which only
freezes CSS animations. These components animate via framer-motion
(main-thread rAF), so each run captures a different frame.

## Fix

Set `MotionGlobalConfig.skipAnimations = true` in the Vitest setup of
both UI packages — freezes framer-motion to a deterministic state during
Argos runs only (interactive Storybook unaffected). Removes the
now-redundant per-story `disableSnapshot` opt-outs.

Note: removing `CircularProgressBar`'s opt-out adds new baselines
(one-time Argos approval, not flakiness).
2026-06-10 17:00:21 +02:00
Thomas Trompette
2514cab860 fix(server): include relation join column names in updatedFields of update events (#21405)
## Context

Since #21052, update-event diffs are keyed by the relation field name
(e.g. `company`) instead of the join column name (e.g. `companyId`).
`updatedFields` is derived from the diff keys, so any **workflow
database-event trigger** (or webhook) configured with a field filter on
a relation join column **silently stopped firing** — no run is created
at all.

We hit this in production: a `cloudWorkspace.updated` trigger filtered
on `twentyContactId` stopped creating runs the same day #21052 was
deployed. Updating the record's relation produced `updatedFields:
["twentyContact"]`, which no longer matches the stored settings `fields:
["twentyContactId"]` in
`WorkflowDatabaseEventTriggerListener.shouldTriggerJob`.

## Solution

Keep the diff keyed by relation field name (the timeline rendering from
#21052 relies on it — adding both keys to the diff would display
relation changes twice), but expose **both** the relation field name and
its join column name in `updatedFields`:

- New `computeUpdatedFieldsFromDiff()` in
`object-record-changed-values.ts`: expands MANY_TO_ONE relation diff
keys with their join column name.
- Used in `formatTwentyOrmEventToDatabaseBatchEvent` for
UPDATED/DELETED/RESTORED and UPSERTED events instead of
`Object.keys(diff)`.

This restores matching for pre-existing trigger/webhook configurations
(join column names) while keeping configurations using relation field
names working.

## Test plan

- [x] Unit tests: relation diff keyed by relation name; `updatedFields`
contains both `company` and `companyId`
- [x] End-to-end util test on UPDATED event: `updatedFields: ['company',
'companyId']`, diff keyed by `company`
- [x] Downstream consumer specs pass (workflow trigger listener,
webhooks, subscriptions, logic-function triggers)
- [x] Verified against the production workspace that a `twentyContactId`
update currently produces no workflow run with the old behavior
2026-06-10 14:48:24 +00:00
Marie
6971d6fa95 Prevent self-hosting app from re-matching/re-creating people on no-op updates (#21406)
The match-telemetry-event-with-people logic function triggers on
selfHostingUser.* (both created and updated) and unconditionally wrote
personId back to the record on every run. Since the handler's own write
produces an updated event — and the telemetry webhook also updates
unrelated fields (name, serverUrl, etc.) on returning signups — the
matching logic re-ran unnecessarily, querying people and writing on each
pass.

This adds an early-return guard so the handler only does work when
there's actually something to match:

Skip when the selfHostingUser is already linked (personId set) and the
primary email hasn't changed.
Still (re)match on first link and on genuine email changes — the
legitimate reasons for listening on updated.
The guard reads before.email.primaryEmail via an 'before' in properties
narrowing so it stays type-safe across the create/update event union.
2026-06-10 14:46:50 +00:00
Charles Bochet
7258722754 security: upgrade @nestjs/graphql 12→13 + @ptc-org/nestjs-query 4→9 (+ @nestjs/config 4) (#21402)
## What

Upgrades the NestJS GraphQL stack to clear the High **`ws`** alert
(GHSA-3h5v-q93c-6h6q) and modernize off two heavily-patched majors.
`@nestjs/graphql@13` pulls `ws@8.20.1` (was 8.16.0).

This had to be a **coordinated** upgrade: `@ptc-org/nestjs-query@4.2.0`
doesn't support `@nestjs/graphql@13`, so all three move together.

| Package | From → To |
|---|---|
| `@nestjs/config` | 3.3.0 → ^4.0.4 |
| `@nestjs/graphql` | 12.1.1 → ^13.4.2 |
| `@ptc-org/nestjs-query-{core,graphql,typeorm}` | 4.x → ^9.4.0 |

## The tricky bits

- **Re-ported the custom `@nestjs/graphql` patch onto v13.** v13 rewrote
the schema builder and added its *own* native multi-schema support
(`includeModules`, native `clear()`). Twenty's patch
(`resolverSchemaScope` + `computeReachableTypes` — the
core/metadata/admin split) is re-merged into v13's new
`generate(options, includeModules, reachableTypes)` flow, with a
link-preserving `storage.clear()` so cross-schema `resolveType` closures
keep working.
- **Re-ported the `@ptc-org` patch onto 9.4.0**: removes the
`@shareable` federation directive from built-in connection/response
types, **and** adds a `.js` extension to its extensionless deep import
of `@nestjs/graphql` internals — which v13's new `"exports"` map
otherwise rejects at runtime (this was the boot blocker).
- **`AppTokenService`**: nestjs-query 9 requires custom services to
inject their repo and `super(repo)` it (added an `@InjectRepository`
constructor).
- **`gridPosition` input fields**: dropped the `deprecationReason` (a
*required* input field can't be `@deprecated` under the upgraded
graphql) — fields keep their original nullability, so the **schema is
unchanged**.
- **Service specs**: nestjs-query 9's `TypeOrmQueryService` reads the
repo's driver/metadata at construction, so the mocked repos now include
`manager`/`metadata`.

## Verification

- `nx typecheck twenty-server`: **0 errors**; lint clean
- Server boots; **all 3 GraphQL schemas** (`/graphql`, `/metadata`,
`/admin-panel`) generate and respond `200`
- `graphql:generate` for all 3 schemas is **byte-identical** to before
the upgrade (the reachable-types re-port is faithful)
- **108 service unit tests pass** (incl. all 6 `TypeOrmQueryService`
services)
- `ws@8.16.0` gone (now 8.17.1 + 8.18.0); `yarn install --immutable`
clean

## Note on lodash
`lodash@4.17.21` still remains via `zapier-platform-core` (runtime) and
`@stoplight/spectral`, so the lodash alert is **reduced but not fully
cleared** by this PR — it needs those separate sources addressed (or a
resolution).
2026-06-10 15:55:15 +02:00
Brendan Erofeev
f1c7aecadb fix(front): sanitize optimistic input when creating a record (#21076)
## Summary

Closes #15800.

Clicking **+ Add New** from a relation cell to create a **Task** or
**Note** (e.g. from a custom object's Tasks/Notes section in the list
view) throws:

```
Uncaught (in promise) Error: Should never occur, encountered unknown fields name in objectMetadataItem task
```

### Root cause

`useCreateOneRecord` computes a **sanitized** input (with
`sanitizeRecordInput`, which strips fields that don't belong to the
object) and sends it to the GraphQL mutation. But it still feeds the
**raw** input to the optimistic cache computation:

```ts
const sanitizedInput = { ...sanitizeRecordInput({ objectMetadataItem, recordInput }), id: idForCreation };

const optimisticRecordInput = computeOptimisticRecordFromInput({
  ...
  recordInput: {
    ...computeOptimisticCreateRecordBaseRecordInput(objectMetadataItem),
    ...recordInput, // ← raw input, may contain fields unknown to the object
    id: idForCreation,
  },
  ...
});
// mutation uses the sanitized input:
mutate({ variables: { input: sanitizedInput } });
```

`computeOptimisticRecordFromInput` asserts that every input key maps to
a field on the object and `throw`s otherwise. So when the create input
carries a field the target object doesn't have (the relation-create path
passes a `name`, but Task/Note use `title`), the optimistic step throws
before the mutation ever runs.

`useCreateManyRecords` does **not** have this problem — it already feeds
the sanitized input to `computeOptimisticRecordFromInput`.

### Fix

Feed the sanitized input to the optimistic computation in
`useCreateOneRecord`, exactly as `useCreateManyRecords` does:

```ts
recordInput: {
  ...computeOptimisticCreateRecordBaseRecordInput(objectMetadataItem),
  ...sanitizedInput,
},
```

This is safe and behavior-preserving for valid creates:
`computeOptimisticRecordFromInput` only ever reads *known* fields (it
iterates the object's field metadata); unknown input keys never
contribute to the optimistic record — they only trip the invariant.
Relations are resolved through their join columns, which sanitization
keeps.

## Test plan

- [x] `npx oxlint --type-aware` — passes on the changed files
- [x] `npx oxfmt --check` — passes
- [x] `tsc --noEmit` — no type errors in the changed files
- [x] `npx jest computeOptimisticRecordFromInput` — passes, including a
new case asserting that input which has been through
`sanitizeRecordInput` no longer trips the "Should never occur,
encountered unknown fields" invariant (the existing test already covers
the raw input throwing)
- [ ] Manual: from a custom object's Notes/Tasks relation, use **+ Add
New** to create a Note/Task — no error, the record is created

### Note on test scope

The crash only reproduces through the full relation-create flow with
live metadata; at the hook level in jsdom the create resolves
regardless, so a hook-level test would not guard the regression. The
added test instead locks the underlying mechanism the fix relies on —
that sanitized input is safe for `computeOptimisticRecordFromInput` —
alongside the existing test that proves raw unknown fields throw.

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
2026-06-10 12:22:59 +00:00
Charles Bochet
09cc0c6f21 security: clear yeoman-environment High alert via resolution to 6.0.1 (#21400)
## What

Clears the `yeoman-environment` High alert (GHSA-vv9j-gjw2-j8wp —
*arbitrary package install without confirmation*).

`yeoman-environment@4.4.3` is exact-pinned by `zapier-platform-cli`.
**There is no parent-bump fix**: `zapier-platform-cli@19.0.0` is the
latest stable and still pins 4.4.3 — cli 17→3.19.3, 18/19→4.4.3, all in
the vulnerable `< 6.0.1` range.

## Why a resolution (and why it's safe for us)

zapier-cli **lazy-loads** `yeoman-environment` via dynamic `import()`
*only* inside its `init` and `pull` scaffolding command handlers.
twenty-zapier only ever runs `validate` / `versions` / `push` /
`promote` (see `project.json`), so the vulnerable code path is never
imported here.

Since no zapier-cli release carries the fix, the only way to remove the
vulnerable version is a resolution:

```jsonc
"yeoman-environment": "6.0.1"
```

The 4→6 major only affects zapier-cli's `init`/`pull` (which we never
invoke); all our commands are unaffected.

## Verification
- `yeoman-environment` now **6.0.1** (no 4.4.3 in the lockfile)
- `nx build` + `typecheck` twenty-zapier 
- `zapier-platform validate` → **35/35 checks pass** with yeoman 6
forced
- `yarn install --immutable` clean
2026-06-10 13:35:28 +02:00
Charles Bochet
e53f877559 security: clear picomatch High alert via @angular-devkit/core resolution (#21398)
## What

Clears the root `picomatch` High alert (GHSA-c2c7-rcm5-vvqj, range
`>=4.0.0 <4.0.4`).

`picomatch@4.0.2` is exact-pinned by `@angular-devkit/core@19.2.x`,
pulled by NestJS's codegen tooling (`@nestjs/schematics` + `@nestjs/cli`
use Angular's schematics engine). angular-devkit/core backported the
picomatch 4.0.4 fix in **19.2.24**.

## Why a resolution here (not a parent bump)

The clean parent-bump — bumping `@nestjs/cli` so it pulls patched
angular-devkit — **breaks `nest build`**. `@nestjs/cli` 11.0.17+ has an
SWC-builder output-path change: it writes compiled files under
`dist/`**`src/`**`…` instead of `dist/…`, breaking every `node
dist/<path>` reference (`main`, `command`, worker,
`database/scripts/*`). This is what failed in the first revision of this
PR (`Cannot find module '…/dist/database/scripts/truncate-db.js'`).

- The cli's angular-devkit pin is **exact**, so there's no clean
refresh.
- Every `@nestjs/cli` ≥11.0.17 (incl. the latest 11.0.23) has the
regression.
- There's no stable NestJS 12/13 to move to (12 is alpha-only).
- `tsconfig` `rootDir` doesn't override the output base.

So a one-patch resolution is genuinely the cleaner, lower-risk fix:

```jsonc
"@angular-devkit/core": "19.2.24"   // patch within the same 19.2 minor
```

`@nestjs/cli` stays 11.0.16 (correct `dist/` layout), and picomatch
resolves to 4.0.4.

## Verification
- picomatch now **4.0.4 + 2.3.2** (both patched); no 4.0.2 in the
lockfile
- `nx build twenty-server` emits `dist/main.js` and
`dist/database/scripts/*.js` at the correct paths (the prior CI failure)
- `yarn install --immutable` clean
2026-06-10 13:22:05 +02:00
Charles Bochet
402e4b6a78 fix(docker): upgrade Alpine system OpenSSL libs to patched 3.5.7-r0 (#21399)
## Context

Image scanner flags the `prod-twenty` server image for OpenSSL CVEs. The
image carries **two independent OpenSSL surfaces**, and they are fixed
by different levers:

| Surface | Version (before) | Used by app at runtime? | Fix lever |
|---|---|---|---|
| **Node-bundled** OpenSSL | `3.5.6` |  yes (Node links its own) | Node
base bump (separate; `24.16.0-alpine3.23` is already the latest digest)
|
| **Alpine system** `libcrypto3`/`libssl3` | `3.5.6-r0` |  no (`ldd
node` shows no link) | **this PR** — apk upgrade to `3.5.7-r0` |

The pinned `node:24.16.0-alpine3.23` base bakes `libcrypto3`/`libssl3`
at `3.5.6-r0`, while the Alpine v3.23 repo now ships `3.5.7-r0`. These
libs ship as deps of `apk-tools`/`libapk`/`ssl_client` (so they can't be
removed), and Node does not link them — but the scanner still flags them
by version.

## Change

Pin a patched floor (`libcrypto3>=3.5.7-r0`, `libssl3>=3.5.7-r0`) in the
`apk add` lines of both runtime stages (`twenty-server` and
`twenty-app-dev`) so apk upgrades them at build time. Same technique
already used for `curl`/`nghttp2-libs`/`postgresql18-client` (#20805).

## Verification

- Built `--target twenty-server`; image builds clean.
- On the built image, `libcrypto3` and `libssl3` resolve to `3.5.7-r0`.
- Node-bundled OpenSSL is unaffected by this change (separate surface,
no Dockerfile-side upgrade path until a newer Node release links a
patched OpenSSL).
2026-06-10 11:02:33 +00:00
Charles Bochet
1c3ae92c04 fix(server): prevent SSE stream teardown errors from crashing all pods (#21395)
## Context

In prod-eu, **all `twenty-server` API pods crash simultaneously**
several times per hour (then restart in lockstep) since ~Jun 2. Each
crash is an **unhandled promise rejection** in SSE event-stream teardown
— `exitCode=1`, identical stack on every one of the 7 pods:

```
Error: Failed to acquire lock for key: workspace:<id>:activeStreams
  at CacheLockService.withLock (cache-lock.service.ts:53)
  at async EventStreamService.destroyEventStream (event-stream.service.ts:75)
  at async cleanup (wrap-async-iterator-with-lifecycle.ts)
  at async Object.return (wrap-async-iterator-with-lifecycle.ts)
  at async Object.cancel (graphql-yoga/.../result-processor/sse.js:68)
```

### Mechanism

1. A busy workspace's SSE clients reconnect (no client backoff), so
connect/disconnect contend on a **single per-workspace Redis lock**
`workspace:<id>:activeStreams`.
2. Under contention `CacheLockService.withLock` exhausts its retries and
**throws**.
3. In the `destroyEventStream` teardown path the throw escapes
`wrapAsyncIteratorWithLifecycle`'s `cleanup()` — `return()` does `try {
await cleanup() } finally { … }` and does **not** catch a cleanup throw.
4. graphql-yoga invokes this from `cancel()` as a **fire-and-forget**
`Promise.all` on connection abort. With **no global `unhandledRejection`
handler**, Node's default policy terminates the process with **exit code
1**.
5. The crash drops all that pod's SSE clients → they reconnect to
surviving pods → contention moves there → the whole fleet crashes
together → restarts → reconnect storm → repeats (~14 min period,
matching the metrics).

## Changes

Crash-stopping hotfix (defense in depth). Does **not** change the
locking design or client reconnect behavior — see follow-ups.

- **`wrapAsyncIteratorWithLifecycle`**: `onCleanup()` is now best-effort
— wrapped in try/catch so teardown can never reject out of
`next()`/`return()`/`throw()`. The original iterator error is still
rethrown unchanged. Adds an `onCleanupError` hook so the swallowed error
is still reported.
- **`EventStreamResolver`**: wires `onCleanupError` to
`ExceptionHandlerService.captureExceptions` (→ Sentry) with workspace +
channel context, so these failures stay visible.
- **`main.ts`**: registers a global `process.on('unhandledRejection')`
that reports via `ExceptionHandlerService` (Sentry) instead of letting
Node terminate. Registering the listener also suppresses Node's default
process-termination. Non-`Error` reasons are formatted with
`util.inspect` (per Copilot review) so Sentry gets a readable message
rather than `[object Object]`.

## Verification

The fix was checked against the exact crash path — a wrapped iterator
whose `onCleanup` rejects:

- `return()` (graphql-yoga's `cancel()` path) **resolves** instead of
rejecting → no unhandled rejection.
- `next()` on a completed stream **resolves** despite a rejecting
cleanup.
- A genuine iterator error still surfaces on `next()` (cleanup failure
doesn't mask it).
- `onCleanupError` receives the original `Error`.

All four pass. `nx lint twenty-server` + `oxfmt` clean; no new `tsc`
errors in the changed files.

## Follow-ups (not in this PR)

- Remove the unnecessary `withLock` around the already-atomic Redis
`SADD`/`SREM` in `event-stream.service.ts` (the contention source).
- Restore exponential backoff + jitter on the frontend SSE reconnect
(regressed in #21061) to stop the thundering herd.
- Infra: make `/healthz` a meaningful liveness signal and add
`maxUnavailable` + a PodDisruptionBudget so pods can't all die together.
2026-06-10 10:05:55 +00:00
Clive F
3655942fa4 fix: reload stale clients on any older app version, not just major (#21011)
## Context

The GraphQL error handler emits an `APP_VERSION_MISMATCH` error
(surfaced on the client as a "your app version is out of date, please
refresh" prompt) when a client's app version is behind the server's.
Today that comparison only fires when the **major** version differs:

```ts
const frontEndMajor = semver.parse(frontEndAppVersion)?.major;
const backendMajor = semver.parse(backendAppVersion)?.major;
if (isDefined(frontEndMajor) && isDefined(backendMajor) && frontEndMajor < backendMajor) { ... }
```

Twenty ships schema changes in minor and patch releases too. A tab left
open across a minor/patch deploy keeps sending GraphQL documents built
against the previous schema. If a field was removed or renamed, those
operations fail with opaque field-level errors and the user never gets
the refresh prompt — a hard reload is the only recovery.

## What this does

- **Server:** fires the mismatch whenever the client version is strictly
older than the server (`semver.lt`), regardless of which version
component changed.
- **Client:** `onAppVersionMismatch` now reloads the page once (in
addition to the existing snackbar) to pull the current `index.html` and
hashed assets, guarded by a short `sessionStorage` window so a
still-stale reload can't cause a refresh loop.

## Notes

- No behavior change for clients on the same or newer version.
- Relies on the existing `x-app-version` header and `APP_VERSION` config
that already drive this check.

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
2026-06-10 10:01:12 +00:00
Charles Bochet
abbb1bdf98 security: clear serialize-javascript High alert (terser-webpack-plugin refresh) (#21397)
## What

Clears the High `serialize-javascript` alert (GHSA-5c6j-r48x-rmvq) — a
plain in-range refresh, **no resolution**, only `yarn.lock` (net −22
lines).

`serialize-javascript@6.0.2` was pulled only by
**`terser-webpack-plugin@5.3.16`** (webpack's minifier). The patched
line (7.x) is outside terser's old `^6.0.2` range — but
**`terser-webpack-plugin@5.6.1` dropped the `serialize-javascript`
dependency entirely**, and webpack already requests
`terser-webpack-plugin@^5.3.16` (which allows 5.6.1).

So `yarn up -R terser-webpack-plugin` → 5.6.1 removes the vulnerable
6.0.2; only the patched **7.0.5** (twenty-website's direct dep) remains.

## Verification
- No `serialize-javascript@6.x` left in the lockfile
- `nx build twenty-server` (exercises the `@nestjs/cli` webpack/terser
path) 
- `yarn install --immutable` clean
2026-06-10 11:52:26 +02:00
Charles Bochet
3ee82c7a3c security: clear koa High alert by bumping nx 22.5.4 → 22.7.5 (#21396)
## What

Clears the High `koa` alert (GHSA-7gcc-r8m5-44qm) — via a parent bump,
**no resolution**.

`koa@3.0.3` was a deep transitive of the module-federation build
tooling:
`koa` ← `@module-federation/dts-plugin@0.21.4` ←
`@module-federation/enhanced@0.21.4`, pulled via **two** paths —
`@nx/module-federation@22.5.4` (directly) and `@module-federation/node`
(pinning the old `enhanced@0.21.x` line).

`@module-federation/dts-plugin` 2.x dropped koa, so the fix moves both
paths onto the 2.x line:

- bump `nx` + `@nx/{jest,js,react,storybook,vite,web}` **22.5.4 →
22.7.5** (minor): `@nx/module-federation@22.7.5` uses
`@module-federation/enhanced@^2.3.3`
- refresh `@module-federation/node` within its existing `^2.7.21` range
→ 2.7.44, which uses `@module-federation/enhanced@2.5.1`

Result: `@module-federation/enhanced` + `dts-plugin` collapse to 2.5.1,
and **koa is gone from the tree entirely** (net −309 lockfile lines
after dedup).

## Verification (nx is the monorepo task runner — verified broadly)
- `nx build` + `typecheck`: twenty-shared, twenty-ui, twenty-front 
- `nx typecheck twenty-server` 
- `nx test twenty-shared` 
- `yarn install --immutable` clean

The only `package.json` change is the nx version bump; everything else
is lockfile.
2026-06-10 11:44:03 +02:00