Commit Graph

3615 Commits

Author SHA1 Message Date
Etienne 0459f25dec Files v2 - Add new workspace field file upload resolver (#17325) 2026-01-22 13:15:52 +00:00
github-actions[bot] 96bdae4582 i18n - translations (#17339)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2026-01-22 14:09:39 +01:00
Abdul Rahman aff577689f Add syncable NavigationMenuItem entity to core schema (#17232)
Implements a new syncable `navigationMenuItem` entity in the core schema
to replace the workspace `favorite` entity.

## Next Steps
- Frontend integration ([separate
PR](https://github.com/twentyhq/twenty/pull/17268))
- Data migration (separate PR)
2026-01-22 12:48:51 +00:00
Félix Malfait 8dec37b826 feat: migrate typecheck to tsgo for faster type checking (#17331)
## Summary
- Switch `twenty-server` and `fireflies` typecheck from tsc to tsgo
(~75x faster)
- Enable tsgo in VSCode via `typescript.experimental.useTsgo` setting
- Add `@ts-nocheck` to `remove-step.spec.ts` to work around tsgo
performance issue with deep spread operations

## Performance
| Package | Before (tsc) | After (tsgo) |
|---------|-------------|--------------|
| `twenty-server` | ~150s | ~2s |
| `twenty-front` | ~40s | ~2s |

## Related
- Workaround for: https://github.com/microsoft/typescript-go/issues/2551

## Test plan
- [x] `npx nx typecheck twenty-server` passes
- [x] `npx nx typecheck twenty-front` passes  
- [x] `npx nx run-many --target=typecheck --exclude=fireflies` passes
(fireflies has pre-existing type errors)
2026-01-22 13:39:07 +01:00
martmull d833914888 Disable query logs if I want to (#17329)
As tile
2026-01-22 13:38:45 +01:00
neo773 a1c112a9b9 Fix null user crash in admin panel user lookup (#17336) 2026-01-22 13:35:30 +01:00
Weiko 4c565425c5 Fix workspace resolver when billing is not enabled (#17333) 2026-01-22 13:33:29 +01:00
martmull f92c8a98a4 2114 extensibility manage serverless execution from built code (#17317)
Summary

- Serverless functions are now built when created/updated instead of at
execution time
  - Added builtHandlerPath field to track pre-built function artifacts
- Renamed base-typescript-project to seed-project with pre-built ESM
output included
- Renamed file folder enums for consistency (Functions → BuiltFunction,
SourceCode → Source)
  - supports backwards compatibility with existing serverless functions

Tested with all combinations
- storage : local driver and S3 driver
- serverless : local driver and lambda driver
2026-01-22 12:56:44 +01:00
Weiko 9ee2cd0fe3 Enable RLS in lab (#17328) 2026-01-22 12:27:11 +01:00
neo773 1ffd23764c Fix Gmail sync edge case with multi-label emails (#17318)
Gmail emails often have multiple labels - an email in "XYZ" label
typically also has "INBOX". The previous negative filter approach
(-label:inbox -label:sent...) excluded these emails entirely, even when
they had a selected label.

Switched to positive OR filtering: (label:cyz OR label:xyz-visible)
-label:spam... which correctly fetches emails with any of the selected
labels regardless of other labels they have.

This edge case wasn't caught earlier because manual testing with INBOX
selected worked fine - it only surfaced when selecting custom labels
without INBOX.
2026-01-22 12:00:45 +01:00
Weiko 670ff1583e Fix RLS entitlement check + fix role page with RLS on object without object-permission not being displayed (#17326) 2026-01-22 11:48:24 +01:00
Paul Rastoin 6aa43b68f7 Identification cleanup (#17301)
# Introduction

following https://github.com/twentyhq/twenty/pull/17279
As we've finally identified all the syncable metadata entities, which
means they're expected to have non nullable applicationId and
universalIdentifier at pg_level we can remove previous retro comp
universalIdentifier fallbacking and update the dto too

~~This needs IdentifyRemainingEntitiesMetadataCommand and
MakeRemainingEntitiesUniversalIdentifierAndApplicationIdNotNullableMigrationCommand
to be run~~
```ts
[Nest] 197  - 01/21/2026, 3:08:35 PM     LOG [MakeRemainingEntitiesUniversalIdentifierAndApplicationIdNotNullableMigrationCommand] Successfully run MakeRemainingEntitiesUniversalIdentifierAndApplicationIdNotNullableMigrationCommand
```
2026-01-21 16:00:23 +00:00
Paul Rastoin 05f0d572e9 Refactor runner delete action to be workspace agnostic (#17276)
# Introduction

Straight to the point refactor passing only universalIdentifier in
workspace migration delete actions instead of id so it can be run on any
workspace

Related to
https://github.com/orgs/twentyhq/projects/1/views/8?pane=issue&itemId=132343284&issue=twentyhq%7Ccore-team-issues%7C1641
2026-01-21 15:57:52 +00:00
Thomas Trompette 2920dec6ba Add logs to debug workflow crons (#17302)
We have 3 crons that get a lot of timeout in sentry. 
Adding logs to help debugging.
2026-01-21 15:49:45 +00:00
nitin 26a70c23e9 [Dashboards] fix line chart duplicate widget bleed by hashing series IDs (#17303)
closes last issue in the bug bucket -
https://github.com/twentyhq/core-team-issues/issues/2109

<img width="695" height="41" alt="CleanShot 2026-01-21 at 19 40 38"
src="https://github.com/user-attachments/assets/c6872e11-1e36-4b4e-ab06-9943c22cfab2"
/>


 ### Root cause - 
line chart series have stable IDs, so Apollo normalizes them across
duplicated widgets and X‑axis edits leak
 
 ### Fix - 
prefix series IDs with a deterministic hash of objectMetadataId +
configuration
 
 before - 
 


https://github.com/user-attachments/assets/538eded8-7036-415a-b707-a10007c3aa11


 


after - 


https://github.com/user-attachments/assets/cf9ca89a-e7c9-4106-b6cd-fbc072d9a233
2026-01-21 15:39:29 +00:00
Paul Rastoin 54a9a1087b Fix identify remaining entities command to cover soft-deleted rows (#17304)
# Introduction
Some entities aren't passing the migration due to not covering the soft
deleted rows

## Entities that implements soft deletion
Inserted with universal programmatically
- rowLevelPermissionPredicate
- rowLevelPermissionPredicateGroup
- pageLayout
- pageLayoutTab
- pageLayoutWidget

Legacy might contains null
- viewFilterGroup
- serverlessFunction
- viewSort
2026-01-21 15:36:26 +01:00
neo773 2ecc1ba897 fix gmail message import policy (#17272)
Gmail sync was importing messages from folders users had explicitly
disabled because the folder filtering logic lived in the parent service
but the Gmail driver wasn't aware of the import policy when building its
API queries.
Moved messageFolderImportPolicy down to the driver level so Gmail can
- Build proper -label: exclusions during initial sync
- Added `buildGmailLabelSearchName` as our syntax for nested folders was
wrong
- Filter out messages from disabled folders during incremental sync via
history API
2026-01-21 12:49:58 +00:00
Paul Rastoin 596b7cc62d Deprecate nullable syncableEntity (#17279)
# Introduction
As we've been identifying both standard and custom entities for all the
metadata that had standard
We now still need to identify all custom entities enforcing them to have
an `applicationId` and `universalIdentifier`

In this PR we've removed the `SyncableEntityRequired` in favor requiring
props directly in the `SyncableEntity`
Which means that all metadata in db will now expect non nullable
applicationId and universalIdentifier across the whole application

Will add some type cleanup later in
https://github.com/twentyhq/twenty/pull/17277
2026-01-21 10:23:55 +00:00
github-actions[bot] 6b97e3b428 i18n - translations (#17296)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2026-01-20 22:21:02 +01:00
Félix Malfait 3ed67b825e feat: implement generic many-to-many junction relation support (#16820)
## Overview

This PR implements **generic many-to-many relation support** through
junction tables (also known as associative entities or join tables).
This replaces the need for hardcoded taskTarget/noteTarget logic and
provides a flexible foundation for modeling complex entity
relationships.

## Architecture

### Data Model

Many-to-many relationships are implemented using a **junction object
pattern**:

```
┌─────────┐       ┌──────────────────┐       ┌─────────┐
│  Pet    │──────>│   PetRocket      │<──────│ Rocket  │
│         │ 1:N   │  (junction)      │  N:1  │         │
│ rockets ├───────┤ pet    : Pet     ├───────┤         │
└─────────┘       │ rocket : Rocket  │       └─────────┘
                  └──────────────────┘
```

The junction object (PetRocket) has:
- A `MANY_TO_ONE` relation to **Pet** (the source)
- A `MANY_TO_ONE` relation to **Rocket** (the target)

The source object (Pet) has a `ONE_TO_MANY` relation pointing to the
junction, with **field settings** that specify which target field to
follow.

### Field Settings Schema

Junction configuration is stored in `FieldMetadataRelationSettings`:

```typescript
{
  relationType: "ONE_TO_MANY",
  // Points to the target field on the junction object
  junctionTargetFieldId?: string;      // For regular relations
  junctionTargetMorphId?: string;      // For polymorphic relations
}
```

**Two configuration modes:**
1. **`junctionTargetFieldId`** - References a specific `RELATION` field
on the junction
2. **`junctionTargetMorphId`** - References a `morphId` group for
polymorphic targets (e.g., link to Person OR Company)

### GraphQL Query Generation

When a junction relation is detected, the GraphQL fields are generated
to fetch the nested target:

```graphql
query GetPetWithRockets {
  pet(id: "...") {
    rockets {           # ONE_TO_MANY to junction
      id
      rocket {          # Target field on junction
        id
        name
        __typename
      }
    }
  }
}
```

For polymorphic junction targets:
```graphql
caretakerPerson { id, name }
caretakerCompany { id, name }
```

## Frontend Architecture

### Display Flow

1. **Detection**: `hasJunctionConfig()` checks if field has junction
settings
2. **Config Resolution**: `getJunctionConfig()` resolves junction object
metadata and target fields
3. **Record Extraction**: `extractTargetRecordsFromJunction()` extracts
target records from junction records
4. **Rendering**: Target records displayed as chips (not junction
records)

### Edit Flow

1. **Picker Opening**: Initializes the multi-record picker with:
   - Searchable object types (derived from junction target fields)
   - Pre-selected items (extracted from existing junction records)
   
2. **Selection Handling**: Manages create/delete of junction records:
   - **Select**: Creates new junction record with source + target IDs
   - **Deselect**: Finds and deletes the junction record
- **Optimistic Updates**: Manually updates Recoil store before API call

### Key Trade-offs

| Decision | Trade-off |
|----------|-----------|
| Junction records managed manually | More control over optimistic
updates, but requires manual cache management |
| Settings stored per-field | Flexible (same junction can power
different views), but requires UI to configure |
| Polymorphic via morphId groups | Supports N target types, but adds
query complexity |
| Feature flag gated | Safe rollout, but requires flag management |

## Backend Changes

- **Validation**: Junction target field must exist and be a valid
`MANY_TO_ONE` relation
- **Settings**: Extended `FieldMetadataRelationSettings` type with
junction fields
- **Dev Seeder**: Added sample junction objects (PetRocket,
EmploymentHistory, PetCareAgreement) for testing

## How to Test

1. Enable the `IS_JUNCTION_RELATIONS_ENABLED` feature flag
2. Create objects with junction pattern (Pet → PetRocket → Rocket)
3. Configure the junction target in field settings (advanced mode)
4. Verify:
- Display shows target objects (Rockets), not junction records
(PetRockets)
   - Picker allows selecting/deselecting targets
   - Changes persist correctly



https://github.com/user-attachments/assets/d04f057a-228c-4de8-af48-76bb2d72cac1

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
2026-01-20 21:58:13 +01:00
github-actions[bot] b1bd749abd i18n - translations (#17295)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2026-01-20 20:07:22 +01:00
Paul Rastoin e35b4b86cb Migrate serverless function service to v2 (#17285)
# Introduction

In this PR we're migrating the serverless function service that was
using the SF repo directly to the v2 build and runner.
The whole serverless engine now deals with flat entities only

## Resolvers
Refactored the resolvers ( serverlessFunction, route, database and cron
trigger) :
- return types to `dto`
- Standardized the flat to dto transpilation within the resolvers
- Find and findMany passing by the cached data

## Services
Refactored the services ( serverlessFunction, route, database and cron
trigger) :
- return type to be `flat`
- always calling v2 and computing cache

## New additional caches
- application variables ( cf
https://github.com/twentyhq/core-team-issues/issues/2116 )
- serverless function layer

## What to test:
- CRUD ( database trigger  , route trigger, cron trigger, serverless
function through workflows  )
- Duplicating a workflow with a serverless function code node  

## Concerns
We need to implement the cron that will hard delete soft deleted s3
serverless functions, not in this PR though ( cf
https://github.com/twentyhq/twenty/pull/17285#discussion_r2709168570 and
https://github.com/twentyhq/core-team-issues/issues/2118 )
2026-01-20 18:32:22 +00:00
github-actions[bot] ccf2eae684 i18n - translations (#17294)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2026-01-20 18:40:35 +01:00
Etienne 30c13fc947 Files v2 - Add new FILES field type (#17236)
In this PR : 
- New field type FieldMetadataType.FILES added (as jsonb in DB)
- Backend: GraphQL types, validation, REST API schema, data processor
handling
- Frontend: field configuration in settings
- Feature flag IS_FILES_FIELD_ENABLED to gate the feature
- Integration tests for create/filter validation
- Dev seed: Feature flag enabled + FILES field on survey result object


To do in next PRs : 
- Backend : 
-- new workspaceFile controller (for download) & new workspaceFile
upload resolver (for upload)
-- pre-hook/post-hook to ensure fileId existence + listener to ensure
cleaning
- Frontend : FILES field display/edit in table view, record, ...
2026-01-20 17:03:49 +00:00
github-actions[bot] 41329a0ea9 i18n - translations (#17292)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2026-01-20 18:09:44 +01:00
Thomas Trompette 799b08e1f2 Stop workflow in any status (#17271)
Workflows can now be cancelled when not started or enqueued.
Also displaying the stop option when selecting all.

We got the case of a client that did an infinite loop. So he had a lot
of workflows to stop. Supporting select all would have avoid him to wait
for 4 hours so we process the runs throttled. This is not something that
is supposed to happen often so I did not see the point of refactoring
the endpoint. But I wanted the users to have this option just in case
2026-01-20 16:38:39 +00:00
github-actions[bot] e40130f575 i18n - translations (#17290)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2026-01-20 17:42:30 +01:00
Angel98518 9cf88df67b Improve streamToBuffer error handling and memory safety (#17255)
## Description

This PR improves error handling and memory safety for the streamToBuffer
utility function.

### Changes
- Add proper cleanup of event listeners to prevent memory leaks
- Add checks for already-ended streams to handle edge cases gracefully
- Add protection against multiple resolve/reject calls with isResolved
flag
- Add handling for close events with appropriate error messages
- Improve robustness when streams are destroyed or closed externally

### Impact
This fixes potential memory leaks and race conditions when streams are
cancelled, destroyed, or closed before completion.

### Code Statistics
- 1 file changed
- ~53 lines added, 9 lines modified
- ~95% code changes (functional improvements)

---------

Co-authored-by: GitTensor Miner <miner@gittensor.io>
Co-authored-by: Etienne <45695613+etiennejouan@users.noreply.github.com>
2026-01-20 16:19:47 +00:00
Weiko b6635ba272 Add RLS Entitlement check (#17179)
## Context
- Add RLS entitlement to billing
- Check value in the backend (for RLS predicate entity
queries/mutations)
- Expose billingEntitlements to the API inside currentWorkspace to check
available features to the workspace and display the role components
accordingly
- Cleanup RLS when plan changes back to one without RLS.

This should cover almost everything, imho we don't need to check in the
ORM because => We can't create RLS without the correct PLAN and
switching back to a PLAN without RLS deletes existing RLS through
stripes webhooks
2026-01-20 16:06:37 +00:00
Thomas Trompette e599d6c4d0 Handle dynamic predicates (#17287)
Fetch and send full workspace member to handle dynamic predicate. I only
tested that it did not break the current logic since the frontend does
not yet send queries we dynamic predicates.
2026-01-20 14:57:36 +00:00
martmull 02a181a49f Add endpoint to upload application file (#17270)
As title
2026-01-20 14:10:34 +00:00
martmull 801878cb2b 2091 extensibility twenty sdk add command twenty app function logs and twenty app function test (#17278)
add `function:logs` and `function:execute` cli commands
2026-01-20 14:10:01 +00:00
nitin a004662221 [Dashboards] align cumulative range filtering with raw-value filters (#17265)
closes https://github.com/twentyhq/core-team-issues/issues/2097

- Align cumulative range filtering with non‑cumulative behavior by
applying rangeMin/rangeMax to raw values before cumulative totals are
computed.
- Remove cumulative‑value filtering to prevent hidden points from
influencing visible totals.
2026-01-20 12:15:24 +00:00
Etienne 472fe4eec3 Fix dev env (#17269) 2026-01-20 09:57:20 +00:00
Weiko b64951634e fix rls create new record with composite (#17243)
## Context
buildValueFromFilter was not handling composite filters which was needed
for RLS. This PR implements that and fix some issues with RLS

Tested with a few composite + relation fields
<img width="559" height="206" alt="Screenshot 2026-01-19 at 15 38 42"
src="https://github.com/user-attachments/assets/d64afa6e-3e12-4843-a215-a693665112c5"
/>
2026-01-20 05:36:38 +00:00
neo773 527c00d326 refresh oAuth tokens when sending email (#17250)
calls `connectedAccountRefreshTokensService` in `SendEmailTool`
resolving error `Lifetime validation failed, the token is expired.`
2026-01-19 17:54:07 +00:00
github-actions[bot] dc982abb78 i18n - translations (#17258)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2026-01-19 19:01:11 +01:00
github-actions[bot] 042b15a7d1 i18n - translations (#17257)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2026-01-19 18:49:13 +01:00
Raphaël Bosi d0bc8b9ffe [DASHBOARDS] Move all the graph computing logic to the backend (#17189)
- Create resolvers for each type of charts which needs data
transformation after the group by operation: Bar Chart, Line Chart and
Pie Chart
- Move all the utils to the backend and refactored some into services

This allows all the computation to be done in the backend, improving
performances in the frontend.
2026-01-19 17:25:45 +00:00
Weiko 1f1ccb281e Cache WorkspaceMember (#17247)
## Context
Due to RLS feature, workspaceMember entity and its properties is
becoming necessary in different places of the backend. To avoid querying
many times, this PR adds a map of workspace members per workspaceId,
leveraging WorkspaceCache pattern for WorkspaceEntity (in opposition
with CoreEntity)

Due to its content (mostly PII), I prefer to cache it locally only (node
process VS Redis) using localDataOnly.

TODO: invalidation logic when the workspaceMember object is updated
(more precisely, when its field map is updated). There is no easy way
today to update that list so it can be done in another PR imho
2026-01-19 17:13:10 +00:00
Thomas Trompette b15c042e77 SSE - Protect query api (#17241)
- Ensure user can only add/remove queries of its own stream
- Prevent stream from being highjacked
2026-01-19 17:12:41 +00:00
Paul Rastoin 28e98086b0 Identify index (#17239)
# Introduction
Related to https://github.com/twentyhq/core-team-issues/issues/1989

1/ Migration, applicationId and universalIdentifier are required on
entity ( save point migration + upgrade command fallback pattern )
2/ Backfill using previous standard ids

## Test
tested prod extract
2026-01-19 15:36:41 +00:00
github-actions[bot] e0d1edb940 i18n - translations (#17248)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2026-01-19 16:50:31 +01:00
Marie 1a38152451 Support referencing updatedFields array in databaseEvent triggers (#17240)
Closes https://github.com/twentyhq/core-team-issues/issues/1838

DatabaseEventTriggers can now define a field-level granularity on which
updates they should react to.
After a discussion with the team, we have decided to rely on field names
and not UID, just as we do for objects. The rationale is that we want to
keep a pleasant devX and consider it's not on twenty to ensure
continuity of api usage around an object if their name is updated: for
instance if a user has based their webhook on the name of an object, if
they decide to update it, they have to take care of updating their
webhook.
2026-01-19 15:33:49 +00:00
Etienne f381516d30 BILLING - Send dis-sync error to sentry (#17242)
Investigate Stripe activity when workspace not found, creating
[alert](https://twenty-v7.sentry.io/issues/alerts/rules/twenty-server/16611050/details/)
in Sentry
2026-01-19 15:31:37 +00:00
Paul Rastoin 59c7295033 Identify standard role (#17234)
# Introduction
Related to https://github.com/twentyhq/core-team-issues/issues/1989

1/ Migration, applicationId and universalIdentifier are required on
entity ( save point migration + upgrade command fallback pattern )
2/ Backfill using previous standard ids

## Test
tested prod extract

## Note
Added build to typeorm nx generate migration command so we never forgot
to build server before
2026-01-19 15:04:33 +00:00
Thomas Trompette 7cf0dc522a Add permissions to SSE (#17201)
Flow:
- fetch user role from context stored in cache - do not support api
context yet
- check object permissions
- filter restricted fields on event
- fetch role rls predicate and combine it with the query filter

Do not support dynamic predicates yet.
2026-01-19 13:39:12 +00:00
Paul Rastoin 859e718cf2 Identify view group (#17220)
# Introduction
Related to https://github.com/twentyhq/core-team-issues/issues/1989

1/ Migration, applicationId and universalIdentifier are required on
entity ( save point migration + upgrade command fallback pattern )
2/ Backfill using previous standard ids

## Test
tested prod extract
2026-01-19 12:56:52 +00:00
Vedant Madane 4ab95235ce [Breaking: DEPLOY SERVER BEFORE FRONT] fix: allow custom domain without Cloudflare API key (#17160)
## Summary

Fixes #17101

When self-hosting TwentyCRM, users can now set workspace custom domains
without requiring CLOUDFLARE_API_KEY to be configured. This enables
manual DNS configuration for those not using Cloudflare.

## Changes

- Added isCloudflareConfigured method to DnsManagerService
- Modified all Cloudflare-dependent methods to gracefully handle missing
configuration
- Added getManualDnsRecords helper that provides DNS configuration
instructions when Cloudflare is not available
- isHostnameWorking returns true in manual mode, allowing the domain to
be saved and enabled
- Added comprehensive tests for non-Cloudflare scenarios

## Behavior

When CLOUDFLARE_API_KEY is set: Works exactly as before with automatic
Cloudflare provisioning

When CLOUDFLARE_API_KEY is NOT set:
- Custom domain can be saved to the database
- User receives manual DNS configuration instructions
- Domain is marked as working, user is responsible for external DNS and
TLS configuration

## Testing

Added tests covering non-Cloudflare scenarios. Full test suite requires
Docker which was not run locally.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Introduces a Cloudflare integration feature flag and wires it through
server and front to gate the custom domain UI.
> 
> - Server: adds `isCloudflareIntegrationEnabled` to `ClientConfig`,
computed from `CLOUDFLARE_API_KEY` and `CLOUDFLARE_ZONE_ID` in
`client-config.service`; updates GraphQL schema and unit tests.
> - Frontend: adds `isCloudflareIntegrationEnabledState`, extends
`ClientConfig` type, sets the flag in `useClientConfig`, and
conditionally renders `SettingsCustomDomain` in `SettingsDomain` when
enabled.
> - Updates mocked client config to include the new flag.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
0c76dde03b1ae940a9dae65e8673c3ba4cfec9dc. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Signed-off-by: Vedant Madane <6527493+VedantMadane@users.noreply.github.com>
Co-authored-by: Félix Malfait <felix@twenty.com>
Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
2026-01-19 12:47:09 +01:00
Félix Malfait dc93cf4c59 feat: add TypeScript Go (tsgo) for faster type checking (#17211)
## Summary

- Add `@typescript/native-preview` (tsgo) for dramatically faster type
checking on frontend projects
- Configure tsgo as default for frontend projects (twenty-front,
twenty-ui, twenty-shared, etc.)
- Keep tsc for twenty-server (faster for NestJS decorator-heavy code)
- Fix type imports for tsgo compatibility (DOMPurify, AxiosInstance)
- Remove deprecated `baseUrl` from tsconfigs where safe

## Performance Results

| Project | tsgo | tsc -b | Speedup |
|---------|------|--------|---------|
| **twenty-front** | 1.4s | 60.7s | **43x faster** |
| **twenty-server** | 2m42s | 1m10s | tsc is faster (decorators) |

tsgo excels at modern React/JSX codebases but struggles with
decorator-heavy NestJS backends, so we use the optimal checker for each.

## Usage

```bash
# Default (tsgo for frontend, tsc for backend)
nx typecheck twenty-front
nx typecheck twenty-server

# Force tsc fallback if needed
nx typecheck twenty-front --configuration=tsc

# Force tsgo on backend (slower, not recommended)
nx typecheck twenty-server --configuration=tsgo
```

## Test plan

- [x] `nx typecheck twenty-front` passes with tsgo
- [x] `nx typecheck twenty-server` passes with tsc
- [x] `nx run-many -t typecheck --exclude=fireflies` passes
- [ ] CI tests pass
2026-01-19 12:46:34 +01:00