Compare commits

..
Author SHA1 Message Date
Sonarly Claude Code 3f9b551366 fix: allow BlockNote floating panels to overflow side panel container
https://sonarly.com/issue/23837?type=bug

BlockNote's built-in image upload/embed panel is clipped by multiple `overflow: hidden` CSS declarations on the side panel container hierarchy, making it impossible to fully interact with image insertion when editing notes in the side panel.

Fix: Added a CSS override for BlockNote's `.bn-file-panel` class in the `StyledEditor` component to constrain the file upload/embed panel width to 100% of its container. This prevents the panel from extending beyond the side panel boundaries where it would be clipped by the `overflow: hidden` on the side panel container hierarchy.

The fix is placed in `BlockEditor.tsx` alongside all other BlockNote CSS overrides, following the existing pattern in the codebase. The `box-sizing: border-box` ensures padding/border are included in the width calculation.

**Limitations:** This is a partial fix. If the panel is positioned with absolute coordinates by Mantine's Floating UI that place it outside the container entirely, a more comprehensive fix would be needed — either a custom `filePanel` component that renders via a portal (using the existing `ModalContainerContext` in `SidePanelForDesktop`), or changing `overflow: hidden` to `overflow: clip` on `StyledSidePanel` (which would still clip but wouldn't create a scroll container for absolutely positioned descendants).
2026-04-10 19:56:46 +00:00
3528 changed files with 88831 additions and 166986 deletions
+1 -1
View File
@@ -22,7 +22,7 @@ This main guide provides a high-level overview and navigation hub.
A syncable entity is a metadata entity that:
- Has a **`universalIdentifier`**: A unique identifier used for syncing entities across workspaces/applications
- Has an **`applicationId`**: Links the entity to an application (Standard or Custom applications)
- Has an **`applicationId`**: Links the entity to an application (Twenty Standard or Custom applications)
- Participates in the **workspace migration system**: Can be created, updated, and deleted through the migration pipeline
- Is **cached as a flat entity**: Denormalized representation for efficient validation and change detection
+1 -8
View File
@@ -8,10 +8,6 @@ inputs:
api-key:
description: API key or access token for the target instance
required: true
app-path:
description: Path to the app directory (relative to repo root). Defaults to repo root.
required: false
default: '.'
runs:
using: composite
@@ -23,13 +19,11 @@ runs:
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version-file: '${{ inputs.app-path }}/.nvmrc'
node-version-file: '.nvmrc'
cache: yarn
cache-dependency-path: '${{ inputs.app-path }}/yarn.lock'
- name: Install dependencies
shell: bash
working-directory: ${{ inputs.app-path }}
run: yarn install --immutable
- name: Configure remote
@@ -49,5 +43,4 @@ runs:
- name: Deploy
shell: bash
working-directory: ${{ inputs.app-path }}
run: yarn twenty deploy --remote target
@@ -8,10 +8,6 @@ inputs:
api-key:
description: API key or access token for the target workspace
required: true
app-path:
description: Path to the app directory (relative to repo root). Defaults to repo root.
required: false
default: '.'
runs:
using: composite
@@ -23,13 +19,11 @@ runs:
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version-file: '${{ inputs.app-path }}/.nvmrc'
node-version-file: '.nvmrc'
cache: yarn
cache-dependency-path: '${{ inputs.app-path }}/yarn.lock'
- name: Install dependencies
shell: bash
working-directory: ${{ inputs.app-path }}
run: yarn install --immutable
- name: Configure remote
@@ -49,5 +43,4 @@ runs:
- name: Install
shell: bash
working-directory: ${{ inputs.app-path }}
run: yarn twenty install --remote target
@@ -15,8 +15,8 @@ outputs:
description: 'URL where the Twenty test server can be reached'
value: http://localhost:2021
api-key:
description: 'API key (type: API_KEY) for the seeded Twenty dev workspace'
value: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIyMDIwMjAyMC0xYzI1LTRkMDItYmYyNS02YWVjY2Y3ZWE0MTkiLCJ0eXBlIjoiQVBJX0tFWSIsIndvcmtzcGFjZUlkIjoiMjAyMDIwMjAtMWMyNS00ZDAyLWJmMjUtNmFlY2NmN2VhNDE5IiwiaWF0IjoxNzM1Njg5NjAwLCJleHAiOjQ4OTE0NDk2MDAsImp0aSI6IjIwMjAyMDIwLWY0MDEtNGQ4YS1hNzMxLTY0ZDAwN2MyN2JhZCJ9.bfQjfyN0NEtTCLE_xPyNcwonDzlSXFoP8kdCQTdnuDc
description: 'API key for the Twenty test instance'
value: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIyMDIwMjAyMC05ZTNiLTQ2ZDQtYTU1Ni04OGI5ZGRjMmIwMzQiLCJ1c2VySWQiOiIyMDIwMjAyMC05ZTNiLTQ2ZDQtYTU1Ni04OGI5ZGRjMmIwMzQiLCJ3b3Jrc3BhY2VJZCI6IjIwMjAyMDIwLTFjMjUtNGQwMi1iZjI1LTZhZWNjZjdlYTQxOSIsIndvcmtzcGFjZU1lbWJlcklkIjoiMjAyMDIwMjAtMDY4Ny00YzQxLWI3MDctZWQxYmZjYTk3MmE3IiwidXNlcldvcmtzcGFjZUlkIjoiMjAyMDIwMjAtOWUzYi00NmQ0LWE1NTYtODhiOWRkYzJiMDM1IiwidHlwZSI6IkFDQ0VTUyIsImF1dGhQcm92aWRlciI6InBhc3N3b3JkIiwiaWF0IjoxNzUxMjgxNzA0LCJleHAiOjQ5MDQ4ODE3MDR9.9S4wc0MOr5iczsomlFxZdOHD1IRDS4dnRSwNVNpctF4
runs:
using: 'composite'
+7 -17
View File
@@ -19,7 +19,6 @@ jobs:
files: |
packages/twenty-sdk/**
packages/twenty-server/**
.github/workflows/ci-sdk.yaml
!packages/twenty-sdk/package.json
sdk-test:
needs: changed-files-check
@@ -70,6 +69,7 @@ jobs:
ports:
- 6379:6379
env:
NODE_ENV: test
TWENTY_API_URL: http://localhost:3000
TWENTY_API_KEY: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIyMDIwMjAyMC1lNmI1LTQ2ODAtOGEzMi1iODIwOTczNzE1NmIiLCJ1c2VySWQiOiIyMDIwMjAyMC1lNmI1LTQ2ODAtOGEzMi1iODIwOTczNzE1NmIiLCJ3b3Jrc3BhY2VJZCI6IjIwMjAyMDIwLTFjMjUtNGQwMi1iZjI1LTZhZWNjZjdlYTQxOSIsIndvcmtzcGFjZU1lbWJlcklkIjoiMjAyMDIwMjAtNDYzZi00MzViLTgyOGMtMTA3ZTAwN2EyNzExIiwidXNlcldvcmtzcGFjZUlkIjoiMjAyMDIwMjAtMWU3Yy00M2Q5LWE1ZGItNjg1YjUwNjlkODE2IiwidHlwZSI6IkFDQ0VTUyIsImF1dGhQcm92aWRlciI6InBhc3N3b3JkIiwiaWF0IjoxNzUxMjgxNzA0LCJleHAiOjIwNjY4NTc3MDR9.HMGqCsVlOAPVUBhKSGlD1X86VoHKt4LIUtET3CGIdik
steps:
@@ -79,26 +79,16 @@ jobs:
fetch-depth: 10
- name: Install dependencies
uses: ./.github/actions/yarn-install
- name: Build SDK
- name: Build
run: npx nx build twenty-sdk
- name: Setup server environment
run: npx nx reset:env:e2e-testing-server twenty-server
- name: Create databases
- name: Server / Create Test DB
run: |
PGPASSWORD=postgres psql -h localhost -p 5432 -U postgres -d postgres -c 'CREATE DATABASE "default";'
PGPASSWORD=postgres psql -h localhost -p 5432 -U postgres -d postgres -c 'CREATE DATABASE "test";'
- name: Setup database
run: npx nx run twenty-server:database:reset
- name: Start server
run: nohup npx nx start:ci twenty-server > /tmp/twenty-server.log 2>&1 &
- name: Wait for server to be ready
run: npx wait-on http://localhost:3000/healthz --timeout 120000 --interval 1000
- name: SDK / Run e2e Tests
run: NODE_ENV=test npx vitest run --config ./vitest.e2e.config.ts
working-directory: packages/twenty-sdk
- name: Server / Dump logs on failure
if: failure()
run: tail -100 /tmp/twenty-server.log || echo "No server log file found"
uses: ./.github/actions/nx-affected
with:
tag: scope:sdk
tasks: test:e2e
ci-sdk-status-check:
if: always() && !cancelled()
timeout-minutes: 5
+3 -5
View File
@@ -25,7 +25,6 @@ jobs:
packages/twenty-server/**
packages/twenty-front/src/generated/**
packages/twenty-front/src/generated-metadata/**
packages/twenty-front/src/generated-admin/**
packages/twenty-client-sdk/**
packages/twenty-emails/**
packages/twenty-shared/**
@@ -166,14 +165,13 @@ jobs:
npx nx run twenty-front:graphql:generate
npx nx run twenty-front:graphql:generate --configuration=metadata
npx nx run twenty-front:graphql:generate --configuration=admin
if ! git diff --quiet -- packages/twenty-front/src/generated packages/twenty-front/src/generated-metadata packages/twenty-front/src/generated-admin; then
echo "::error::GraphQL schema changes detected. Please run the three graphql:generate configurations ('data', 'metadata', 'admin') and commit the changes."
if ! git diff --quiet -- packages/twenty-front/src/generated packages/twenty-front/src/generated-metadata; then
echo "::error::GraphQL schema changes detected. Please run 'npx nx run twenty-front:graphql:generate' and 'npx nx run twenty-front:graphql:generate --configuration=metadata' and commit the changes."
echo ""
echo "The following GraphQL schema changes were detected:"
echo "==================================================="
git diff -- packages/twenty-front/src/generated packages/twenty-front/src/generated-metadata packages/twenty-front/src/generated-admin
git diff -- packages/twenty-front/src/generated packages/twenty-front/src/generated-metadata
echo "==================================================="
echo ""
HAS_ERRORS=true
+3 -4
View File
@@ -69,14 +69,13 @@ jobs:
- name: Server / Start
run: |
npx nx run twenty-server:start:ci &
npx nx start twenty-server &
echo "Waiting for server to be ready..."
timeout 60 bash -c 'until curl -sf http://localhost:3000/healthz; do sleep 2; done'
timeout 60 bash -c 'until curl -s http://localhost:3000/health; do sleep 2; done'
- name: Start worker
working-directory: packages/twenty-server
run: |
NODE_ENV=development node dist/queue-worker/queue-worker.js &
npx nx run twenty-server:worker &
echo "Worker started"
- name: Zapier / Build
+1 -3
View File
@@ -1,8 +1,7 @@
**/**/.env
.DS_Store
/.idea
.claude/
.cursor/debug-*.log
.claude/settings.json
**/**/node_modules/
.cache
@@ -54,4 +53,3 @@ mcp.json
TRANSLATION_QA_REPORT.md
.playwright-mcp/
.playwright-cli/
output/playwright/
+89 -128
View File
@@ -1,170 +1,126 @@
<p align="center">
<a href="https://www.producthunt.com/products/twenty-crm?launch=twenty-2-0">
<img src="./packages/twenty-website/public/images/readme/product-hunt-banner.png" alt="We're live on Product Hunt — Support us" />
</a>
</p>
<p align="center">
<a href="https://www.twenty.com">
<img src="./packages/twenty-website/public/images/core/logo.svg" width="100px" alt="Twenty logo" />
</a>
</p>
<h2 align="center" >The #1 Open-Source CRM</h2>
<h2 align="center" >The #1 Open-Source CRM </h2>
<p align="center"><a href="https://twenty.com">🌐 Website</a> · <a href="https://docs.twenty.com">📚 Documentation</a> · <a href="https://github.com/orgs/twentyhq/projects/1"><img src="./packages/twenty-website/public/images/readme/planner-icon.svg" width="12" height="12"/> Roadmap </a> · <a href="https://discord.gg/cx5n4Jzs57"><img src="./packages/twenty-website/public/images/readme/discord-icon.svg" width="12" height="12"/> Discord</a> · <a href="https://www.figma.com/file/xt8O9mFeLl46C5InWwoMrN/Twenty"><img src="./packages/twenty-website/public/images/readme/figma-icon.png" width="12" height="12"/> Figma</a></p>
<br />
<p align="center"><a href="https://twenty.com"><img src="./packages/twenty-website/public/images/readme/globe-icon.svg" width="12" height="12"/> Website</a> · <a href="https://docs.twenty.com"><img src="./packages/twenty-website/public/images/readme/book-icon.svg" width="12" height="12"/> Documentation</a> · <a href="https://github.com/orgs/twentyhq/projects/1"><img src="./packages/twenty-website/public/images/readme/map-icon.svg" width="12" height="12"/> Roadmap </a> · <a href="https://discord.gg/cx5n4Jzs57"><img src="./packages/twenty-website/public/images/readme/discord-icon.svg" width="12" height="12"/> Discord</a> · <a href="https://www.figma.com/file/xt8O9mFeLl46C5InWwoMrN/Twenty"><img src="./packages/twenty-website/public/images/readme/figma-icon.png" width="12" height="12"/> Figma</a></p>
<p align="center">
<a href="https://www.twenty.com">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="./packages/twenty-website/public/images/readme/github-cover-dark.png" />
<source media="(prefers-color-scheme: light)" srcset="./packages/twenty-website/public/images/readme/github-cover-light.png" />
<img src="./packages/twenty-website/public/images/readme/github-cover-light.png" alt="Twenty banner" />
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/twentyhq/twenty/refs/heads/main/packages/twenty-website/public/images/readme/github-cover-dark.png" />
<source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/twentyhq/twenty/refs/heads/main/packages/twenty-website/public/images/readme/github-cover-light.png" />
<img src="./packages/twenty-website/public/images/readme/github-cover-light.png" alt="Cover" />
</picture>
</a>
</p>
<br />
# Why Twenty
Twenty gives technical teams the building blocks for a custom CRM that meets complex business needs and quickly adapts as the business evolves. Twenty is the CRM you build, ship, and version like the rest of your stack.
<a href="https://twenty.com/why-twenty"><img src="./packages/twenty-website/public/images/readme/star-icon.svg" width="14" height="14"/> Learn more about why we built Twenty</a>
<br />
# Installation
### <img src="./packages/twenty-website/public/images/readme/globe-icon.svg" width="14" height="14"/> Cloud
See:
🚀 [Self-hosting](https://docs.twenty.com/developers/self-host/capabilities/docker-compose)
🖥️ [Local Setup](https://docs.twenty.com/developers/contribute/capabilities/local-setup)
The fastest way to get started. Sign up at [twenty.com](https://twenty.com) and spin up a workspace in under a minute, with no infrastructure to manage and always up to date.
# Why Twenty
### <img src="./packages/twenty-website/public/images/readme/book-icon.svg" width="14" height="14"/> Build an app
We built Twenty for three reasons:
Scaffold a new app with the Twenty CLI:
**CRMs are too expensive, and users are trapped.** Companies use locked-in customer data to hike prices. It shouldn't be that way.
```bash
npx create-twenty-app my-app
```
**A fresh start is required to build a better experience.** We can learn from past mistakes and craft a cohesive experience inspired by new UX patterns from tools like Notion, Airtable or Linear.
Define objects, fields, and views as code:
```ts
import { defineObject, FieldType } from 'twenty-sdk/define';
export default defineObject({
nameSingular: 'deal',
namePlural: 'deals',
labelSingular: 'Deal',
labelPlural: 'Deals',
fields: [
{ name: 'name', label: 'Name', type: FieldType.TEXT },
{ name: 'amount', label: 'Amount', type: FieldType.CURRENCY },
{ name: 'closeDate', label: 'Close Date', type: FieldType.DATE_TIME },
],
});
```
Then ship it to your workspace:
```bash
npx twenty deploy
```
See the [app development guide](https://docs.twenty.com/developers/extend/apps/getting-started) for objects, views, agents, and logic functions.
### <img src="./packages/twenty-website/public/images/readme/rocket-icon.svg" width="14" height="14"/> Self-hosting
Run Twenty on your own infrastructure with [Docker Compose](https://docs.twenty.com/developers/self-host/capabilities/docker-compose), or contribute locally via the [local setup guide](https://docs.twenty.com/developers/contribute/capabilities/local-setup).
**We believe in open-source and community.** Hundreds of developers are already building Twenty together. Once we have plugin capabilities, a whole ecosystem will grow around it.
<br />
<br />
# Everything you need
# What You Can Do With Twenty
Twenty gives you the building blocks of a modern CRM (objects, views, workflows, and agents) and lets you extend them as code. Here's a tour of what's in the box.
Please feel free to flag any specific needs you have by creating an issue.
Want to go deeper? Read the <a href="https://docs.twenty.com/user-guide/introduction"><img src="./packages/twenty-website/public/images/readme/planner-icon.svg" width="14" height="14"/> User Guide</a> for product walkthroughs, or the <a href="https://docs.twenty.com"><img src="./packages/twenty-website/public/images/readme/book-icon.svg" width="14" height="14"/> Documentation</a> for developer reference.
Below are a few features we have implemented to date:
<table align="center">
<tr>
<td width="50%">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="./packages/twenty-website/public/images/readme/v2-build-apps-dark.png" />
<source media="(prefers-color-scheme: light)" srcset="./packages/twenty-website/public/images/readme/v2-build-apps-light.png" />
<img src="./packages/twenty-website/public/images/readme/v2-build-apps-light.png" alt="Create your apps" />
</picture>
<p align="center"><a href="https://docs.twenty.com/developers/extend/apps/getting-started"><img src="./packages/twenty-website/public/images/readme/code-icon.svg" width="16" height="16"/> Learn more about apps in doc</a></p>
</td>
<td width="50%">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="./packages/twenty-website/public/images/readme/v2-version-control-dark.png" />
<source media="(prefers-color-scheme: light)" srcset="./packages/twenty-website/public/images/readme/v2-version-control-light.png" />
<img src="./packages/twenty-website/public/images/readme/v2-version-control-light.png" alt="Stay on top with version control" />
</picture>
<p align="center"><a href="https://docs.twenty.com/developers/extend/apps/publishing"><img src="./packages/twenty-website/public/images/readme/monitor-icon.svg" width="16" height="16"/> Learn more about version control in doc</a></p>
</td>
</tr>
<tr>
<td width="50%">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="./packages/twenty-website/public/images/readme/v2-all-tools-dark.png" />
<source media="(prefers-color-scheme: light)" srcset="./packages/twenty-website/public/images/readme/v2-all-tools-light.png" />
<img src="./packages/twenty-website/public/images/readme/v2-all-tools-light.png" alt="All the tools you need to build anything" />
</picture>
<p align="center"><a href="https://docs.twenty.com/developers/extend/apps/building"><img src="./packages/twenty-website/public/images/readme/rocket-icon.svg" width="16" height="16"/> Learn more about primitives in doc</a></p>
</td>
<td width="50%">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="./packages/twenty-website/public/images/readme/v2-tools-dark.png" />
<source media="(prefers-color-scheme: light)" srcset="./packages/twenty-website/public/images/readme/v2-tools-light.png" />
<img src="./packages/twenty-website/public/images/readme/v2-tools-light.png" alt="Customize your layouts" />
</picture>
<p align="center"><a href="https://docs.twenty.com/user-guide/layout/overview"><img src="./packages/twenty-website/public/images/readme/planner-icon.svg" width="16" height="16"/> Learn more about layouts in doc</a></p>
</td>
</tr>
<tr>
<td width="50%">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="./packages/twenty-website/public/images/readme/v2-ai-agents-dark.png" />
<source media="(prefers-color-scheme: light)" srcset="./packages/twenty-website/public/images/readme/v2-ai-agents-light.png" />
<img src="./packages/twenty-website/public/images/readme/v2-ai-agents-light.png" alt="AI agents and chats" />
</picture>
<p align="center"><a href="https://docs.twenty.com/user-guide/ai/overview"><img src="./packages/twenty-website/public/images/readme/message-icon.svg" width="16" height="16"/> Learn more about AI in doc</a></p>
</td>
<td width="50%">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="./packages/twenty-website/public/images/readme/v2-crm-tools-dark.png" />
<source media="(prefers-color-scheme: light)" srcset="./packages/twenty-website/public/images/readme/v2-crm-tools-light.png" />
<img src="./packages/twenty-website/public/images/readme/v2-crm-tools-light.png" alt="Plus all the tools of a good CRM" />
</picture>
<p align="center"><a href="https://docs.twenty.com/user-guide/introduction"><img src="./packages/twenty-website/public/images/readme/star-icon.svg" width="16" height="16"/> Learn more about CRM features in doc</a></p>
</td>
</tr>
</table>
+ [Personalize layouts with filters, sort, group by, kanban and table views](#personalize-layouts-with-filters-sort-group-by-kanban-and-table-views)
+ [Customize your objects and fields](#customize-your-objects-and-fields)
+ [Create and manage permissions with custom roles](#create-and-manage-permissions-with-custom-roles)
+ [Automate workflow with triggers and actions](#automate-workflow-with-triggers-and-actions)
+ [Emails, calendar events, files, and more](#emails-calendar-events-files-and-more)
## Personalize layouts with filters, sort, group by, kanban and table views
<p align="center">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/twentyhq/twenty/refs/heads/main/packages/twenty-website/public/images/readme/views-dark.png" />
<source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/twentyhq/twenty/refs/heads/main/packages/twenty-website/public/images/readme/views-light.png" />
<img src="./packages/twenty-website/public/images/readme/views-light.png" alt="Companies Kanban Views" />
</picture>
</p>
## Customize your objects and fields
<p align="center">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/twentyhq/twenty/refs/heads/main/packages/twenty-website/public/images/readme/data-model-dark.png" />
<source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/twentyhq/twenty/refs/heads/main/packages/twenty-website/public/images/readme/data-model-light.png" />
<img src="./packages/twenty-website/public/images/readme/data-model-light.png" alt="Setting Custom Objects" />
</picture>
</p>
## Create and manage permissions with custom roles
<p align="center">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/twentyhq/twenty/refs/heads/main/packages/twenty-website/public/images/readme/permissions-dark.png" />
<source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/twentyhq/twenty/refs/heads/main/packages/twenty-website/public/images/readme/permissions-light.png" />
<img src="./packages/twenty-website/public/images/readme/permissions-light.png" alt="Permissions" />
</picture>
</p>
## Automate workflow with triggers and actions
<p align="center">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/twentyhq/twenty/refs/heads/main/packages/twenty-website/public/images/readme/workflows-dark.png" />
<source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/twentyhq/twenty/refs/heads/main/packages/twenty-website/public/images/readme/workflows-light.png" />
<img src="./packages/twenty-website/public/images/readme/workflows-light.png" alt="Workflows" />
</picture>
</p>
## Emails, calendar events, files, and more
<p align="center">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/twentyhq/twenty/refs/heads/main/packages/twenty-website/public/images/readme/plus-other-features-dark.png" />
<source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/twentyhq/twenty/refs/heads/main/packages/twenty-website/public/images/readme/plus-other-features-light.png" />
<img src="./packages/twenty-website/public/images/readme/plus-other-features-light.png" alt="Other Features" />
</picture>
</p>
<br />
# Stack
- <a href="https://www.typescriptlang.org/"><img src="./packages/twenty-website/public/images/readme/stack-typescript.svg" width="14" height="14"/> TypeScript</a>
- <a href="https://nx.dev/"><img src="./packages/twenty-website/public/images/readme/stack-nx.svg" width="14" height="14"/> Nx</a>
- <a href="https://nestjs.com/"><img src="./packages/twenty-website/public/images/readme/stack-nestjs.svg" width="14" height="14"/> NestJS</a>, with <a href="https://bullmq.io/">BullMQ</a>, <a href="https://www.postgresql.org/"><img src="./packages/twenty-website/public/images/readme/stack-postgresql.svg" width="14" height="14"/> PostgreSQL</a>, <a href="https://redis.io/"><img src="./packages/twenty-website/public/images/readme/stack-redis.svg" width="14" height="14"/> Redis</a>
- <a href="https://reactjs.org/"><img src="./packages/twenty-website/public/images/readme/stack-react.svg" width="14" height="14"/> React</a>, with <a href="https://jotai.org/">Jotai</a>, <a href="https://linaria.dev/">Linaria</a> and <a href="https://lingui.dev/">Lingui</a>
- [TypeScript](https://www.typescriptlang.org/)
- [Nx](https://nx.dev/)
- [NestJS](https://nestjs.com/), with [BullMQ](https://bullmq.io/), [PostgreSQL](https://www.postgresql.org/), [Redis](https://redis.io/)
- [React](https://reactjs.org/), with [Jotai](https://jotai.org/), [Linaria](https://linaria.dev/) and [Lingui](https://lingui.dev/)
# Thanks
<p align="center">
<a href="https://www.chromatic.com/"><img src="./packages/twenty-website/public/images/readme/chromatic.png" height="28" alt="Chromatic" /></a>
&nbsp;&nbsp;&nbsp;&nbsp;
<a href="https://greptile.com"><img src="./packages/twenty-website/public/images/readme/greptile.png" height="28" alt="Greptile" /></a>
&nbsp;&nbsp;&nbsp;&nbsp;
<a href="https://sentry.io/"><img src="./packages/twenty-website/public/images/readme/sentry.png" height="28" alt="Sentry" /></a>
&nbsp;&nbsp;&nbsp;&nbsp;
<a href="https://crowdin.com/"><img src="./packages/twenty-website/public/images/readme/crowdin.png" height="28" alt="Crowdin" /></a>
<a href="https://www.chromatic.com/"><img src="./packages/twenty-website/public/images/readme/chromatic.png" height="30" alt="Chromatic" /></a>
<a href="https://greptile.com"><img src="./packages/twenty-website/public/images/readme/greptile.png" height="30" alt="Greptile" /></a>
<a href="https://sentry.io/"><img src="./packages/twenty-website/public/images/readme/sentry.png" height="30" alt="Sentry" /></a>
<a href="https://crowdin.com/"><img src="./packages/twenty-website/public/images/readme/crowdin.png" height="30" alt="Crowdin" /></a>
<a href="https://e2b.dev/"><img src="./packages/twenty-website/public/images/readme/e2b.svg" height="30" alt="E2B" /></a>
</p>
Thanks to these amazing services that we use and recommend for UI testing (Chromatic), code review (Greptile), catching bugs (Sentry) and translating (Crowdin).
@@ -172,4 +128,9 @@ Want to go deeper? Read the <a href="https://docs.twenty.com/user-guide/introduc
# Join the Community
<p><a href="https://github.com/twentyhq/twenty"><img src="./packages/twenty-website/public/images/readme/star-icon.svg" width="12" height="12"/> Star the repo</a> · <a href="https://discord.gg/cx5n4Jzs57"><img src="./packages/twenty-website/public/images/readme/discord-icon.svg" width="12" height="12"/> Discord</a> · <a href="https://github.com/twentyhq/twenty/discussions"><img src="./packages/twenty-website/public/images/readme/message-icon.svg" width="12" height="12"/> Feature requests</a> · <a href="https://github.com/orgs/twentyhq/projects/1/views/35"><img src="./packages/twenty-website/public/images/readme/rocket-icon.svg" width="12" height="12"/> Releases</a> · <a href="https://twitter.com/twentycrm"><img src="./packages/twenty-website/public/images/readme/x-icon.svg" width="12" height="12"/> X</a> · <a href="https://www.linkedin.com/company/twenty/"><img src="./packages/twenty-website/public/images/readme/linkedin-icon.svg" width="12" height="12"/> LinkedIn</a> · <a href="https://twenty.crowdin.com/twenty"><img src="./packages/twenty-website/public/images/readme/language-icon.svg" width="12" height="12"/> Crowdin</a> · <a href="https://github.com/twentyhq/twenty/contribute"><img src="./packages/twenty-website/public/images/readme/code-icon.svg" width="12" height="12"/> Contribute</a></p>
- Star the repo
- Subscribe to releases (watch -> custom -> releases)
- Follow us on [Twitter](https://twitter.com/twentycrm) or [LinkedIn](https://www.linkedin.com/company/twenty/)
- Join our [Discord](https://discord.gg/cx5n4Jzs57)
- Improve translations on [Crowdin](https://twenty.crowdin.com/twenty)
- [Contributions](https://github.com/twentyhq/twenty/contribute) are, of course, most welcome!
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "create-twenty-app",
"version": "2.0.0",
"version": "0.9.0",
"description": "Command-line interface to create Twenty application",
"main": "dist/cli.cjs",
"bin": "dist/cli.cjs",
@@ -7,7 +7,9 @@
"npm": "please-use-yarn",
"yarn": ">=4.0.2"
},
"keywords": [],
"keywords": [
"twenty-app"
],
"packageManager": "yarn@4.9.2",
"scripts": {
"twenty": "twenty",
@@ -0,0 +1,70 @@
import { APPLICATION_UNIVERSAL_IDENTIFIER } from 'src/constants/universal-identifiers';
import { appBuild, appDeploy, appInstall, appUninstall } from 'twenty-sdk/cli';
import { MetadataApiClient } from 'twenty-client-sdk/metadata';
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
const APP_PATH = process.cwd();
describe('App installation', () => {
beforeAll(async () => {
const buildResult = await appBuild({
appPath: APP_PATH,
tarball: true,
onProgress: (message: string) => console.log(`[build] ${message}`),
});
if (!buildResult.success) {
throw new Error(
`Build failed: ${buildResult.error?.message ?? 'Unknown error'}`,
);
}
const deployResult = await appDeploy({
tarballPath: buildResult.data.tarballPath!,
onProgress: (message: string) => console.log(`[deploy] ${message}`),
});
if (!deployResult.success) {
throw new Error(
`Deploy failed: ${deployResult.error?.message ?? 'Unknown error'}`,
);
}
const installResult = await appInstall({ appPath: APP_PATH });
if (!installResult.success) {
throw new Error(
`Install failed: ${installResult.error?.message ?? 'Unknown error'}`,
);
}
});
afterAll(async () => {
const uninstallResult = await appUninstall({ appPath: APP_PATH });
if (!uninstallResult.success) {
console.warn(
`App uninstall failed: ${uninstallResult.error?.message ?? 'Unknown error'}`,
);
}
});
it('should find the installed app in the applications list', async () => {
const metadataClient = new MetadataApiClient();
const result = await metadataClient.query({
findManyApplications: {
id: true,
name: true,
universalIdentifier: true,
},
});
const installedApp = result.findManyApplications.find(
(application: { universalIdentifier: string }) =>
application.universalIdentifier === APPLICATION_UNIVERSAL_IDENTIFIER,
);
expect(installedApp).toBeDefined();
});
});
@@ -1,87 +0,0 @@
import * as fs from 'fs';
import * as os from 'os';
import * as path from 'path';
import { appDevOnce, appUninstall } from 'twenty-sdk/cli';
const APP_PATH = process.cwd();
const CONFIG_DIR = path.join(os.homedir(), '.twenty');
function validateEnv(): { apiUrl: string; apiKey: string } {
const apiUrl = process.env.TWENTY_API_URL;
const apiKey = process.env.TWENTY_API_KEY;
if (!apiUrl || !apiKey) {
throw new Error(
'TWENTY_API_URL and TWENTY_API_KEY must be set.\n' +
'Start a local server: yarn twenty server start\n' +
'Or set them in vitest env config.',
);
}
return { apiUrl, apiKey };
}
async function checkServer(apiUrl: string) {
let response: Response;
try {
response = await fetch(`${apiUrl}/healthz`);
} catch {
throw new Error(
`Twenty server is not reachable at ${apiUrl}. ` +
'Make sure the server is running before executing integration tests.',
);
}
if (!response.ok) {
throw new Error(`Server at ${apiUrl} returned ${response.status}`);
}
}
function writeConfig(apiUrl: string, apiKey: string) {
const payload = JSON.stringify(
{
remotes: {
local: { apiUrl, apiKey, accessToken: apiKey },
},
defaultRemote: 'local',
},
null,
2,
);
fs.mkdirSync(CONFIG_DIR, { recursive: true });
fs.writeFileSync(path.join(CONFIG_DIR, 'config.test.json'), payload);
}
export async function setup() {
const { apiUrl, apiKey } = validateEnv();
await checkServer(apiUrl);
writeConfig(apiUrl, apiKey);
await appUninstall({ appPath: APP_PATH }).catch(() => {});
const result = await appDevOnce({
appPath: APP_PATH,
onProgress: (message: string) => console.log(`[dev] ${message}`),
});
if (!result.success) {
throw new Error(
`Dev sync failed: ${result.error?.message ?? 'Unknown error'}`,
);
}
}
export async function teardown() {
const uninstallResult = await appUninstall({ appPath: APP_PATH });
if (!uninstallResult.success) {
console.warn(
`App uninstall failed: ${uninstallResult.error?.message ?? 'Unknown error'}`,
);
}
}
@@ -1,46 +0,0 @@
import { CoreApiClient } from 'twenty-client-sdk/core';
import { MetadataApiClient } from 'twenty-client-sdk/metadata';
import { APPLICATION_UNIVERSAL_IDENTIFIER } from 'src/constants/universal-identifiers';
import { describe, expect, it } from 'vitest';
describe('App installation', () => {
it('should find the installed app in the applications list', async () => {
const client = new MetadataApiClient();
const result = await client.query({
findManyApplications: {
id: true,
name: true,
universalIdentifier: true,
},
});
const app = result.findManyApplications.find(
(a: { universalIdentifier: string }) =>
a.universalIdentifier === APPLICATION_UNIVERSAL_IDENTIFIER,
);
expect(app).toBeDefined();
});
});
describe('CoreApiClient', () => {
it('should support CRUD on standard objects', async () => {
const client = new CoreApiClient();
const created = await client.mutation({
createNote: {
__args: { data: { title: 'Integration test note' } },
id: true,
},
});
expect(created.createNote.id).toBeDefined();
await client.mutation({
destroyNote: {
__args: { id: created.createNote.id },
id: true,
},
});
});
});
@@ -1,4 +1,4 @@
import { defineApplication } from 'twenty-sdk/define';
import { defineApplication } from 'twenty-sdk';
import {
APP_DESCRIPTION,
@@ -1,4 +1,4 @@
import { defineRole } from 'twenty-sdk/define';
import { defineRole } from 'twenty-sdk';
import {
APP_DISPLAY_NAME,
@@ -1,15 +1,6 @@
import tsconfigPaths from 'vite-tsconfig-paths';
import { defineConfig } from 'vitest/config';
const TWENTY_API_URL = process.env.TWENTY_API_URL ?? 'http://localhost:2020';
const TWENTY_API_KEY =
process.env.TWENTY_API_KEY ??
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIyMDIwMjAyMC0xYzI1LTRkMDItYmYyNS02YWVjY2Y3ZWE0MTkiLCJ0eXBlIjoiQVBJX0tFWSIsIndvcmtzcGFjZUlkIjoiMjAyMDIwMjAtMWMyNS00ZDAyLWJmMjUtNmFlY2NmN2VhNDE5IiwiaWF0IjoxNzM1Njg5NjAwLCJleHAiOjQ4OTE0NDk2MDAsImp0aSI6IjIwMjAyMDIwLWY0MDEtNGQ4YS1hNzMxLTY0ZDAwN2MyN2JhZCJ9.bfQjfyN0NEtTCLE_xPyNcwonDzlSXFoP8kdCQTdnuDc';
// Make env vars available to globalSetup (test.env only applies to workers)
process.env.TWENTY_API_URL = TWENTY_API_URL;
process.env.TWENTY_API_KEY = TWENTY_API_KEY;
export default defineConfig({
plugins: [
tsconfigPaths({
@@ -20,12 +11,14 @@ export default defineConfig({
test: {
testTimeout: 120_000,
hookTimeout: 120_000,
fileParallelism: false,
include: ['src/**/*.integration-test.ts'],
globalSetup: ['src/__tests__/global-setup.ts'],
setupFiles: ['src/__tests__/setup-test.ts'],
env: {
TWENTY_API_URL,
TWENTY_API_KEY,
TWENTY_API_URL: process.env.TWENTY_API_URL ?? 'http://localhost:2020',
TWENTY_API_KEY:
process.env.TWENTY_API_KEY ??
// Tim Apple (admin) access token for twenty-app-dev
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIyMDIwMjAyMC05ZTNiLTQ2ZDQtYTU1Ni04OGI5ZGRjMmIwMzQiLCJ1c2VySWQiOiIyMDIwMjAyMC05ZTNiLTQ2ZDQtYTU1Ni04OGI5ZGRjMmIwMzQiLCJ3b3Jrc3BhY2VJZCI6IjIwMjAyMDIwLTFjMjUtNGQwMi1iZjI1LTZhZWNjZjdlYTQxOSIsIndvcmtzcGFjZU1lbWJlcklkIjoiMjAyMDIwMjAtMDY4Ny00YzQxLWI3MDctZWQxYmZjYTk3MmE3IiwidXNlcldvcmtzcGFjZUlkIjoiMjAyMDIwMjAtOWUzYi00NmQ0LWE1NTYtODhiOWRkYzJiMDM1IiwidHlwZSI6IkFDQ0VTUyIsImF1dGhQcm92aWRlciI6InBhc3N3b3JkIiwiaWF0IjoxNzUxMjgxNzA0LCJleHAiOjQ5MDQ4ODE3MDR9.9S4wc0MOr5iczsomlFxZdOHD1IRDS4dnRSwNVNpctF4',
},
},
});
@@ -149,25 +149,6 @@ describe('copyBaseApplicationProject', () => {
);
});
it('should create an empty public directory in the scaffolded project', async () => {
await copyBaseApplicationProject({
appName: 'my-test-app',
appDisplayName: 'My Test App',
appDescription: 'A test application',
appDirectory: testAppDirectory,
});
const publicDirectoryPath = join(testAppDirectory, 'public');
expect(await fs.pathExists(publicDirectoryPath)).toBe(true);
const publicDirectoryStats = await fs.stat(publicDirectoryPath);
expect(publicDirectoryStats.isDirectory()).toBe(true);
const publicDirectoryContents = await fs.readdir(publicDirectoryPath);
expect(publicDirectoryContents).toHaveLength(0);
});
it('should handle empty description', async () => {
await copyBaseApplicationProject({
appName: 'my-test-app',
@@ -23,8 +23,6 @@ export const copyBaseApplicationProject = async ({
await renameDotfiles({ appDirectory });
await addEmptyPublicDirectory({ appDirectory });
await generateUniversalIdentifiers({
appDisplayName,
appDescription,
@@ -51,14 +49,6 @@ const renameDotfiles = async ({ appDirectory }: { appDirectory: string }) => {
}
};
const addEmptyPublicDirectory = async ({
appDirectory,
}: {
appDirectory: string;
}) => {
await fs.ensureDir(join(appDirectory, 'public'));
};
const generateUniversalIdentifiers = async ({
appDisplayName,
appDescription,
@@ -1,5 +1,5 @@
import { DEFAULT_ROLE_UNIVERSAL_IDENTIFIER } from 'src/roles/default-role';
import { defineApplication } from 'twenty-sdk/define';
import { defineApplication } from 'twenty-sdk';
export default defineApplication({
universalIdentifier: 'ac1d2ed1-8835-4bd4-9043-28b46fdda465',
@@ -1,4 +1,8 @@
import { defineField, FieldType, STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS } from 'twenty-sdk/define';
import {
defineField,
FieldType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk';
export default defineField({
universalIdentifier: 'da15cfc6-3657-457d-8757-4ba11b5bb6e1',
@@ -1,4 +1,8 @@
import { defineField, FieldType, STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS } from 'twenty-sdk/define';
import {
defineField,
FieldType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk';
export default defineField({
universalIdentifier: '505532f5-1fc5-4a58-8074-ba9b48650dbc',
@@ -1,4 +1,8 @@
import { defineField, FieldType, STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS } from 'twenty-sdk/define';
import {
defineField,
FieldType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk';
export default defineField({
universalIdentifier: 'be15e062-b065-48b4-979c-65b9a50e0cb1',
@@ -1,4 +1,8 @@
import { defineField, FieldType, STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS } from 'twenty-sdk/define';
import {
defineField,
FieldType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk';
export default defineField({
universalIdentifier: 'c90ae72d-4ddf-4f22-882f-eef98c91e40e',
@@ -2,7 +2,7 @@ import styled from '@emotion/styled';
import { useEffect, useState } from 'react';
import { OAuthApplicationVariables } from 'src/logic-functions/get-oauth-application-variables';
import { VERIFY_PAGE_PATH } from 'src/logic-functions/get-verify-page';
import { defineFrontComponent } from 'twenty-sdk/define';
import { defineFrontComponent } from 'twenty-sdk';
const StyledContainer = styled.div`
display: flex;
@@ -1,4 +1,4 @@
import { defineLogicFunction, RoutePayload } from "twenty-sdk/define";
import { defineLogicFunction, RoutePayload } from "twenty-sdk";
import { MetadataApiClient } from 'twenty-sdk/clients';
export const OAUTH_TOKEN_PAIRS_PATH = '/oauth/token-pairs';
@@ -1,4 +1,4 @@
import { defineLogicFunction } from 'twenty-sdk/define';
import { defineLogicFunction } from 'twenty-sdk';
export type OAuthApplicationVariables = {
apolloClientId: string;
@@ -1,4 +1,4 @@
import { defineLogicFunction } from "twenty-sdk/define";
import { defineLogicFunction } from "twenty-sdk";
export const VERIFY_PAGE_PATH = '/oauth/verify';
@@ -1,4 +1,8 @@
import { defineLogicFunction, type DatabaseEventPayload, type ObjectRecordUpdateEvent } from 'twenty-sdk/define';
import {
defineLogicFunction,
type DatabaseEventPayload,
type ObjectRecordUpdateEvent,
} from 'twenty-sdk';
import { CoreApiClient } from 'twenty-sdk/clients';
type CompanyRecord = {
@@ -1,4 +1,4 @@
import { definePostInstallLogicFunction, type InstallLogicFunctionPayload } from 'twenty-sdk/define';
import { definePostInstallLogicFunction, type InstallLogicFunctionPayload } from 'twenty-sdk';
const handler = async (payload: InstallLogicFunctionPayload): Promise<void> => {
console.log('Post install logic function executed successfully!', payload.previousVersion);
@@ -1,4 +1,4 @@
import { definePreInstallLogicFunction, type InstallLogicFunctionPayload } from 'twenty-sdk/define';
import { definePreInstallLogicFunction, type InstallLogicFunctionPayload } from 'twenty-sdk';
const handler = async (payload: InstallLogicFunctionPayload): Promise<void> => {
console.log('Pre install logic function executed successfully!', payload.previousVersion);
@@ -1,4 +1,4 @@
import { defineRole } from 'twenty-sdk/define';
import { defineRole } from 'twenty-sdk';
export const DEFAULT_ROLE_UNIVERSAL_IDENTIFIER =
'b8faae3f-e174-43fa-ab94-715712ae26cb';
@@ -1,4 +1,4 @@
import { type ApplicationConfig } from 'twenty-sdk/define';
import { type ApplicationConfig } from 'twenty-sdk';
const config: ApplicationConfig = {
universalIdentifier: 'a4df0c0f-c65e-44e5-8436-24814182d4ac',
@@ -1,4 +1,4 @@
import { Object } from 'twenty-sdk/define';
import { Object } from 'twenty-sdk';
@Object({
universalIdentifier: 'd1831348-b4a4-4426-9c0b-0af19e7a9c27',
@@ -1,4 +1,4 @@
import { type FunctionConfig } from 'twenty-sdk/define';
import { type FunctionConfig } from 'twenty-sdk';
import type { ProcessResult } from './types';
import { WebhookHandler } from './webhook-handler';
@@ -1,4 +1,4 @@
import { defineApplication } from 'twenty-sdk/define';
import { defineApplication } from 'twenty-sdk';
export default defineApplication({
universalIdentifier: '718ed9ab-53fc-49c8-8deb-0cff78ecf0d2',
@@ -1,4 +1,4 @@
import { defineField, FieldType, STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS } from 'twenty-sdk/define';
import { defineField, FieldType, STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS } from 'twenty-sdk';
export default defineField({
universalIdentifier: '9378751e-c23b-4e84-887d-2905cb8359b4',
@@ -1,4 +1,8 @@
import { defineField, FieldType, STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS } from 'twenty-sdk/define';
import {
defineField,
FieldType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk';
export default defineField({
universalIdentifier: '2f195c4c-1db1-4bbe-80b6-25c2f63168b0',
@@ -1,4 +1,4 @@
import { defineField, FieldType, STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS } from 'twenty-sdk/define';
import { defineField, FieldType, STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS } from 'twenty-sdk';
export default defineField({
universalIdentifier: 'fa342e26-9742-4db8-85b4-4d78ba18482f',
@@ -1,4 +1,8 @@
import { defineField, FieldType, STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS } from 'twenty-sdk/define';
import {
defineField,
FieldType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk';
export default defineField({
universalIdentifier: 'bec14de7-6683-4784-91ba-62d83b5f30f7',
@@ -1,4 +1,4 @@
import { defineLogicFunction } from 'twenty-sdk/define';
import { defineLogicFunction } from 'twenty-sdk';
import { CoreApiClient } from 'twenty-client-sdk/core';
import { calculateStatus } from '../shared/calculate-status';
@@ -1,4 +1,8 @@
import { DatabaseEventPayload, defineLogicFunction, ObjectRecordCreateEvent } from 'twenty-sdk/define';
import {
DatabaseEventPayload,
defineLogicFunction,
ObjectRecordCreateEvent,
} from 'twenty-sdk';
import { CoreApiClient } from 'twenty-client-sdk/core';
import { calculateStatus } from '../shared/calculate-status';
@@ -1,4 +1,8 @@
import { DatabaseEventPayload, defineLogicFunction, ObjectRecordCreateEvent } from 'twenty-sdk/define';
import {
DatabaseEventPayload,
defineLogicFunction,
ObjectRecordCreateEvent,
} from 'twenty-sdk';
import { CoreApiClient } from 'twenty-client-sdk/core';
import { calculateStatus } from '../shared/calculate-status';
@@ -1,4 +1,4 @@
import { type ApplicationConfig } from 'twenty-sdk/define';
import { type ApplicationConfig } from 'twenty-sdk';
const config: ApplicationConfig = {
universalIdentifier: '1eadac4e-db9f-4cce-b20b-de75f41e34dc',
@@ -1,5 +1,10 @@
import axios from 'axios';
import { type DatabaseEventPayload, type FunctionConfig, type ObjectRecordCreateEvent, type ObjectRecordUpdateEvent } from 'twenty-sdk/define';
import {
type DatabaseEventPayload,
type FunctionConfig,
type ObjectRecordCreateEvent,
type ObjectRecordUpdateEvent,
} from 'twenty-sdk';
import Twenty, { type Person } from '../generated';
const MAILCHIMP_API_URL: string =
@@ -1,4 +1,4 @@
import { type ApplicationConfig } from 'twenty-sdk/define';
import { type ApplicationConfig } from 'twenty-sdk';
const config: ApplicationConfig = {
universalIdentifier: '0ed2bcb8-64ab-4ca1-b875-eeabf41b5f95',
@@ -1,5 +1,5 @@
import axios from 'axios';
import { type FunctionConfig } from 'twenty-sdk/define';
import { type FunctionConfig } from 'twenty-sdk';
import {
type stripeCustomer,
type stripeEvent,
@@ -1,4 +1,4 @@
import { defineAgent } from 'twenty-sdk/define';
import { defineAgent } from 'twenty-sdk';
export const EXAMPLE_AGENT_UNIVERSAL_IDENTIFIER =
'110bebc2-f116-46b6-a35d-61e91c3c0a43';
@@ -1,4 +1,4 @@
import { defineApplication } from 'twenty-sdk/define';
import { defineApplication } from 'twenty-sdk';
import { DEFAULT_ROLE_UNIVERSAL_IDENTIFIER } from 'src/roles/default-role';
export const APPLICATION_UNIVERSAL_IDENTIFIER =
@@ -1,4 +1,4 @@
import { defineField, FieldType } from 'twenty-sdk/define';
import { defineField, FieldType } from 'twenty-sdk';
import { EXAMPLE_OBJECT_UNIVERSAL_IDENTIFIER } from 'src/objects/example-object';
export default defineField({
@@ -1,6 +1,6 @@
import { useEffect, useState } from 'react';
import { CoreApiClient, CoreSchema } from 'twenty-client-sdk/core';
import { defineFrontComponent } from 'twenty-sdk/define';
import { defineFrontComponent } from 'twenty-sdk';
export const HELLO_WORLD_FRONT_COMPONENT_UNIVERSAL_IDENTIFIER =
'7a758f23-5e7d-497d-98c9-7ca8d6c085b0';
@@ -1,5 +1,5 @@
import { CoreApiClient } from 'twenty-client-sdk/core';
import { defineLogicFunction } from 'twenty-sdk/define';
import { defineLogicFunction } from 'twenty-sdk';
const handler = async (): Promise<{ message: string }> => {
const client = new CoreApiClient();
@@ -1,4 +1,4 @@
import { defineLogicFunction } from 'twenty-sdk/define';
import { defineLogicFunction } from 'twenty-sdk';
const handler = async (): Promise<{ message: string }> => {
return { message: 'Hello, World!' };
@@ -1,4 +1,7 @@
import { definePostInstallLogicFunction, type InstallLogicFunctionPayload } from 'twenty-sdk/define';
import {
definePostInstallLogicFunction,
type InstallLogicFunctionPayload,
} from 'twenty-sdk';
const handler = async (payload: InstallLogicFunctionPayload): Promise<void> => {
console.log(
@@ -1,4 +1,4 @@
import { definePreInstallLogicFunction, type InstallLogicFunctionPayload } from 'twenty-sdk/define';
import { definePreInstallLogicFunction, type InstallLogicFunctionPayload } from 'twenty-sdk';
const handler = async (payload: InstallLogicFunctionPayload): Promise<void> => {
console.log('Pre install logic function executed successfully!', payload.previousVersion);
@@ -1,4 +1,4 @@
import { defineNavigationMenuItem } from 'twenty-sdk/define';
import { defineNavigationMenuItem } from 'twenty-sdk';
import { NavigationMenuItemType } from 'twenty-shared/types';
import { EXAMPLE_VIEW_UNIVERSAL_IDENTIFIER } from 'src/views/example-view';
@@ -1,4 +1,4 @@
import { defineObject, FieldType } from 'twenty-sdk/define';
import { defineObject, FieldType } from 'twenty-sdk';
export const EXAMPLE_OBJECT_UNIVERSAL_IDENTIFIER =
'47fd9bd9-392b-4d9f-9091-9a91b1edf519';
@@ -1,6 +1,6 @@
import { EXAMPLE_OBJECT_UNIVERSAL_IDENTIFIER } from 'src/objects/example-object';
import { HELLO_WORLD_FRONT_COMPONENT_UNIVERSAL_IDENTIFIER } from 'src/front-components/hello-world';
import { definePageLayout, PageLayoutTabLayoutMode } from 'twenty-sdk/define';
import { definePageLayout, PageLayoutTabLayoutMode } from 'twenty-sdk';
export default definePageLayout({
universalIdentifier: '203aeb94-6701-46d6-9af1-be2bbcc9e134',
@@ -1,4 +1,4 @@
import { defineRole } from 'twenty-sdk/define';
import { defineRole } from 'twenty-sdk';
export const DEFAULT_ROLE_UNIVERSAL_IDENTIFIER =
'c38f4d11-760c-4d5c-89ed-e569c28b7b70';
@@ -1,4 +1,4 @@
import { defineSkill } from 'twenty-sdk/define';
import { defineSkill } from 'twenty-sdk';
export const EXAMPLE_SKILL_UNIVERSAL_IDENTIFIER =
'90cf9144-4811-4653-93a2-9a6780fe6aac';
@@ -1,4 +1,4 @@
import { defineView, ViewKey } from 'twenty-sdk/define';
import { defineView, ViewKey } from 'twenty-sdk';
import { EXAMPLE_OBJECT_UNIVERSAL_IDENTIFIER, NAME_FIELD_UNIVERSAL_IDENTIFIER } from 'src/objects/example-object';
export const EXAMPLE_VIEW_UNIVERSAL_IDENTIFIER = '965e3776-b966-4be8-83f7-6cd3bce5e1bd';
@@ -17,7 +17,8 @@ export default defineConfig({
TWENTY_API_URL: process.env.TWENTY_API_URL ?? 'http://localhost:2020',
TWENTY_API_KEY:
process.env.TWENTY_API_KEY ??
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIyMDIwMjAyMC0xYzI1LTRkMDItYmYyNS02YWVjY2Y3ZWE0MTkiLCJ0eXBlIjoiQVBJX0tFWSIsIndvcmtzcGFjZUlkIjoiMjAyMDIwMjAtMWMyNS00ZDAyLWJmMjUtNmFlY2NmN2VhNDE5IiwiaWF0IjoxNzM1Njg5NjAwLCJleHAiOjQ4OTE0NDk2MDAsImp0aSI6IjIwMjAyMDIwLWY0MDEtNGQ4YS1hNzMxLTY0ZDAwN2MyN2JhZCJ9.bfQjfyN0NEtTCLE_xPyNcwonDzlSXFoP8kdCQTdnuDc',
// Tim Apple (admin) access token for twenty-app-dev
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIyMDIwMjAyMC05ZTNiLTQ2ZDQtYTU1Ni04OGI5ZGRjMmIwMzQiLCJ1c2VySWQiOiIyMDIwMjAyMC05ZTNiLTQ2ZDQtYTU1Ni04OGI5ZGRjMmIwMzQiLCJ3b3Jrc3BhY2VJZCI6IjIwMjAyMDIwLTFjMjUtNGQwMi1iZjI1LTZhZWNjZjdlYTQxOSIsIndvcmtzcGFjZU1lbWJlcklkIjoiMjAyMDIwMjAtMDY4Ny00YzQxLWI3MDctZWQxYmZjYTk3MmE3IiwidXNlcldvcmtzcGFjZUlkIjoiMjAyMDIwMjAtOWUzYi00NmQ0LWE1NTYtODhiOWRkYzJiMDM1IiwidHlwZSI6IkFDQ0VTUyIsImF1dGhQcm92aWRlciI6InBhc3N3b3JkIiwiaWF0IjoxNzUxMjgxNzA0LCJleHAiOjQ5MDQ4ODE3MDR9.9S4wc0MOr5iczsomlFxZdOHD1IRDS4dnRSwNVNpctF4',
},
},
});
@@ -1,7 +1,7 @@
import { APPLICATION_UNIVERSAL_IDENTIFIER } from 'src/application.config';
import { appBuild, appDeploy, appInstall, appUninstall } from 'twenty-sdk/cli';
import { MetadataApiClient } from 'twenty-client-sdk/metadata';
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
import { APPLICATION_UNIVERSAL_IDENTIFIER } from 'src/application.config';
const APP_PATH = process.cwd();
@@ -1,87 +0,0 @@
import * as fs from 'fs';
import * as os from 'os';
import * as path from 'path';
import { appDevOnce, appUninstall } from 'twenty-sdk/cli';
const APP_PATH = process.cwd();
const CONFIG_DIR = path.join(os.homedir(), '.twenty');
function validateEnv(): { apiUrl: string; apiKey: string } {
const apiUrl = process.env.TWENTY_API_URL;
const apiKey = process.env.TWENTY_API_KEY;
if (!apiUrl || !apiKey) {
throw new Error(
'TWENTY_API_URL and TWENTY_API_KEY must be set.\n' +
'Start a local server: yarn twenty server start\n' +
'Or set them in vitest env config.',
);
}
return { apiUrl, apiKey };
}
async function checkServer(apiUrl: string) {
let response: Response;
try {
response = await fetch(`${apiUrl}/healthz`);
} catch {
throw new Error(
`Twenty server is not reachable at ${apiUrl}. ` +
'Make sure the server is running before executing integration tests.',
);
}
if (!response.ok) {
throw new Error(`Server at ${apiUrl} returned ${response.status}`);
}
}
function writeConfig(apiUrl: string, apiKey: string) {
const payload = JSON.stringify(
{
remotes: {
local: { apiUrl, apiKey, accessToken: apiKey },
},
defaultRemote: 'local',
},
null,
2,
);
fs.mkdirSync(CONFIG_DIR, { recursive: true });
fs.writeFileSync(path.join(CONFIG_DIR, 'config.test.json'), payload);
}
export async function setup() {
const { apiUrl, apiKey } = validateEnv();
await checkServer(apiUrl);
writeConfig(apiUrl, apiKey);
await appUninstall({ appPath: APP_PATH }).catch(() => {});
const result = await appDevOnce({
appPath: APP_PATH,
onProgress: (message: string) => console.log(`[dev] ${message}`),
});
if (!result.success) {
throw new Error(
`Dev sync failed: ${result.error?.message ?? 'Unknown error'}`,
);
}
}
export async function teardown() {
const uninstallResult = await appUninstall({ appPath: APP_PATH });
if (!uninstallResult.success) {
console.warn(
`App uninstall failed: ${uninstallResult.error?.message ?? 'Unknown error'}`,
);
}
}
@@ -1,63 +0,0 @@
import { MetadataApiClient } from 'twenty-client-sdk/metadata';
import { APPLICATION_UNIVERSAL_IDENTIFIER } from 'src/application.config';
import { describe, expect, it } from 'vitest';
describe('App installation', () => {
it('should find the installed app in the applications list', async () => {
const client = new MetadataApiClient();
const result = await client.query({
findManyApplications: {
id: true,
name: true,
universalIdentifier: true,
},
});
const app = result.findManyApplications.find(
(a: { universalIdentifier: string }) =>
a.universalIdentifier === APPLICATION_UNIVERSAL_IDENTIFIER,
);
expect(app).toBeDefined();
});
});
describe('PostCard object', () => {
it('should exist with expected fields and relations', async () => {
const client = new MetadataApiClient();
const { objects } = await client.query({
objects: {
__args: {
filter: { isCustom: { is: true } },
paging: { first: 50 },
},
edges: {
node: {
nameSingular: true,
fields: {
__args: { paging: { first: 500 } },
edges: { node: { name: true } },
},
},
},
},
});
const obj = objects.edges
.map((e: { node: { nameSingular: string } }) => e.node)
.find((n: { nameSingular: string }) => n.nameSingular === 'postCard');
expect(obj).toBeDefined();
const names = obj!.fields.edges.map(
(e: { node: { name: string } }) => e.node.name,
);
expect(names).toContain('name');
expect(names).toContain('content');
expect(names).toContain('status');
expect(names).toContain('deliveredAt');
expect(names).toContain('recipient');
});
});
@@ -0,0 +1,53 @@
import * as fs from 'fs';
import * as os from 'os';
import * as path from 'path';
import { beforeAll } from 'vitest';
const CONFIG_DIR = path.join(os.homedir(), '.twenty');
const CONFIG_PATH = path.join(CONFIG_DIR, 'config.test.json');
beforeAll(async () => {
const apiUrl = process.env.TWENTY_API_URL!;
const token = process.env.TWENTY_API_KEY!;
if (!apiUrl || !token) {
throw new Error(
'TWENTY_API_URL and TWENTY_API_KEY must be set.\n' +
'Start a local server: yarn twenty server start\n' +
'Or set them in vitest env config.',
);
}
let response: Response;
try {
response = await fetch(`${apiUrl}/healthz`);
} catch {
throw new Error(
`Twenty server is not reachable at ${apiUrl}. ` +
'Make sure the server is running before executing integration tests.',
);
}
if (!response.ok) {
throw new Error(`Server at ${apiUrl} returned ${response.status}`);
}
fs.mkdirSync(CONFIG_DIR, { recursive: true });
fs.writeFileSync(
CONFIG_PATH,
JSON.stringify(
{
remotes: {
local: { apiUrl, apiKey: token },
},
defaultRemote: 'local',
},
null,
2,
),
);
process.env.TWENTY_APP_ACCESS_TOKEN ??= token;
});
@@ -1,4 +1,4 @@
import { defineAgent } from 'twenty-sdk/define';
import { defineAgent } from 'twenty-sdk';
export default defineAgent({
universalIdentifier: 'b8d4f2a3-9c5e-4f7b-a012-3e4d5c6b7a8f',
@@ -1,4 +1,4 @@
import { defineApplication } from 'twenty-sdk/define';
import { defineApplication } from 'twenty-sdk';
import { DEFAULT_ROLE_UNIVERSAL_IDENTIFIER } from './roles/default-function.role';
export const APPLICATION_UNIVERSAL_IDENTIFIER =
@@ -1,6 +1,5 @@
import { useCallback, useEffect, useState } from 'react';
import { defineFrontComponent } from 'twenty-sdk/define';
import { useRecordId } from 'twenty-sdk/front-component';
import { defineFrontComponent, useRecordId } from 'twenty-sdk';
import { CoreApiClient } from 'twenty-client-sdk/core';
import { isDefined } from 'twenty-shared/utils';
@@ -1,6 +1,11 @@
import { useEffect } from 'react';
import { defineFrontComponent } from 'twenty-sdk/define';
import { useRecordId, updateProgress, enqueueSnackbar, unmountFrontComponent } from 'twenty-sdk/front-component';
import {
defineFrontComponent,
useRecordId,
updateProgress,
enqueueSnackbar,
unmountFrontComponent,
} from 'twenty-sdk';
import { CoreApiClient } from 'twenty-client-sdk/core';
import { isDefined } from 'twenty-shared/utils';
import { POST_CARD_UNIVERSAL_IDENTIFIER } from '../objects/post-card.object';
@@ -1,6 +1,11 @@
import { useEffect } from 'react';
import { defineFrontComponent } from 'twenty-sdk/define';
import { useRecordId, updateProgress, enqueueSnackbar, unmountFrontComponent } from 'twenty-sdk/front-component';
import {
defineFrontComponent,
useRecordId,
updateProgress,
enqueueSnackbar,
unmountFrontComponent,
} from 'twenty-sdk';
import { CoreApiClient } from 'twenty-client-sdk/core';
import { isDefined } from 'twenty-shared/utils';
import { POST_CARD_UNIVERSAL_IDENTIFIER } from '../objects/post-card.object';
@@ -1,4 +1,8 @@
import { defineField, FieldType, STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS } from 'twenty-sdk/define';
import {
defineField,
FieldType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk';
// Field on existing company object
export default defineField({
@@ -1,4 +1,9 @@
import { defineField, FieldType, RelationType, STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS } from 'twenty-sdk/define';
import {
defineField,
FieldType,
RelationType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk';
import { POST_CARD_UNIVERSAL_IDENTIFIER } from '../objects/post-card.object';
import {
POST_CARDS_ON_PERSON_ID,
@@ -1,4 +1,10 @@
import { defineField, FieldType, RelationType, OnDeleteAction, STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS } from 'twenty-sdk/define';
import {
defineField,
FieldType,
RelationType,
OnDeleteAction,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk';
import { POST_CARD_UNIVERSAL_IDENTIFIER } from '../objects/post-card.object';
export const RECIPIENT_ON_POST_CARD_ID = 'c44f158e-2747-42c6-9295-75b8cbae7039';
@@ -1,5 +1,5 @@
import { CoreApiClient } from 'twenty-client-sdk/core';
import { definePostInstallLogicFunction } from 'twenty-sdk/define';
import { definePostInstallLogicFunction } from 'twenty-sdk';
const SEED_POST_CARDS = [
{
@@ -1,4 +1,4 @@
import { definePreInstallLogicFunction } from 'twenty-sdk/define';
import { definePreInstallLogicFunction } from 'twenty-sdk';
const handler = async (params: any) => {
console.log(
@@ -1,4 +1,4 @@
import { defineNavigationMenuItem } from 'twenty-sdk/define';
import { defineNavigationMenuItem } from 'twenty-sdk';
import { NavigationMenuItemType } from 'twenty-shared/types';
import { POST_CARD_UNIVERSAL_IDENTIFIER } from '../objects/post-card.object';
@@ -1,4 +1,4 @@
import { defineObject, FieldType } from 'twenty-sdk/define';
import { defineObject, FieldType } from 'twenty-sdk';
enum PostCardStatus {
DRAFT = 'DRAFT',
@@ -1,4 +1,4 @@
import { definePageLayout, PageLayoutTabLayoutMode } from 'twenty-sdk/define';
import { definePageLayout, PageLayoutTabLayoutMode } from 'twenty-sdk';
import { POST_CARD_UNIVERSAL_IDENTIFIER } from 'src/objects/post-card.object';
import { CARD_FRONT_COMPONENT_UNIVERSAL_IDENTIFIER } from 'src/components/card.front-component';
@@ -1,4 +1,4 @@
import { PermissionFlag, defineRole } from 'twenty-sdk/define';
import { PermissionFlag, defineRole } from 'twenty-sdk';
import {
CONTENT_FIELD_UNIVERSAL_IDENTIFIER,
POST_CARD_UNIVERSAL_IDENTIFIER,
@@ -1,4 +1,4 @@
import { defineSkill } from 'twenty-sdk/define';
import { defineSkill } from 'twenty-sdk';
export default defineSkill({
universalIdentifier: 'a7c3e1f2-8b4d-4e6a-9f01-2d3c4b5a6e7f',
@@ -1,4 +1,4 @@
import { defineView } from 'twenty-sdk/define';
import { defineView } from 'twenty-sdk';
import {
CONTENT_FIELD_UNIVERSAL_IDENTIFIER,
NAME_FIELD_UNIVERSAL_IDENTIFIER,
@@ -1,15 +1,6 @@
import tsconfigPaths from 'vite-tsconfig-paths';
import { defineConfig } from 'vitest/config';
const TWENTY_API_URL = process.env.TWENTY_API_URL ?? 'http://localhost:2020';
const TWENTY_API_KEY =
process.env.TWENTY_API_KEY ??
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIyMDIwMjAyMC0xYzI1LTRkMDItYmYyNS02YWVjY2Y3ZWE0MTkiLCJ0eXBlIjoiQVBJX0tFWSIsIndvcmtzcGFjZUlkIjoiMjAyMDIwMjAtMWMyNS00ZDAyLWJmMjUtNmFlY2NmN2VhNDE5IiwiaWF0IjoxNzM1Njg5NjAwLCJleHAiOjQ4OTE0NDk2MDAsImp0aSI6IjIwMjAyMDIwLWY0MDEtNGQ4YS1hNzMxLTY0ZDAwN2MyN2JhZCJ9.bfQjfyN0NEtTCLE_xPyNcwonDzlSXFoP8kdCQTdnuDc';
// Make env vars available to globalSetup (test.env only applies to workers)
process.env.TWENTY_API_URL = TWENTY_API_URL;
process.env.TWENTY_API_KEY = TWENTY_API_KEY;
export default defineConfig({
plugins: [
tsconfigPaths({
@@ -20,12 +11,14 @@ export default defineConfig({
test: {
testTimeout: 120_000,
hookTimeout: 120_000,
fileParallelism: false,
include: ['src/**/*.integration-test.ts'],
globalSetup: ['src/__tests__/global-setup.ts'],
setupFiles: ['src/__tests__/setup-test.ts'],
env: {
TWENTY_API_URL,
TWENTY_API_KEY,
TWENTY_API_URL: process.env.TWENTY_API_URL ?? 'http://localhost:2020',
TWENTY_API_KEY:
process.env.TWENTY_API_KEY ??
// Tim Apple (admin) access token for twenty-app-dev
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIyMDIwMjAyMC05ZTNiLTQ2ZDQtYTU1Ni04OGI5ZGRjMmIwMzQiLCJ1c2VySWQiOiIyMDIwMjAyMC05ZTNiLTQ2ZDQtYTU1Ni04OGI5ZGRjMmIwMzQiLCJ3b3Jrc3BhY2VJZCI6IjIwMjAyMDIwLTFjMjUtNGQwMi1iZjI1LTZhZWNjZjdlYTQxOSIsIndvcmtzcGFjZU1lbWJlcklkIjoiMjAyMDIwMjAtMDY4Ny00YzQxLWI3MDctZWQxYmZjYTk3MmE3IiwidXNlcldvcmtzcGFjZUlkIjoiMjAyMDIwMjAtOWUzYi00NmQ0LWE1NTYtODhiOWRkYzJiMDM1IiwidHlwZSI6IkFDQ0VTUyIsImF1dGhQcm92aWRlciI6InBhc3N3b3JkIiwiaWF0IjoxNzUxMjgxNzA0LCJleHAiOjQ5MDQ4ODE3MDR9.9S4wc0MOr5iczsomlFxZdOHD1IRDS4dnRSwNVNpctF4',
},
},
});
@@ -1,4 +1,4 @@
import { defineApplication } from 'twenty-sdk/define';
import { defineApplication } from 'twenty-sdk';
import { DEFAULT_ROLE_UNIVERSAL_IDENTIFIER } from './my.role';
@@ -1,4 +1,4 @@
import { defineRole } from 'twenty-sdk/define';
import { defineRole } from 'twenty-sdk';
export const DEFAULT_ROLE_UNIVERSAL_IDENTIFIER =
'9f992b6c-17e9-4381-bab5-b6150b574304';
@@ -1,4 +1,4 @@
import { defineLogicFunction } from 'twenty-sdk/define';
import { defineLogicFunction } from 'twenty-sdk';
export const ADD_NUMBERS_UNIVERSAL_IDENTIFIER =
'f9e5589c-e951-4d99-85db-0a305ab53502';
@@ -1,4 +1,4 @@
import { defineApplication } from 'twenty-sdk/define';
import { defineApplication } from 'twenty-sdk';
export default defineApplication({
universalIdentifier: 'invalid-app-0000-0000-0000-000000000001',
@@ -1,4 +1,4 @@
import { defineObject, FieldType } from 'twenty-sdk/define';
import { defineObject, FieldType } from 'twenty-sdk';
const DUPLICATE_ID = 'duplicate-id-0000-0000-000000000001';
@@ -1,4 +1,4 @@
import { defineObject, FieldType } from 'twenty-sdk/define';
import { defineObject, FieldType } from 'twenty-sdk';
const DUPLICATE_ID = 'duplicate-id-0000-0000-000000000001';
@@ -1,4 +1,4 @@
import { defineApplication } from 'twenty-sdk/define';
import { defineApplication } from 'twenty-sdk';
export default defineApplication({
universalIdentifier: 'e1e2e3e4-e5e6-4000-8000-000000000001',
@@ -1,4 +1,4 @@
import { defineFrontComponent } from 'twenty-sdk/define';
import { defineFrontComponent } from 'twenty-sdk';
export const MyComponent = () => {
return (
@@ -1,4 +1,4 @@
import { defineLogicFunction } from 'twenty-sdk/define';
import { defineLogicFunction } from 'twenty-sdk';
const myHandler = () => {
return 'my-function-result';
@@ -1,4 +1,4 @@
import { FieldType, defineObject } from 'twenty-sdk/define';
import { FieldType, defineObject } from 'twenty-sdk';
export default defineObject({
universalIdentifier: 'e1e2e3e4-e5e6-4000-8000-000000000030',
@@ -1,4 +1,4 @@
import { defineRole } from 'twenty-sdk/define';
import { defineRole } from 'twenty-sdk';
export default defineRole({
universalIdentifier: 'e1e2e3e4-e5e6-4000-8000-000000000040',
@@ -1,4 +1,4 @@
import { defineApplication } from 'twenty-sdk/define';
import { defineApplication } from 'twenty-sdk';
import { DEFAULT_ROLE_UNIVERSAL_IDENTIFIER } from './src/roles/default-function.role';
export default defineApplication({
@@ -1,4 +1,4 @@
import { defineFrontComponent } from 'twenty-sdk/define';
import { defineFrontComponent } from 'twenty-sdk';
import { CardDisplay } from '../utils/card-display.component';
export default defineFrontComponent({
@@ -1,4 +1,4 @@
import { defineFrontComponent } from 'twenty-sdk/define';
import { defineFrontComponent } from 'twenty-sdk';
import { DEFAULT_NAME, formatGreeting } from '../utils/greeting.util';
const GreetingComponent = () => {

Some files were not shown because too many files have changed in this diff Show More