## Summary
This PR adds function build support to the `twenty dev` command,
enabling tree-shaken production builds of serverless functions during
development with Vite's incremental build capabilities.
## Changes
### New Features
- **Function building in dev mode**: Serverless functions are now
compiled to tree-shaken bundles in `.twenty/functions/` during
development
- **Automatic restart on entry point changes**: When functions are added
or removed, the watcher automatically restarts with the new
configuration
- **Function entry point logging**: Dev mode now displays detected
function entry points with their names and paths
- 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.
## 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
https://github.com/user-attachments/assets/3a444937-0290-4505-85e1-64b7b713bc3b
- [x] Analyze the existing `FieldWidgetRelationCard` and
`FieldWidgetMorphRelationCard` components
- [x] Review existing patterns for "More" buttons (found
`IconChevronDown` pattern)
- [x] Create a reusable `FieldWidgetShowMoreButton` component for the
"More (X)" button
- [x] Update `FieldWidgetRelationCard` to show max 5 items initially
with progressive loading
- [x] Update `FieldWidgetMorphRelationCard` to show max 5 items
initially with progressive loading
- [x] Add Storybook story with play function to test progressive loading
for regular relations
- [x] Verify changes visually in Storybook (all tests pass)
- [x] Run code review (no issues found)
- [x] Run CodeQL security check (no code changes for CodeQL to analyze)
- [x] Address PR review feedback:
- [x] Move constants to separate files
(`FieldWidgetRelationCardInitialVisibleItems.ts` and
`FieldWidgetRelationCardLoadMoreIncrement.ts`)
- [x] Add translation for "More (X)" string using `t` macro from
`@lingui/core/macro`
## Summary
This PR adds progressive loading functionality to the FIELD widget with
CARD display mode:
1. **New Component**: Created `FieldWidgetShowMoreButton` - A styled
button component that displays "More (X)" with a chevron icon, showing
the remaining count of hidden items.
2. **Updated Components**:
- `FieldWidgetRelationCard`: Now displays max 5 relation cards
initially, with a "More (X)" button that loads 5 more items on each
click
- `FieldWidgetMorphRelationCard`: Same progressive loading behavior for
morph relations
3. **Constants**: Moved to separate files following codebase
conventions:
- `FIELD_WIDGET_RELATION_CARD_INITIAL_VISIBLE_ITEMS` (5)
- `FIELD_WIDGET_RELATION_CARD_LOAD_MORE_INCREMENT` (5)
4. **Storybook Story**: Added
`OneToManyRelationCardWidgetWithProgressiveLoading` story with play
function that tests:
- Initial display of 5 items
- "More (7)" button visibility
- Loading additional items on click
- Button disappearing when all items are shown
<!-- START COPILOT ORIGINAL PROMPT -->
<details>
<summary>Original prompt</summary>
>
> ----
>
> *This section details on the original issue you should resolve*
>
> <issue_title>[RPL] Allow users to progressively load relations in
FIELD widget with CARD display mode</issue_title>
> <issue_description>## Current state
>
> The FIELD widget should display max 5 relations in CARD display mode.
>
> ## Goal
>
> Display a "More (12)" button. Clicking on this button loads 5 more
items. After a click, "More (12)" becomes "More (7)".
>
> <img width="1334" height="712" alt="Image"
src="https://github.com/user-attachments/assets/e801cca2-af99-4d87-8ce2-0c639775b0b1"
/>
>
> ## Technical input
>
> `FieldWidgetRelationCard` already loads all the relations and maps
over each one of them. Create a state that's updated when clicking on
the More button, slicing the relations to display.
>
> ## Acceptance criteria
>
> - Must work for relations and morph relations in _card_ display mode
> - Create stories to assert it works properly (with play function to
actually test the components)</issue_description>
>
> ## Comments on the Issue (you are @copilot in this section)
>
> <comments>
> </comments>
>
</details>
<!-- START COPILOT CODING AGENT SUFFIX -->
- Fixestwentyhq/core-team-issues#2063
<!-- START COPILOT CODING AGENT TIPS -->
---
💡 You can make Copilot smarter by setting up custom instructions,
customizing its development environment and configuring Model Context
Protocol (MCP) servers. Learn more [Copilot coding agent
tips](https://gh.io/copilot-coding-agent-tips) in the docs.
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Devessier <29370468+Devessier@users.noreply.github.com>
Co-authored-by: Baptiste Devessier <baptiste@devessier.fr>
Summary
Rewrites the app:dev command to use Vite's dev server instead of manual
file watching with chokidar. This provides better file watching
capabilities and aligns with the existing build tooling.
<img width="1588" height="822" alt="image"
src="https://github.com/user-attachments/assets/7c54bd39-4905-456f-8e3d-64e97b031de0"
/>
# 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
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.
# 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
## Description
This PR improves the developer experience for the `twenty-sdk` and
`create-twenty-app` packages by reorganizing commands, adding
development tooling, and improving documentation.
## Changes
### twenty-sdk
#### Command Refactoring
- **Renamed `app-watch` → `app-dev`**: Renamed `AppWatchCommand` to
`AppDevCommand` and moved to `app-dev.ts` for consistent naming with the
CLI command `app:dev`
- **Moved `app-add` → `entity-add`**: Relocated entity creation logic
from `app/app-add.ts` to `entity/entity-add.ts` and renamed
`AppAddCommand` to `EntityAddCommand` for better separation of concerns
#### Development Tooling
- **Added `dev` target**: New Nx target `npx nx run twenty-sdk:dev` that
runs the build in watch mode for faster development iteration
### create-twenty-app
#### Improved Scaffolded Project
- **Enhanced base-application README** with:
- Updated Getting Started to recommend `yarn dev` for development
- Added "Available Commands" section listing all available scripts
#### Better Onboarding
- **Improved success message** after app creation with formatted next
steps:
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.
# 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
## Summary
- Fix E2E login test flakiness by using `click()` auto-waiting instead
of `isVisible()` check
- The login form shows a loader while GraphQL data loads. The previous
`isVisible()` check returned immediately (no waiting) and would fail
while the loader was showing
- Using `click()` which has built-in auto-waiting for elements to be
visible and actionable fixes this
## Test plan
- E2E tests should pass more reliably in CI
- Login setup test should no longer timeout waiting for the email field
## 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
0c76dde03b. 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>
FIELD widgets displaying ONE_TO_MANY relations now show a "See all"
action button (arrow-up-right icon) that navigates to the record index
with filtered relations.
### Demo
https://github.com/user-attachments/assets/fd632553-70e3-4757-8f26-80aa8d2ce0d2
### Changes
- **`WidgetAction` type**: Added `'see-all'` to `WidgetActionId`
- **`useWidgetActions` hook**: Returns `'see-all'` action for
ONE_TO_MANY relation fields (position 0, before edit)
- **`WidgetActionFieldSeeAll` component**: Renders the navigation button
with:
- `IconArrowUpRight` icon
- Link computed using same logic as `RecordDetailRelationSection`
- Hover visibility behavior matching existing edit button
- **`WidgetActionRenderer`**: Added case for `'see-all'` action
- **Storybook**: Added `OneToManyRelationFieldWidgetWithSeeAllButton`
story verifying button visibility and well-formed link
### Link computation
Uses the same filter URL pattern as `RecordDetailRelationSection`:
```typescript
const filterQueryParams = {
filter: {
[relationFieldMetadataItem.name]: {
[ViewFilterOperand.IS]: {
selectedRecordIds: [targetRecord.id],
},
},
},
viewId: indexViewId,
};
const filterLinkHref = getAppPath(
AppPath.RecordIndexPage,
{ objectNamePlural: relationObjectMetadataItem.namePlural },
filterQueryParams,
);
```
The "see-all" action is shown regardless of field read-only status since
it's a navigation action, not an edit action.
<!-- START COPILOT ORIGINAL PROMPT -->
<details>
<summary>Original prompt</summary>
>
> ----
>
> *This section details on the original issue you should resolve*
>
> <issue_title>[RPL] Display "See all" widget action for FIELD widget
displaying relations</issue_title>
> <issue_description>The "See all" widget action is the button you can
see in the top right corner in the following screen. We should redirect
the user to the record index showing the filtered relations.
>
> <img width="1334" height="712" alt="Image"
src="https://github.com/user-attachments/assets/c8aca25e-2136-43b3-8896-abd161a5693e"
/>
>
> Example of interaction today in production:
>
>
https://github.com/user-attachments/assets/3730301c-d04b-4195-b2fb-a51f8efee08a
>
> ## Technical details
>
> See `useWidgetActions` and `WidgetActionRenderer`. Use the
`arrow-up-right` icon.
>
> See how link is computed here:
https://github.com/twentyhq/twenty/blob/3cf7803ee1ae545add02f0c628a933618ce736d5/packages/twenty-front/src/modules/object-record/record-field-list/record-detail-section/relation/components/RecordDetailRelationSection.tsx#L193-L204.
Reuse the same link.
>
> ## Acceptance criteria
>
> - The action should be displayed in addition to other actions that
might be rendered today
> - The action must only be displayed for ONE_TO_MANY relations
> - Ensure you write at least one story to ensure the button is
correctly displayed; the button should a well formed
link</issue_description>
>
> ## Comments on the Issue (you are @copilot in this section)
>
> <comments>
> </comments>
>
</details>
<!-- START COPILOT CODING AGENT SUFFIX -->
- Fixestwentyhq/core-team-issues#2064
<!-- START COPILOT CODING AGENT TIPS -->
---
💡 You can make Copilot smarter by setting up custom instructions,
customizing its development environment and configuring Model Context
Protocol (MCP) servers. Learn more [Copilot coding agent
tips](https://gh.io/copilot-coding-agent-tips) in the docs.
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Devessier <29370468+Devessier@users.noreply.github.com>
Co-authored-by: Devessier <baptiste@devessier.fr>
- Add URL validation in getImageBufferFromUrl utility
- Add response status validation and content-type checking
- Add timeout and connection error handling with specific error messages
- Validate buffer is not empty before processing
- Validate file type detection results before proceeding
- Ensure detected file type is actually an image format
- Add proper type safety for Axios error handling
This improves robustness when uploading images from URLs by:
- Preventing invalid URLs from being processed
- Providing clear error messages for different failure scenarios
- Ensuring only valid image files are processed
- Handling network errors gracefully
---------
Co-authored-by: GitTensor Miner <miner@gittensor.io>
Opening a PR to merge the wip work by @martmull
We will re-organize twenty-sdk in an upcoming PR
---------
Co-authored-by: martmull <martmull@hotmail.fr>
In this PR
- handle settings permission check for applications. Until then this was
unhandled and applications could not perform actions requiring settings
permissions, even if they were granted them
- fix ties to attachment, noteTarget etc:
When an object had their fields synchronized in the app, the system
fields created as a side-effect of the object creation (relations to
noteTarget, attachment, taskTarget, favorites, timelineActivities -
created with `isCustom: true`, not sure that is correct btw) were then
deleted because they are not declared in the app, and identified as
deletable because of `isCustom: true`.
Updating the logic to exclude system fields from the logic that detects
fields to delete.
I think this outline the confusion we have around isCustom, isSystem
etc.
- introduce uploadFile util in generated twenty client as it cannot be
handled by the client's query / mutation. I had to use this for my
invoicing app
# 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
This PR implements SSE update events across the main components of the
application : tables, boards, calendars, show pages.
There is still work to do on other event type and on making sure
everything works fine, but this first implementation should be robust
enough to start with.
Some problems encountered along the way :
- Events are returning raw Postgres output, because they are not
normalized by the GraphQL layer, so we ended up with amountMicros as
string values, which the frontend does not like, so I implemented a
small util in our `formatResult` generic pipeline to turn amountMicros
to a number if it's a string value. We could implement other formatters
for composite fields if we see problems with events.
-`action` property was missing in SSE events, which is required by the
frontend to know what kind of event it is.
# QA
https://github.com/user-attachments/assets/393b45ac-59d2-48b0-855b-2ce9c4b8ae57https://github.com/user-attachments/assets/cb214e7a-1595-4b85-bdb6-87630993d2e2
# 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
## Summary
Moves the custom ESLint rules from `tools/eslint-rules` to
`packages/twenty-eslint-rules` for better organization within the
monorepo packages structure.
## Changes
- Move `eslint-rules` from `tools/` to `packages/twenty-eslint-rules`
- Use `loadWorkspaceRules` from `@nx/eslint-plugin` to load custom rules
- Update all ESLint configs to use the `twenty/` rule prefix instead of
`@nx/workspace-`
- Update `project.json`, `jest.config.mjs` with new paths
- Update `package.json` workspaces and `nx.json` cache inputs
- Update Dockerfile reference
## Technical Details
The custom ESLint rules are now loaded using Nx's `loadWorkspaceRules`
utility which:
- Handles TypeScript transpilation automatically
- Allows loading workspace rules from any directory
- Provides a cleaner approach than the previous `@nx/workspace-`
convention
## Testing
- Verified all 17 custom ESLint rules load correctly from the new
location
- Verified linting works on dependent packages (twenty-front,
twenty-server, etc.)
As part of the extensibility effort, we are introducing a new engine
entity called "Front Component". This represents a dynamic react
component that will be rendered in CommandMenu actions or in PageLayout
widgets
This PR introduce the entity and all the necessary boilerplate to make
it syncable and cachable in the engine