- add `forwardedRequestHeaders` `string[]` column in `core.routeTrigger`
- filter request headers and forward filtered headers to function
payload (avoid spreading unexpectedly token or cookie)
- add `forwardedRequestHeaders` option in twenty-sdk `defineFunction`
util
BREAKING for actual routeTrigger payload but only 16 to migrate in
production
## Context
Now that RLS predicates are applied, creating a record through the FE
(which is empty by default) is failing if your role has predicates and
your input does not respect them (which will always be true since, as
said above, input will be pretty much empty)
## Implementation
- Moved isMatching* filters to twenty-shared
- Implemented isMatchingRlsPredicates utils in the backend (ORM) to
check before insertion/update if the record is matching the current user
role Rls predicates, reusing the isMatching* filters utils moved to
twenty-shared
- Frontend now applies RLS predicates before creating a new record
(similarly to what we do with view filters)
Note:
It seems composite were not properly handled with view-filter insertion
logic, since I'm reusing the util for now, the issue remains for RLS and
will need to be addressed
This PR refactors object record operation dispatch through a browser
event instead of a state that was registering all operations.
This is a cleaner pattern as it is a synchronous event code path instead
of relying on a useEffect to watch state change, which is not ideal.
We introduce this change first to then rely on this new pattern to
dispatch SSE events in a following PR.
## Summary
Fixes a regression where the save button was not visible when creating a
new role.
## Root Cause
PR #17062 (RLS FE implementation) introduced a change to the `isDirty`
logic that added `isDefined(settingsPersistedRole)` as a condition:
```typescript
const isDirty =
isDefined(settingsPersistedRole) &&
!isDeeplyEqual(settingsDraftRole, settingsPersistedRole);
```
However, in create mode, `settingsPersistedRole` is intentionally set to
`undefined` (in `SettingsRoleCreateEffect.tsx`), causing `isDirty` to
always evaluate to `false` and hiding the save button.
## Fix
Added `isCreateMode` to the `isDirty` condition so the save button shows
when creating a new role:
```typescript
const isDirty =
isCreateMode ||
(isDefined(settingsPersistedRole) &&
!isDeeplyEqual(settingsDraftRole, settingsPersistedRole));
```
cc @Weiko
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> Restores save button visibility when creating a role.
>
> - Simplifies `isDirty` to `!isDeeplyEqual(settingsDraftRole,
settingsPersistedRole)`, removing the `isDefined(settingsPersistedRole)`
check so create-mode is considered dirty.
>
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
21593d54c3. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
---------
Co-authored-by: Weiko <corentin@twenty.com>
# Introduction
Instead of comparing existing to expected comparing expected to existing
Also improved logs and fallback use cases
# Test
Tested on a prod extract
Fixes https://github.com/twentyhq/twenty/issues/16775
When a field is deleted from the model, the workflow action step still
stores the deleted field name in `step.settings.input.objectRecord`.
We need to filter out the fields that are not valid anymore. This logic
existed before but had been removed with the migration to tool services.
# Introduction
In the https://github.com/twentyhq/core-team-issues/issues/1989 's
context we will apply migration through an upgrade command post entity
backfill to fit the applied constraint. But suspended soft deleted
workspace are not included in the upgrade workspace batches
In order to be able to pass the constraint in production, discussed with
@FelixMalfait, we will manually clear all the currently suspended and
soft deleted workspace ignore their grace period
## Force mode
When running the command in force it will ignore the limit per execution
( which really serve the cron job ) and the grace period
## Test
Tested on a production extract
# Introduction
Related 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 `standardId` or `isCustom`
## Test
Both tested on prod extract
Some view field are set as non custom is prod whereas they should for
several manually handle-able workspace amount
This PR fixes two critical bugs with the DATE type, following up the
recent refactor around dates and time zones :
https://github.com/twentyhq/twenty/pull/16544
There are a lot of related bugs in Sentry, this PR should fix all bugs
that are of the form :
`Cannot parse: 2026-02-04T00:00:00.000Z`
# `node-postgres` date type
The package `node-postgres`, used by TypeORM, returns by default a Date
object for the date OID type.
But we can change this behavior without patching anything.
See the documentation for `pg-types` :
https://github.com/brianc/node-pg-types
Our fix is to call `setTypeParser` from `pg` to have the postgres "date"
type returned as a string always.
```ts
export const setPgDateTypeParser = () => {
types.setTypeParser(PG_DATE_TYPE_OID, (val: string) => val);
};
```
Then in server's main.ts :
```ts
const bootstrap = async () => {
setPgDateTypeParser();
```
## Are we safe with this very low-level fix ?
Since TypeORM bypasses a string value returned by `pg` we are safe with
this modification at a very low-level.
```ts
else if (columnMetadata.type === "date") {
value = DateUtils.mixedDateToDateString(value)
}
```
https://github.com/typeorm/typeorm/blob/0ec4079cd3760dc49247a02c54415f16a2a51766/src/driver/postgres/PostgresDriver.ts#L838
```ts
/**
* Converts given value into date string in a "YYYY-MM-DD" format.
*/
static mixedDateToDateString(value: string | Date): string {
if (value instanceof Date) {
return (
this.formatZerolessValue(value.getFullYear(), 4) +
"-" +
this.formatZerolessValue(value.getMonth() + 1) +
"-" +
this.formatZerolessValue(value.getDate())
)
}
return value
}
```
https://github.com/typeorm/typeorm/blob/6f3788b83730463e3b787b2a98bb41695e13caf8/src/util/DateUtils.ts#L28
For reference the original type parser seems to be configured in
node-pg-types, on the 1182 OID, which is an array of date / 1082 OID,
this type parser calls another small library `postgres-date` which
creates a JS Date. I couldn't find a type parser explicitly on 1082 in
the stack TypeORM -> node-postgres -> node-pg-types -> postgres-date
```ts
register(1182, parseStringArray) // date[]
```
https://github.com/brianc/node-pg-types/blob/26bfe645a8ddc0c73830b1b8c63f2c4f8265b24f/lib/textParsers.js#L168C19-L168C45
```ts
static parse (dateString) {
return new PGDateParser(dateString).getJSDate()
}
```
https://github.com/bendrucker/postgres-date/blob/b3060560ed62c250f7800d29e4b68a9d5a0622bd/index.js#L285
# Calendar view mixing DATE and DATE_TIME
At the time of the refactor, DATE type wasn't properly tested with
calendar view, thus the drag and drop of a card with a calendar view on
a DATE field and not a DATE_TIME field, was not working, this is now
fixed.
## Changes
- Removed complex retry logic in `ImapClientProvider` by delegating to
root orchestrator
- Added `parseImapAuthenticationError` for handling revoked
authentication credentials previously the code existed in
`parse-imap-error.util` but it didn't belong there and nor was not used
in `ImapClientProvider` causing revoked channels to be stuck in limbo
- Removed `parseImapError` from `*-error-handler.service.ts `
- Created `isImapNetworkError` for consistency with Gmail and Microsoft
- `isImapNetworkError` is called at utility level for consistency
## Summary
This PR reduces clutter at the repository root to improve navigation on
GitHub. The README is now visible much sooner when browsing the repo.
## Changes
### Deleted from root
- `nx` wrapper script → use `npx nx` instead
- `render.yaml` → no longer used
- `jest.preset.js` → inlined `@nx/jest/preset` directly in each
package's jest.config
- `.prettierrc` → moved config to `package.json`
- `.prettierignore` → patterns already covered by `.gitignore`
### Moved/Consolidated
| From | To |
|------|-----|
| `Makefile` | `packages/twenty-docker/Makefile` (merged) |
| `crowdin-app.yml` | `.github/crowdin-app.yml` |
| `crowdin-docs.yml` | `.github/crowdin-docs.yml` |
| `.vale.ini` | `.github/vale.ini` |
| `tools/eslint-rules/` | `packages/twenty-eslint-rules/` |
| `eslint.config.react.mjs` |
`packages/twenty-front/eslint.config.react.mjs` |
## Result
Root items reduced from ~32 to ~22 (folders + files).
## Files updated
- GitHub workflow files updated to reference new crowdin config paths
- Jest configs updated to use `@nx/jest/preset` directly
- ESLint configs updated with new import paths
- `nx.json` updated with new paths
- `package.json` now includes prettier config and updated workspace
paths
- Dockerfile updated with new eslint-rules path
- new channel EVENT_STREAM_CHANNEL based on event stream id
- on event, perform the matching and publish only to the right streams
- store a list of active streams per workspace
- store the user id along with the queries for each stream
Bonus:
- remove onSubscriptionMatch
# Introduction
followup https://github.com/twentyhq/twenty/pull/16962
Root cause: as now the twenty standard app installation in seeded
workspace invalidates and set the page layout-xxx caches the inserted
through direct repo access data in the prefill legacy page layout
methods do not interact with the cache
The cache being set in prior when recompute does not result in db
introspection as before as when was unset
## Summary
The e2e login test was failing because it unconditionally tried to click
'Continue with Email' button, but this button doesn't exist when
password is the only auth method.
## Root Cause
In `SignInUpWorkspaceScopeFormEffect.tsx`, when a workspace only has
password authentication (no Google/Microsoft/SSO), the effect
automatically calls `continueWithEmail()` which skips the Init step and
shows the email field directly.
## Changes
1. **loginPage.ts**: Added `clickLoginWithEmailIfVisible()` method that
only clicks the button if it exists
2. **login.setup.ts**:
- Replaced `clickLoginWithEmail()` with `clickLoginWithEmailIfVisible()`
- Updated regex from `/Welcome to .+/` to `/Welcome, .+/` to match the
recent UI change
Fixes#17006
## Changes
Added `lastAuthenticateWorkspaceSsoMethodState` Recoil atom to track the
last used SSO method, persisted in localStorage
Updated `SignInUpWithGoogle` component to display a "Last" pill badge
when Google was the last auth method
Updated `SignInUpWithMicrosoft` component to display a "Last" pill badge
when Microsoft was the last auth method
Updated `SignInUpWithSSO` component to display a "Last" pill badge when
SSO was the last auth method.
The state is saved after successful authentication redirect, ensuring it
persists across sessions
## Implementation Details
Uses existing `localStorageEffect` for persistence across browser
sessions
Leverages the existing Pill component from twenty-ui with blue accent
color
The label is positioned on the right border of the button using absolute
positioning
State is saved in the `useAuth` hook during Google/Microsoft sign-in and
in the SSO login component
<img width="684" height="608" alt="image"
src="https://github.com/user-attachments/assets/629a1599-aab0-44e4-b684-ae91a8e725fd"
/>
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> Highlights the most recently used authentication method and preserves
it across sessions.
>
> - Introduces `AuthenticatedMethod` enum and
`lastAuthenticatedMethodState` (persisted via `localStorageEffect`) and
preserves it through `useAuth.clearSession`
> - Displays a "Last" pill via `LastUsedPill` on `SignInUpWithGoogle`,
`SignInUpWithMicrosoft`, `SignInUpWithSSO`, and
`SignInUpWithCredentials` when appropriate; adds
`StyledSSOButtonContainer` for badge positioning
> - Adds `useHasMultipleAuthMethods` to detect when to show the badge;
threads `isGlobalScope` to relevant components
> - Sets last-used method on click/submit in SSO and credentials flows
(`useSignInUp`, SSO button handlers)
> - Refactors `SignInUpGlobalScopeForm` to use `SignInUpWithCredentials`
and updates `SignInUp` title logic for global scope (e.g., "Welcome to
Twenty")
>
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
70d5527609. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
---------
Co-authored-by: Félix Malfait <felix@twenty.com>
- added article to detail how to attach a pdf to a given record
- added link in another article
- updated the english section of the docs.json to have the file appear
in the left menu
- updated the 2 navigation files
---------
Co-authored-by: github-actions <github-actions@twenty.com>
Refactors to be consistent with Microsoft service
Handles scenarios like temporary error which was not handled before
Moved `IsGmailNetworkError` from root orchestrator to driver level
Closes https://github.com/twentyhq/core-team-issues/issues/2058
We settled on a simpler solution by just hardcoding the objects order
for now. We will change this once it's possible to reorder the workspace
favorites with drag and drop.
# Introduction
Some legacy caches are based on the flat ones
So we need to seq invalidate before invalidating others as it could
result in inter dep cache invalidation race condition
# Introduction
As we've just
[identified](https://github.com/twentyhq/twenty/pull/16981) the standard
field metadata in production we can not enable the is unique comparison
even for standard entities
The current Japanese translations are severely lacking. My company
(based in Tokyo) uses Japanese as its main language. I've gone through
and corrected/standardized everything. This is good enough to merge as a
starting point.
Includes `lingui compile` for the changes.
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> Major localization refresh focused on Japanese.
>
> - Rewrites and standardizes `ja-JP` message strings (clearer phrasing,
consistent terminology, corrected placeholders/punctuation)
> - Regenerates compiled Lingui locale bundles; updates `ja-JP.ts` and
`ko-KR.ts` outputs
> - No runtime logic changes; scope limited to generated i18n resources
>
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
3365dcdac6. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
---------
Co-authored-by: Félix Malfait <felix@twenty.com>
Upgraded to Storybook 10. We still use `@storybook/test-runner` for
testing since it appears it'd require more work to move from Jest to
Vitest than I initially anticipated, but I completed this PR to fix
`storybook:serve:dev` - it takes time to load, but it works the way it
used to with Storybook 8.
https://github.com/user-attachments/assets/7afc32c6-4bcf-4b37-b83b-8d00d28dda15
## Summary
- Update all illustration icons to use `theme.accent.accent3` for fill
and `theme.accent.accent8` for border
- Replaces hardcoded `IllustrationIcon.blue` values with
theme-responsive accent colors
- Icons will now adapt correctly to theme changes
## Test plan
- [ ] Navigate to Settings > Data model > select any object
- [ ] Verify the Relations "Type" column icons use the accent colors
- [ ] Verify the Fields "Data type" column icons use the accent colors
- [ ] Switch themes (if available) and verify icons adapt accordingly