
@@ -88,11 +87,11 @@ Front-Komponenten gibt es in zwei Rendering-Modi, die durch die Option `isHeadle
```tsx src/front-components/sync-tracker.tsx
import { defineFrontComponent } from 'twenty-sdk/define';
-import { useRecordId, enqueueSnackbar } from 'twenty-sdk/front-component';
+import { useSelectedRecordIds, enqueueSnackbar } from 'twenty-sdk/front-component';
import { useEffect } from 'react';
const SyncTracker = () => {
- const recordId = useRecordId();
+ const [recordId] = useSelectedRecordIds();
useEffect(() => {
enqueueSnackbar({ message: `Tracking record ${recordId}`, variant: 'info' });
@@ -116,7 +115,7 @@ Da die Komponente `null` zurückgibt, überspringt Twenty das Rendern eines Cont
Das Paket `twenty-sdk` stellt vier Command-Hilfskomponenten bereit, die für Headless-Front-Komponenten ausgelegt sind. Jede Komponente führt beim Mounten eine Aktion aus, behandelt Fehler durch Anzeige einer Snackbar-Benachrichtigung und unmountet die Front-Komponente nach Abschluss automatisch.
-Importieren Sie sie aus `twenty-sdk/command`:
+Importieren Sie sie aus `twenty-sdk/front-component`:
* **`Command`** — Führt einen asynchronen Callback über das Prop `execute` aus.
* **`CommandLink`** — Navigiert zu einem App-Pfad. Props: `to`, `params`, `queryParams`, `options`.
@@ -127,8 +126,8 @@ Hier ist ein vollständiges Beispiel einer Headless-Front-Komponente, die `Comma
```tsx src/front-components/run-action.tsx
import { defineFrontComponent } from 'twenty-sdk/define';
-import { Command } from 'twenty-sdk/command';
-import { CoreApiClient } from 'twenty-sdk/clients';
+import { Command } from 'twenty-sdk/front-component';
+import { CoreApiClient } from 'twenty-client-sdk/core';
const RunAction = () => {
const execute = async () => {
@@ -160,7 +159,6 @@ import { defineCommandMenuItem } from 'twenty-sdk/define';
export default defineCommandMenuItem({
universalIdentifier: 'f6a7b8c9-d0e1-2345-fabc-456789012345',
label: 'Run my action',
- icon: 'IconPlayerPlay',
frontComponentUniversalIdentifier: 'e5f6a7b8-c9d0-1234-efab-345678901234',
});
```
@@ -169,7 +167,7 @@ Und ein Beispiel, das `CommandModal` verwendet, um vor der Ausführung um Bestä
```tsx src/front-components/delete-draft.tsx
import { defineFrontComponent } from 'twenty-sdk/define';
-import { CommandModal } from 'twenty-sdk/command';
+import { CommandModal } from 'twenty-sdk/front-component';
const DeleteDraft = () => {
const execute = async () => {
@@ -202,7 +200,7 @@ Front-Komponenten laufen browserseitig in einem isolierten Web Worker, während
Eine mit `httpRouteTriggerSettings` deklarierte Logikfunktion ist über HTTP unter ihrem Routenpfad erreichbar. Twenty injiziert die Basis-URL, unter der deine Funktionen bereitgestellt werden, als `TWENTY_FUNCTIONS_URL` in den Worker, zusammen mit dem `TWENTY_APP_ACCESS_TOKEN`, das den Aufruf authentifiziert. Es gibt noch keinen eigenen SDK-Client zum Aufrufen deiner eigenen Funktionen, daher rufe sie mit einem einfachen `fetch` auf:
-> **In Twenty Cloud werden HTTP-ausgelöste Logikfunktionen auf einer eigenen, arbeitsbereichsspezifischen Domain bereitgestellt** unter `https://\
.twenty.com\` — genau darauf verweist `TWENTY_FUNCTIONS_URL`. Für externe Aufrufer kopiere die exakte URL aus den **HTTP trigger**-Einstellungen der Funktion oder aus dem **Settings**-Tab der Anwendung.
+> **In Twenty Cloud werden HTTP-ausgelöste Logikfunktionen auf einer eigenen, arbeitsbereichsspezifischen Domain bereitgestellt** unter `https://\.withtwenty.com\` — genau darauf verweist `TWENTY_FUNCTIONS_URL`. Für externe Aufrufer kopiere die exakte URL aus den **HTTP trigger**-Einstellungen der Funktion oder aus dem **Settings**-Tab der Anwendung.
Die `/s/`-Funktionsroute ist **veraltet** und wird **am 2026-07-24 deaktiviert**. Verwende stattdessen `TWENTY_FUNCTIONS_URL` (oben) und migriere alle hart codierten `/s/`-URLs vor diesem Datum. Die `/s/`-Route bleibt für Self-Hosting verfügbar.
@@ -212,7 +210,7 @@ Eine headless Front-Komponente kann den Aufruf beim Mounten über die `Command`-
```tsx src/front-components/sync-prs.tsx
import { defineFrontComponent } from 'twenty-sdk/define';
-import { Command } from 'twenty-sdk/command';
+import { Command } from 'twenty-sdk/front-component';
const SyncPrs = () => {
const execute = async () => {
@@ -316,13 +314,13 @@ Verwenden Sie innerhalb Ihrer Komponente SDK-Hooks, um auf den aktuellen Benutze
import { defineFrontComponent } from 'twenty-sdk/define';
import {
useUserId,
- useRecordId,
+ useSelectedRecordIds,
useFrontComponentId,
} from 'twenty-sdk/front-component';
const RecordInfo = () => {
const userId = useUserId();
- const recordId = useRecordId();
+ const [recordId] = useSelectedRecordIds();
const componentId = useFrontComponentId();
return (
@@ -405,12 +403,11 @@ Hier ist ein Beispiel, das die Host-API verwendet, um nach Abschluss einer Aktio
```tsx src/front-components/archive-record.tsx
import { defineFrontComponent } from 'twenty-sdk/define';
-import { useRecordId } from 'twenty-sdk/front-component';
-import { enqueueSnackbar, closeSidePanel } from 'twenty-sdk/front-component';
-import { CoreApiClient } from 'twenty-sdk/clients';
+import { enqueueSnackbar, closeSidePanel, useSelectedRecordIds } from 'twenty-sdk/front-component';
+import { CoreApiClient } from 'twenty-client-sdk/core';
const ArchiveRecord = () => {
- const recordId = useRecordId();
+ const [recordId] = useSelectedRecordIds();
const handleArchive = async () => {
const client = new CoreApiClient();
@@ -451,10 +448,10 @@ export default defineFrontComponent({
Verwenden Sie `useSelectedRecordIds()`, um mehrere ausgewählte Datensätze zu verwalten. Dies ist nützlich für Stapelvorgänge:
```tsx src/front-components/bulk-export.tsx
-import { defineFrontComponent, numberOfSelectedRecords } from 'twenty-sdk/define';
+import { defineFrontComponent } from 'twenty-sdk/define';
import { useSelectedRecordIds } from 'twenty-sdk/front-component';
import { enqueueSnackbar, closeSidePanel } from 'twenty-sdk/front-component';
-import { CoreApiClient } from 'twenty-sdk/clients';
+import { CoreApiClient } from 'twenty-client-sdk/core';
const BulkExport = () => {
const selectedRecordIds = useSelectedRecordIds();
@@ -492,12 +489,19 @@ export default defineFrontComponent({
name: 'bulk-export',
description: 'Export selected records',
component: BulkExport,
- command: {
- universalIdentifier: 'd0e1f2a3-b4c5-6789-defa-012345678902',
- label: 'Bulk Export',
- availabilityType: 'RECORD_SELECTION',
- conditionalAvailabilityExpression: numberOfSelectedRecords > 0,
- },
+});
+```
+
+Stellen Sie sie mit einem auf Datensatzauswahlen beschränkten [Befehlmenüeintrag](/l/de/developers/extend/apps/layout/command-menu-items) bereit:
+
+```ts src/command-menu-items/bulk-export.command-menu-item.ts
+import { defineCommandMenuItem } from 'twenty-sdk/define';
+
+export default defineCommandMenuItem({
+ universalIdentifier: 'd0e1f2a3-b4c5-6789-defa-012345678902',
+ label: 'Bulk Export',
+ availabilityType: 'RECORD_SELECTION',
+ frontComponentUniversalIdentifier: 'd0e1f2a3-b4c5-6789-defa-012345678901',
});
```
diff --git a/packages/twenty-docs/l/de/developers/extend/apps/layout/navigation-menu-items.mdx b/packages/twenty-docs/l/de/developers/extend/apps/layout/navigation-menu-items.mdx
index e81f72c8cf..bd5ea53e79 100644
--- a/packages/twenty-docs/l/de/developers/extend/apps/layout/navigation-menu-items.mdx
+++ b/packages/twenty-docs/l/de/developers/extend/apps/layout/navigation-menu-items.mdx
@@ -35,6 +35,8 @@ export default defineNavigationMenuItem({
* `position` steuert die Reihenfolge in der Seitenleiste.
+* Das Enum enthält außerdem `NavigationMenuItemType.RECORD`, das intern für vom Benutzer erstellte Datensatzfavoriten verwendet wird — es ist in einem App-Manifest nicht verwendbar (es gibt kein Feld, um auf einen Datensatz zu verweisen).
+
* `icon` und `color` sind optional und passen das Erscheinungsbild des Eintrags an.
* `folderUniversalIdentifier` ist ebenfalls bei jedem Eintrag verfügbar, um ihn innerhalb eines übergeordneten Elements vom Typ `FOLDER` zu verschachteln.
diff --git a/packages/twenty-docs/l/de/developers/extend/apps/layout/views.mdx b/packages/twenty-docs/l/de/developers/extend/apps/layout/views.mdx
index 64942099ef..451614e995 100644
--- a/packages/twenty-docs/l/de/developers/extend/apps/layout/views.mdx
+++ b/packages/twenty-docs/l/de/developers/extend/apps/layout/views.mdx
@@ -33,17 +33,32 @@ export default defineView({
## Hauptpunkte
* `objectUniversalIdentifier` gibt an, auf welches Objekt diese Ansicht angewendet wird. Es kann sich um ein von Ihnen definiertes benutzerdefiniertes Objekt oder ein Standardobjekt von Twenty handeln.
-* `key` bestimmt den Ansichtstyp – `ViewKey.INDEX` ist die Hauptlistenansicht für das Objekt.
+* `key: ViewKey.INDEX` markiert die Ansicht als die Hauptlistenansicht des Objekts (diejenige, die ein `OBJECT`-Navigationselement öffnet).
* `fields` steuert, welche Spalten erscheinen und in welcher Reihenfolge. Jedes Feld referenziert einen `fieldMetadataUniversalIdentifier`.
-* Für erweiterte Konfigurationen können Sie außerdem `filters`, `filterGroups`, `groups` und `fieldGroups` deklarieren.
+* Für erweiterte Konfigurationen können Sie außerdem `filters`, `filterGroups`, `sorts`, `groups` und `fieldGroups` deklarieren.
* `position` steuert die Reihenfolge, wenn mehrere Ansichten für dasselbe Objekt existieren.
+## Optionale Eigenschaften
+
+| Eigenschaft | Werte | Beschreibung |
+| ----------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
+| `type` | `ViewType.TABLE` (Standard), `ViewType.KANBAN`, `ViewType.CALENDAR` | Wie Datensätze angeordnet werden. (`FIELDS_WIDGET` / `TABLE_WIDGET` existieren ebenfalls, werden aber intern von Page-Layout-Widgets verwendet.) |
+| `visibility` | `ViewVisibility.WORKSPACE` (Standard), `ViewVisibility.UNLISTED` | Ob die Ansicht für den gesamten Workspace aufgelistet oder in Auswahlelementen verborgen ist. |
+| `openRecordIn` | `ViewOpenRecordIn.SIDE_PANEL` (Standard), `ViewOpenRecordIn.RECORD_PAGE` | Wo ein Klick auf einen Datensatz diesen öffnet. |
+| `sortierungen` | `{ fieldMetadataUniversalIdentifier, direction: ViewSortDirection.ASC \| DESC }[]` | Standard-Sortierreihenfolge. |
+| `isCompact` | `boolean` | Kompakte Zeilenanzeige. |
+| `mainGroupByFieldMetadataUniversalIdentifier` + `shouldHideEmptyGroups` | — | Datensätze (z. B. Kanban-Spalten) nach einem Feld gruppieren. |
+| `kanbanAggregateOperation`, `kanbanAggregateOperationFieldMetadataUniversalIdentifier`, `kanbanColumnWidth` | `AggregateOperations.*` | Aggregationen und Größen von Kanban-Spalten. |
+| `calendarLayout`, `calendarFieldMetadataUniversalIdentifier` | `ViewCalendarLayout.DAY` / `WEEK` / `MONTH` | Kalenderansichten: Layout und das Datumsfeld, das die Position der Datensätze bestimmt. |
+
+Alle oben genannten Enums werden aus `twenty-sdk/define` exportiert.
+
## Filter
Eine Ansicht kann mit vorab angewendeten Filtern ausgeliefert werden. Jeder Filter hat drei Koordinaten: das **Feld**, das gefiltert wird, der **Operand** (wie verglichen wird) und der **Wert** (womit verglichen wird). Alle drei müssen übereinstimmen — die Verwendung eines Operanden, der nicht auf einen Feldtyp anwendbar ist, wird bei der Synchronisierung zurückgewiesen.
```ts
-import { ViewFilterOperand } from 'twenty-shared/types';
+import { ViewFilterOperand } from 'twenty-sdk/define';
filters: [
{
diff --git a/packages/twenty-docs/l/de/developers/extend/apps/logic/logic-functions.mdx b/packages/twenty-docs/l/de/developers/extend/apps/logic/logic-functions.mdx
index 8acd4092d3..97799810dc 100644
--- a/packages/twenty-docs/l/de/developers/extend/apps/logic/logic-functions.mdx
+++ b/packages/twenty-docs/l/de/developers/extend/apps/logic/logic-functions.mdx
@@ -51,8 +51,12 @@ export default defineLogicFunction({
```
Verfügbare Trigger-Typen:
-* **httpRoute**: Stellt Ihre Funktion unter einem HTTP-Pfad und einer Methode **unter dem Endpunkt `/s/`** bereit:
-> z. B. `path: '/post-card/create'` ist unter `https://your-twenty-server.com/s/post-card/create` aufrufbar
+* **httpRoute**: Enthält deine Funktion auf einem HTTP-Pfad und -Methode an der **Funktions-Basis-URL deines Arbeitsbereichs** — der Wert 20 Injekte als `TWENTY_FUNCTIONS_URL` (auf 20 Cloud, eine dedizierte Domain pro Arbeitsbereich):
+> z. B. `path: '/post-card/create'` ist unter `https://your-workspace.withtwenty.com/post-card/create` aufrufbar
+
+
+Die alte `/s/` Präfix Route (`https://your-twenty-server.com/s/post-card/create`) ist **veraltet in 20 Cloud** und wird auf **2026-07-24** deaktiviert. Es bleibt für selbstgehostete und lokale Instanzen verfügbar, die keine isolierte Funktionsdomain konfigurieren — benutze `TWENTY_FUNCTIONS_URL` wenn diese gesetzt ist und zurück fallen auf `\/s/\` sonst nicht.
+
Um eine routenausgelöste Logikfunktion von einer (headless) Front-Komponente aus aufzurufen, siehe [Aufrufen einer Logikfunktion](/l/de/developers/extend/apps/layout/front-components#calling-a-logic-function).
diff --git a/packages/twenty-docs/l/de/developers/extend/apps/logic/overview.mdx b/packages/twenty-docs/l/de/developers/extend/apps/logic/overview.mdx
index bf8d8287d6..66b7ac8fac 100644
--- a/packages/twenty-docs/l/de/developers/extend/apps/logic/overview.mdx
+++ b/packages/twenty-docs/l/de/developers/extend/apps/logic/overview.mdx
@@ -42,7 +42,7 @@ Eine Logikfunktion wählt einen oder mehrere Auslöser – jeder Eintrag unten i
| Auslöser | Wann sie ausgeführt wird | Einstellung |
| --------------------- | ------------------------------------------------------------------ | ------------------------------- |
-| **HTTP-Route** | Eine Anfrage erreicht Ihren `/s/\`-Endpunkt | `httpRouteTriggerSettings` |
+| **HTTP-Route** | Eine Anfrage trifft die öffentliche URL Ihrer Funktion | `httpRouteTriggerSettings` |
| **Cron** | Ein CRON-Ausdruck trifft zu | `cronTriggerSettings` |
| **Datenbankereignis** | Ein Workspace-Datensatz wird erstellt, aktualisiert oder gelöscht | `databaseEventTriggerSettings` |
| **KI-Tool** | Eine Twenty-KI-Funktion entscheidet sich, Ihre Funktion aufzurufen | `toolTriggerSettings` |
diff --git a/packages/twenty-docs/l/de/developers/extend/apps/operations/cli.mdx b/packages/twenty-docs/l/de/developers/extend/apps/operations/cli.mdx
index 22cc7333a9..73e1c9fae4 100644
--- a/packages/twenty-docs/l/de/developers/extend/apps/operations/cli.mdx
+++ b/packages/twenty-docs/l/de/developers/extend/apps/operations/cli.mdx
@@ -4,7 +4,25 @@ description: yarn twenty Befehle zum Ausführen von Funktionen, Streamen von Log
icon: terminal
---
-Zusätzlich zu `dev`, `dev:build`, `dev:add` und `dev:typecheck` bietet die `yarn twenty` CLI Befehle zum Ausführen von Funktionen, Anzeigen von Logs und Verwalten von App-Installationen.
+Die `yarn twenty` CLI ist Ihre Schnittstelle für alles, was mit der App zu tun hat. Vollständige Befehlsliste:
+
+| Befehl | Was es tut | Dokumentiert in |
+| ----------------------------------------------- | ---------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- |
+| `dev` | Überwacht Ihre Quelldateien und synchronisiert Änderungen in Echtzeit | [Schnellstart](/l/de/developers/extend/apps/getting-started/quick-start) |
+| `plan` | Metadatenänderungen anzeigen, ohne sie anzuwenden | [Synchronisierung & Wiederherstellung](/l/de/developers/extend/apps/operations/sync-and-recovery#previewing-changes-plan) |
+| `apply` | Metadatenänderungen anwenden, nachdem der Plan angezeigt wurde | [Synchronisierung & Wiederherstellung](/l/de/developers/extend/apps/operations/sync-and-recovery) |
+| `dev:build` | Die App kompilieren und den API-Client generieren (`--tarball`, um ein `.tgz` zu packen) | [Veröffentlichen](/l/de/developers/extend/apps/operations/publishing) |
+| `dev:typecheck` | TypeScript-Typprüfung ausführen | [Tests](/l/de/developers/extend/apps/operations/testing) |
+| `dev:add` | Eine neue Entität erstellen (Scaffolding) | [Scaffolding](/l/de/developers/extend/apps/getting-started/scaffolding) |
+| `dev:generate-client` | Den typisierten API-Client erneut generieren | diese Seite |
+| `dev:function:exec` / `dev:function:logs` | Funktionen ausführen und ihre Protokolle streamen | diese Seite |
+| `dev:translations-extract` | Übersetzbare Zeichenketten in `locales/`-Kataloge extrahieren | [Übersetzungen](/l/de/developers/extend/apps/translations/overview) |
+| `dev:catalog-sync` | Eine Synchronisierung des Marktplatzkatalogs auslösen | [Veröffentlichen](/l/de/developers/extend/apps/operations/publishing#how-marketplace-discovery-works) |
+| `app:publish` / `app:install` / `app:uninstall` | Release-Lebenszyklus | [Veröffentlichen](/l/de/developers/extend/apps/operations/publishing) und diese Seite |
+| `docker:*` | Den lokalen Twenty-Server-Container verwalten | [Lokaler Server](/l/de/developers/extend/apps/getting-started/local-server) |
+| `remote:*` | Serververbindungen verwalten | diese Seite |
+
+Jeder Befehl akzeptiert `-r, --remote \`, um ein bestimmtes Remote statt des Standard-Remotes anzusteuern.
## Funktionen ausführen (`yarn twenty dev:function:exec`)
@@ -20,8 +38,9 @@ yarn twenty dev:function:exec -u e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf
# Pass a JSON payload
yarn twenty dev:function:exec -n create-new-post-card -p '{"name": "Hello"}'
-# Execute the post-install function
+# Execute the install hooks
yarn twenty dev:function:exec --postInstall
+yarn twenty dev:function:exec --preInstall
```
## Funktionsprotokolle ansehen (`yarn twenty dev:function:logs`)
@@ -100,6 +119,12 @@ yarn twenty remote:list
# Set the active remote
yarn twenty remote:use
+
+# Check that the active remote's authentication is still valid
+yarn twenty remote:status
+
+# Remove a remote
+yarn twenty remote:remove
```
Ihre Anmeldedaten werden in `~/.twenty/config.json` gespeichert.
diff --git a/packages/twenty-docs/l/de/developers/extend/apps/operations/publishing.mdx b/packages/twenty-docs/l/de/developers/extend/apps/operations/publishing.mdx
index da012a7d19..884be8af2b 100644
--- a/packages/twenty-docs/l/de/developers/extend/apps/operations/publishing.mdx
+++ b/packages/twenty-docs/l/de/developers/extend/apps/operations/publishing.mdx
@@ -229,7 +229,7 @@ yarn twenty dev:catalog-sync
# yarn twenty dev:catalog-sync --remote production
```
-Die im Marktplatz angezeigten Metadaten stammen aus Ihrer `defineApplication()`-Konfiguration — Felder wie `displayName`, `description`, `author`, `category`, `logoUrl`, `screenshots`, `aboutDescription`, `websiteUrl` und `termsUrl`.
+Die im Marketplace angezeigten Metadaten stammen aus deiner `defineApplication()`-Konfiguration – siehe oben unter [Marketplace-Metadaten](#marketplace-metadata).
Wenn Ihre App keine `aboutDescription` in `defineApplication()` definiert, verwendet der Marktplatz automatisch die `README.md` Ihres Pakets von npm als Inhalt der Über-uns-Seite. Das bedeutet, dass Sie eine einzige README sowohl für npm als auch für den Twenty-Marktplatz pflegen können. Wenn Sie im Marktplatz eine andere Beschreibung möchten, setzen Sie `aboutDescription` explizit.
diff --git a/packages/twenty-docs/l/de/developers/extend/apps/operations/sync-and-recovery.mdx b/packages/twenty-docs/l/de/developers/extend/apps/operations/sync-and-recovery.mdx
index 238d6454b2..78b40d2c26 100644
--- a/packages/twenty-docs/l/de/developers/extend/apps/operations/sync-and-recovery.mdx
+++ b/packages/twenty-docs/l/de/developers/extend/apps/operations/sync-and-recovery.mdx
@@ -12,16 +12,20 @@ Die lokale App-Entwicklung dreht sich um **Syncing**: Die CLI baut Ihr Manifest
Für die tägliche lokale Iteration sollten Sie fast immer `yarn twenty dev` verwenden. Bereitstellen und Veröffentlichen sind zum Ausliefern von Releases gedacht, **nicht** für den lokalen Entwicklungszyklus.
-| Sie möchten … | Befehl | Notizen |
-| ------------------------------------------------------- | ----------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
-| Lokal mit Live-Sync iterieren | `yarn twenty dev` | Überwacht Ihre Dateien und synchronisiert bei jeder Änderung. |
-| Einmal synchronisieren und beenden (CI, Skripte, Hooks) | `yarn twenty dev --once` | Führt einen Build und einen Sync aus und beendet sich anschließend. |
-| Änderungen **anzeigen, ohne sie anzuwenden** | `yarn twenty dev --once --dry-run` | Berechnet und druckt das Diff; schreibt nichts. |
-| Die App aus dem Workspace entfernen | `yarn twenty app:uninstall` | Fügen Sie `--yes` hinzu, um die Abfrage zu überspringen. |
-| Einen Tarball an einen Server ausliefern | `yarn twenty app:publish --private` | Erfordert eine strikt höhere `package.json`-Version – siehe [Veröffentlichen](/l/de/developers/extend/apps/operations/publishing). |
-| Im Marketplace (npm) veröffentlichen | `yarn twenty app:publish` | — |
-| Eine bereitgestellte Version installieren/aktualisieren | `yarn twenty app:install` | Installiert die aktuell bereitgestellte Version. |
-| Den lokalen Server zurücksetzen und sauber neu starten | `yarn twenty docker:reset` | Löscht **alle** lokalen Daten – letztes Mittel. |
+| Sie möchten … | Befehl | Notizen |
+| ------------------------------------------------------- | ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| Lokal mit Live-Sync iterieren | `yarn twenty dev` | Überwacht Ihre Dateien und synchronisiert bei jeder Änderung. |
+| Einmal synchronisieren und beenden (CI, Skripte, Hooks) | `yarn twenty apply` | Führt einen Build und einen Sync aus und beendet sich anschließend. Fügen Sie `--force` hinzu, um die Bestätigung für destruktive Änderungen zu überspringen. |
+| Änderungen **anzeigen, ohne sie anzuwenden** | `yarn twenty plan` | Berechnet und druckt das Diff; schreibt nichts. |
+| Die App aus dem Workspace entfernen | `yarn twenty app:uninstall` | Fügen Sie `--yes` hinzu, um die Abfrage zu überspringen. |
+| Einen Tarball an einen Server ausliefern | `yarn twenty app:publish --private` | Erfordert eine strikt höhere `package.json`-Version – siehe [Veröffentlichen](/l/de/developers/extend/apps/operations/publishing). |
+| Im Marketplace (npm) veröffentlichen | `yarn twenty app:publish` | — |
+| Eine bereitgestellte Version installieren/aktualisieren | `yarn twenty app:install` | Installiert die aktuell bereitgestellte Version. |
+| Den lokalen Server zurücksetzen und sauber neu starten | `yarn twenty docker:reset` | Löscht **alle** lokalen Daten – letztes Mittel. |
+
+
+`yarn twenty dev --once` und `yarn twenty dev --once --dry-run` funktionieren weiterhin als veraltete Aliase für `yarn twenty apply` und `yarn twenty plan`.
+
### Lokaler Sync benötigt keinen Versionssprung
@@ -29,19 +33,26 @@ Die strikt steigende `version`-Regel (`VERSION_ALREADY_EXISTS` beim Deploy, `APP
## Die Sync-Ausgabe lesen
-Jeder Sync gibt die Metadatenänderungen aus, die er angewendet hat (oder anwenden würde, mit `--dry-run`):
+Jeder Sync gibt die Metadatenänderungen aus, die angewendet wurden (oder angewendet würden, mit `plan`), im Terraform-Stil – ein Block pro Entity mit ihren Attributen, danach eine zusammenfassende Zeile:
```text filename="Terminal"
-Metadata changes: 2 created, 1 updated, 1 deleted
- created objectMetadata rocket
- created fieldMetadata timelineActivities
- updated fieldMetadata launchedAt
- deleted pageLayout legacyTab
-✓ Synced
+ # objectMetadata "rocket" will be created
+ + icon = "IconRocket"
+ + labelSingular = "Rocket"
+ + ...
+
+ # fieldMetadata "launchedAt" will be updated
+ ~ isNullable = false -> true
+
+Plan: 2 to add, 1 to change, 1 to destroy.
+
+✓ Synced My App (4 files)
```
Dies ist Ihre erste Diagnose: Sie zeigt Ihnen genau, welche Objekte, Felder und Layouts sich geändert haben, sodass Sie bestätigen können, dass ein Sync das Erwartete getan hat, bevor Sie die UI prüfen.
+Destruktive Änderungen (`to destroy`) werden zusammen mit dem, was sie entfernen, aufgeführt (z. B. `objectMetadata "auditNote" — drops the table and all its rows`) und erfordern eine interaktive Bestätigung oder `--force` in Skripten.
+
Wenn ein Sync bei einer einzelnen Entität fehlschlägt, nennt der Fehler die betreffende Entität und ihren `universalIdentifier`, zum Beispiel:
```text
@@ -50,39 +61,42 @@ Migration action 'create' for 'fieldMetadata' (universalIdentifier: 2020...4337)
Verwenden Sie diesen Bezeichner, um die Entität in Ihrem Manifest (und bei Bedarf im Workspace) zu finden, anstatt zu raten, welche in Konflikt steht.
-## Änderungen vorab ansehen (Dry Run)
+## Änderungen vorab ansehen (Plan)
-`yarn twenty dev --once --dry-run` baut Ihr Manifest, fragt den Server nach dem Migrationsplan und gibt ihn aus – **ohne irgendetwas anzuwenden**. Dies ist der sichere Weg, um zu beantworten: "Was würde dieser Sync ändern?", bevor Sie sich darauf festlegen.
+`yarn twenty plan` baut Ihr Manifest, fragt den Server nach dem Migrationsplan und gibt ihn aus – **ohne irgendetwas anzuwenden**. Dies ist der sichere Weg, um zu beantworten: "Was würde dieser Sync ändern?", bevor Sie sich darauf festlegen.
```bash filename="Terminal"
-yarn twenty dev --once --dry-run
+yarn twenty plan
```
```text filename="Terminal"
Building manifest...
-Computing metadata diff (dry run, nothing will be applied)...
-Metadata changes: 1 created, 1 updated
- created fieldMetadata timelineActivities
- updated objectMetadata rocket
-✓ Dry run complete for My App — no changes were applied
+Computing metadata plan (read-only, nothing will be applied)...
+
+ # fieldMetadata "timelineActivities" will be created
+ + ...
+
+Plan: 1 to add, 1 to change, 0 to destroy.
+
+✓ Plan complete for My App — no changes were applied
```
-Ein Dry Run:
+Ein Plan:
* **Schreibt nichts** – keine Metadatenmigration, kein Update von App-Einträgen, keine Änderungen an Standardrollen/-Tabs und keine API-Client-Generierung.
* Liefert dasselbe **Diff**, das ein echter Sync anwenden würde, sodass Sie erstellte/aktualisierte/gelöschte Entitäten im Voraus prüfen können.
* Ist nützlich vor einer riskanten Änderung, bei der Überprüfung einer KI-generierten Änderung oder in einem Skript, das fehlschlagen soll, wenn eine unerwartete Änderung kurz vor der Anwendung steht.
-Ein Dry Run zeigt nur **Metadaten**-Änderungen an und erfordert, dass die App mindestens einmal synchronisiert wurde (damit der Workspace sie kennt). Wenn Sie ihn gegen eine App ausführen, die noch nie synchronisiert wurde, meldet der Server, dass die App nicht installiert ist – führen Sie zuerst einmal `yarn twenty dev` aus.
+Ein Plan zeigt nur **Metadaten**-Änderungen an und erfordert, dass die App mindestens einmal synchronisiert wurde (damit der Workspace sie kennt). Wenn Sie ihn gegen eine App ausführen, die noch nie synchronisiert wurde, meldet der Server, dass die App nicht installiert ist – führen Sie zuerst einmal `yarn twenty dev` aus.
## Wiederherstellungsleiter
Wenn lokale Metadaten falsch aussehen, eskalieren Sie in dieser Reihenfolge und stoppen Sie, sobald Sie nicht mehr blockiert sind. Jeder Schritt ist störender als der vorherige.
-1. **Erneut synchronisieren.** Führen Sie `yarn twenty dev --once` erneut aus. Syncs sind idempotent – das erneute Ausführen eines sauberen Manifests ist sicher und löst oft eine vorübergehende Störung.
-2. **Plan ansehen.** Führen Sie `yarn twenty dev --once --dry-run` aus, um genau zu sehen, was der nächste Sync zu ändern beabsichtigt, ohne es anzuwenden.
+1. **Erneut synchronisieren.** Führen Sie `yarn twenty apply` erneut aus. Syncs sind idempotent – das erneute Ausführen eines sauberen Manifests ist sicher und löst oft eine vorübergehende Störung.
+2. **Plan ansehen.** Führen Sie `yarn twenty plan` aus, um genau zu sehen, was der nächste Sync zu ändern beabsichtigt, ohne es anzuwenden.
3. **Benannten Fehler lesen.** Wenn ein Sync fehlschlägt, notieren Sie sich den Metadatentyp und den `universalIdentifier` in der Meldung (siehe oben) und lokalisieren Sie diese Entität in Ihrem Manifest. Ein Konflikt weist in der Regel auf einen doppelten oder wiederverwendeten Bezeichner hin.
4. **Deinstallieren und neu installieren.** `yarn twenty app:uninstall`, dann erneut synchronisieren (`yarn twenty dev`). Dies baut die Metadaten der App aus einem sauberen Zustand wieder auf, während der Rest Ihres Workspaces intakt bleibt.
5. **Vollständiger Reset (letztes Mittel).** `yarn twenty docker:reset`, dann erneut seeden und synchronisieren.
diff --git a/packages/twenty-docs/l/de/developers/extend/apps/operations/testing.mdx b/packages/twenty-docs/l/de/developers/extend/apps/operations/testing.mdx
index b9c35d5d80..b38f275f6c 100644
--- a/packages/twenty-docs/l/de/developers/extend/apps/operations/testing.mdx
+++ b/packages/twenty-docs/l/de/developers/extend/apps/operations/testing.mdx
@@ -78,6 +78,13 @@ Erstellen Sie eine `vitest.config.ts` im Stammverzeichnis Ihrer App:
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 ?? '';
+
+// 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({
@@ -88,66 +95,74 @@ export default defineConfig({
test: {
testTimeout: 120_000,
hookTimeout: 120_000,
+ fileParallelism: false,
include: ['src/**/*.integration-test.ts'],
- setupFiles: ['src/__tests__/setup-test.ts'],
+ globalSetup: ['src/__tests__/global-setup.ts'],
env: {
- TWENTY_API_URL: 'http://localhost:2020',
- TWENTY_API_KEY: 'your-api-key',
+ TWENTY_API_URL,
+ TWENTY_API_KEY,
},
},
});
```
-Erstellen Sie eine Setup-Datei, die vor dem Testlauf überprüft, dass der Server erreichbar ist:
+Erstellen Sie eine globale Setup-Datei, die überprüft, ob der Server erreichbar ist, eine Testkonfiguration für das SDK schreibt (`~/.twenty/config.test.json`) und die App synchronisiert, bevor die Tests ausgeführt werden:
-```ts src/__tests__/setup-test.ts
+```ts src/__tests__/global-setup.ts
import * as fs from 'fs';
import * as os from 'os';
import * as path from 'path';
-import { beforeAll } from 'vitest';
-const TWENTY_API_URL = process.env.TWENTY_API_URL ?? 'http://localhost:2020';
-const TEST_CONFIG_DIR = path.join(os.tmpdir(), '.twenty-sdk-test');
+import { appDevOnce, appUninstall } from 'twenty-sdk/cli';
+
+const APP_PATH = process.cwd();
+const CONFIG_DIR = path.join(os.homedir(), '.twenty');
+
+export async function setup() {
+ const apiUrl = process.env.TWENTY_API_URL!;
+ const apiKey = process.env.TWENTY_API_KEY!;
-beforeAll(async () => {
// Verify the server is running
- const response = await fetch(`${TWENTY_API_URL}/healthz`);
-
+ const response = await fetch(`${apiUrl}/healthz`);
if (!response.ok) {
- throw new Error(
- `Twenty server is not reachable at ${TWENTY_API_URL}. ` +
- 'Start the server before running integration tests.',
- );
+ throw new Error(`Twenty server is not reachable at ${apiUrl}.`);
}
- // Write a temporary config for the SDK
- fs.mkdirSync(TEST_CONFIG_DIR, { recursive: true });
-
+ // Write the SDK's test config (the CLI reads config.test.json when NODE_ENV=test)
+ fs.mkdirSync(CONFIG_DIR, { recursive: true });
fs.writeFileSync(
- path.join(TEST_CONFIG_DIR, 'config.json'),
+ path.join(CONFIG_DIR, 'config.test.json'),
JSON.stringify({
- remotes: {
- local: {
- apiUrl: process.env.TWENTY_API_URL,
- apiKey: process.env.TWENTY_API_KEY,
- },
- },
+ remotes: { local: { apiUrl, apiKey } },
defaultRemote: 'local',
}, null, 2),
);
-});
+
+ // Start from a clean slate, then sync the app
+ await appUninstall({ appPath: APP_PATH }).catch(() => {});
+
+ const result = await appDevOnce({ appPath: APP_PATH });
+ if (!result.success) {
+ throw new Error(`Dev sync failed: ${result.error?.message}`);
+ }
+}
+
+export async function teardown() {
+ await appUninstall({ appPath: APP_PATH });
+}
```
## Programmgesteuerte SDK-APIs
Der Subpfad `twenty-sdk/cli` exportiert Funktionen, die Sie direkt aus Testcode aufrufen können:
-| Funktion | Beschreibung |
-| -------------- | ----------------------------------------------------- |
-| `appBuild` | Die App bauen und optional ein Tarball erstellen |
-| `appDeploy` | Ein Tarball auf den Server hochladen |
-| `appInstall` | Die App im aktiven Arbeitsbereich installieren |
-| `appUninstall` | Die App aus dem aktiven Arbeitsbereich deinstallieren |
+| Funktion | Beschreibung |
+| -------------- | --------------------------------------------------------------------------- |
+| `appBuild` | Die App bauen und optional ein Tarball erstellen |
+| `appDeploy` | Ein Tarball auf den Server hochladen |
+| `appDevOnce` | Erstellt und synchronisiert die App einmal (entspricht `yarn twenty apply`) |
+| `appInstall` | Die App im aktiven Arbeitsbereich installieren |
+| `appUninstall` | Die App aus dem aktiven Arbeitsbereich deinstallieren |
Jede Funktion gibt ein Ergebnisobjekt mit `success: boolean` und entweder `data` oder `error` zurück.
@@ -238,64 +253,10 @@ Sie können die Typprüfung Ihrer App auch ohne Tests ausführen:
yarn twenty dev:typecheck
```
-Dies führt `tsc --noEmit` aus und meldet etwaige Typfehler.
+Dies führt `tsc --noEmit` gegen die `tsconfig.json` Ihrer App aus und meldet etwaige Typfehler. Gerüstete Apps liefern außerdem ein `yarn typecheck`-Skript mit, das auch Testdateien abdeckt (`tsconfig.spec.json`).
## CI mit GitHub Actions
-Das Scaffolding-Tool erzeugt einen einsatzbereiten GitHub-Actions-Workflow in `.github/workflows/ci.yml`. Er führt Ihre Integrationstests automatisch bei jedem Push auf `main` und bei Pull Requests aus.
+Das Scaffolding-Tool erzeugt einen einsatzbereiten Workflow unter `.github/workflows/ci.yml`. Bei jedem Push auf `main` und jeder Pull-Request startet es einen kurzlebigen Twenty-Server im Runner (über die Aktion `twentyhq/twenty/.github/actions/spawn-twenty-app-dev-test`) und führt anschließend `yarn lint`, `yarn typecheck`, `yarn test:unit` und `yarn test` aus, wobei `TWENTY_API_URL` / `TWENTY_API_KEY` auf diesen Server verweisen. Es sind keine Geheimnisse erforderlich, und Sie können die Serverversion über die Umgebungsvariable `TWENTY_VERSION` oben im Workflow fixieren.
-Der Workflow:
-
-1. Checkt Ihren Code aus
-2. Startet einen temporären Twenty-Server mit der Aktion `twentyhq/twenty/.github/actions/spawn-twenty-docker-image`
-3. Installiert Abhängigkeiten mit `yarn install --immutable`
-4. Führt `yarn test` aus, wobei `TWENTY_API_URL` und `TWENTY_API_KEY` aus den Aktionsausgaben injiziert werden.
-
-```yaml .github/workflows/ci.yml
-name: CI
-
-on:
- push:
- branches:
- - main
- pull_request: {}
-
-env:
- TWENTY_VERSION: latest
-
-jobs:
- test:
- runs-on: ubuntu-latest
- steps:
- - name: Checkout
- uses: actions/checkout@v4
-
- - name: Spawn Twenty instance
- id: twenty
- uses: twentyhq/twenty/.github/actions/spawn-twenty-docker-image@main
- with:
- twenty-version: ${{ env.TWENTY_VERSION }}
- github-token: ${{ secrets.GITHUB_TOKEN }}
-
- - name: Enable Corepack
- run: corepack enable
-
- - name: Setup Node.js
- uses: actions/setup-node@v4
- with:
- node-version-file: '.nvmrc'
- cache: 'yarn'
-
- - name: Install dependencies
- run: yarn install --immutable
-
- - name: Run integration tests
- run: yarn test
- env:
- TWENTY_API_URL: ${{ steps.twenty.outputs.server-url }}
- TWENTY_API_KEY: ${{ steps.twenty.outputs.access-token }}
-```
-
-Sie müssen keine Secrets konfigurieren — die Aktion `spawn-twenty-docker-image` startet einen flüchtigen Twenty-Server direkt im Runner und gibt die Verbindungsdetails aus. Das Secret `GITHUB_TOKEN` wird automatisch von GitHub bereitgestellt.
-
-Um eine bestimmte Twenty-Version statt `latest` festzulegen, ändern Sie die Umgebungsvariable `TWENTY_VERSION` oben im Workflow.
+Unter [Veröffentlichen → Automatisiertes CI/CD](/l/de/developers/extend/apps/operations/publishing#automated-cicd-scaffolded-workflows) finden Sie eine vollständige Schritt-für-Schritt-Anleitung zu beiden eingerichteten Workflows (`ci.yml` und der `cd.yml`-Bereitstellungspipeline).
diff --git a/packages/twenty-docs/l/de/developers/extend/apps/tutorials/document-generator/building-the-ui.mdx b/packages/twenty-docs/l/de/developers/extend/apps/tutorials/document-generator/building-the-ui.mdx
index 5b4356436c..e4b7789a27 100644
--- a/packages/twenty-docs/l/de/developers/extend/apps/tutorials/document-generator/building-the-ui.mdx
+++ b/packages/twenty-docs/l/de/developers/extend/apps/tutorials/document-generator/building-the-ui.mdx
@@ -91,9 +91,11 @@ const GenerateDocumentForm = () => {
}, []);
const generate = async () => {
- const apiBaseUrl = process.env.TWENTY_API_URL;
+ // Prefer the injected functions URL; fall back to the legacy /s prefix (self-hosted/local)
+ const functionsBaseUrl =
+ process.env.TWENTY_FUNCTIONS_URL || `${process.env.TWENTY_API_URL}/s`;
const token = process.env.TWENTY_APP_ACCESS_TOKEN ?? process.env.TWENTY_API_KEY;
- const res = await fetch(`${apiBaseUrl}/s/documents/generate`, {
+ const res = await fetch(`${functionsBaseUrl}/documents/generate`, {
method: 'POST',
headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` },
body: JSON.stringify({ templateId, recordId }),
@@ -186,7 +188,9 @@ const DocumentViewer = () => {
const recordId = useFrontComponentExecutionContext((c) => c.recordId ?? null);
// ...load { content, file } for recordId, then derive the links:
const pdfUrl = document.file?.[0]?.url;
- const webUrl = `${process.env.TWENTY_API_URL ?? ''}/s/documents/view?id=${recordId}`;
+ const functionsBaseUrl =
+ process.env.TWENTY_FUNCTIONS_URL || `${process.env.TWENTY_API_URL ?? ''}/s`;
+ const webUrl = `${functionsBaseUrl}/documents/view?id=${recordId}`;
// Render the template body, plus quick links to the web page and the PDF.
// Links open in a new tab so they don't navigate the embedded component.
diff --git a/packages/twenty-docs/l/de/developers/extend/apps/tutorials/document-generator/http-routes.mdx b/packages/twenty-docs/l/de/developers/extend/apps/tutorials/document-generator/http-routes.mdx
index 60fb5ca096..1c8f3efc1c 100644
--- a/packages/twenty-docs/l/de/developers/extend/apps/tutorials/document-generator/http-routes.mdx
+++ b/packages/twenty-docs/l/de/developers/extend/apps/tutorials/document-generator/http-routes.mdx
@@ -9,8 +9,15 @@ Der gleiche Handler kann auch HTTP-Anfragen beantworten. Wir werden zwei Routen
* ein **POST** Endpunkt der UI-Aufrufe, um ein Dokument zu generieren, und
* ein öffentlicher **GET** Endpunkt, der ein Dokument als druckbare Webseite darstellt.
-Beide verwenden `httpRouteTriggerSettings`. App-Routen werden unter `/s` auf Ihrem
-20 Server bedient (z.B. `http://localhost:2020/s/documents/generate`).
+Beide verwenden `httpRouteTriggerSettings`. Auf dem lokalen Dev-Server werden App-Routen
+unter dem Präfix `/s` bedient (z.B. `http://localhost:2020/s/documents/generate`).
+
+
+Bei 20 Cloud werden Routen in der dedizierten Funktion des Arbeitsbereichs, der Domain
+– die URL 20 injiziert als `TWENTY_FUNCTIONS_URL`, ohne `/s` Präfix. Das `/s`
+Präfix ist dort veraltet und bleibt nur für selbstgehostete und lokale Instanzen übrig.
+Siehe [Aufruf einer Logikfunktion](/l/de/developers/extend/apps/layout/front-components#calling-a-logic-function).
+
## POST-Route — bei Bedarf generieren
diff --git a/packages/twenty-docs/l/de/developers/extend/apps/tutorials/document-generator/publishing.mdx b/packages/twenty-docs/l/de/developers/extend/apps/tutorials/document-generator/publishing.mdx
index 31d7bc56e1..786a40bb34 100644
--- a/packages/twenty-docs/l/de/developers/extend/apps/tutorials/document-generator/publishing.mdx
+++ b/packages/twenty-docs/l/de/developers/extend/apps/tutorials/document-generator/publishing.mdx
@@ -77,11 +77,11 @@ Führe die gleichen Tore CI aus:
yarn lint # oxlint
yarn typecheck # tsgo
yarn test:unit # unit tests
-yarn twenty dev --once --dry-run # preview the metadata diff
+yarn twenty plan # preview the metadata diff
```
-Der Trockenlauf druckt genau das, was sich auf dem Server ändern würde, ohne es anzuwenden —
-eine gute abschließende Vernunftprüfung. Siehe
+Der Plan gibt genau aus, was sich auf dem Server ändern würde, ohne die Änderungen anzuwenden —
+eine gute abschließende Plausibilitätsprüfung. Siehe
[Testing](/l/de/developers/extend/apps/operations/testing) und
[Synchronisieren & Wiederherstellen](/l/de/developers/extend/apps/operations/sync-and-recovery).
diff --git a/packages/twenty-docs/l/es/developers/extend/apps/config/install-hooks.mdx b/packages/twenty-docs/l/es/developers/extend/apps/config/install-hooks.mdx
index 6a071a738c..1dac13f569 100644
--- a/packages/twenty-docs/l/es/developers/extend/apps/config/install-hooks.mdx
+++ b/packages/twenty-docs/l/es/developers/extend/apps/config/install-hooks.mdx
@@ -4,9 +4,9 @@ description: "Ejecuta lógica antes o después de la instalación: introduce dat
icon: wrench
---
-Los hooks de instalación son funciones de lógica especiales que se ejecutan durante el ciclo de vida de la instalación o actualización. Comparten el mismo tiempo de ejecución del controlador que las [logic functions](/l/es/developers/extend/apps/logic/logic-functions) normales y reciben un `InstallPayload`, pero se declaran con sus propias funciones de definición — `definePostInstallLogicFunction()` y `definePreInstallLogicFunction()` — y están fuera del modelo de desencadenadores normal (HTTP, cron, eventos de base de datos).
+Los hooks de instalación son funciones de lógica especiales que se ejecutan durante el ciclo de vida de la instalación o actualización. Comparten el mismo tiempo de ejecución del handler que las [logic functions](/l/es/developers/extend/apps/logic/logic-functions) normales y reciben un `InstallPayload` (`{ previousVersion?: string; newVersion: string }` — `previousVersion` es `undefined` en una instalación nueva), pero se declaran con sus propias funciones define y viven fuera del modelo de disparadores normal (HTTP, cron, eventos de base de datos).
-Cada aplicación puede definir **como máximo una función de preinstalación** y **como máximo una función de posinstalación**. La compilación del manifiesto generará un error si se detecta más de una de cualquiera de las dos.
+Cada aplicación puede definir **como máximo una función de preinstalación** y **como máximo una función de posinstalación**. La compilación del manifiesto genera un error si se detecta más de una de cualquiera de las dos.
```
┌─────────────────────────────────────────────────────────────┐
@@ -19,111 +19,59 @@ Cada aplicación puede definir **como máximo una función de preinstalación**
└─────────────────────────────────────────────────────────────┘
```
-
-
+## De un vistazo
-Una función de posinstalación se ejecuta automáticamente una vez que tu aplicación ha terminado de instalarse en un espacio de trabajo. El servidor la ejecuta **después** de que se hayan sincronizado los metadatos de la aplicación y se haya generado el cliente del SDK, de modo que el espacio de trabajo esté completamente listo para usarse y el nuevo esquema esté disponible. Los casos de uso típicos incluyen poblar datos predeterminados, crear registros iniciales, configurar los ajustes del espacio de trabajo o aprovisionar recursos en servicios de terceros.
+| | `definePreInstallLogicFunction` | `definePostInstallLogicFunction` |
+| ---------------- | ---------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| Ejecuciones | Antes de la migración de metadatos — el esquema y los datos **anteriores** siguen intactos | Después de la migración y la generación del SDK — el esquema **nuevo** está en su lugar |
+| Ejecución | Siempre síncrona; bloquea la instalación | Asíncrona de forma predeterminada (en cola, 3 reintentos); modo síncrono opcional mediante `shouldRunSynchronously: true` |
+| En caso de fallo | La instalación se **aborta** antes de cualquier cambio de esquema | Asíncrono: se vuelve a intentar hasta 3 veces. Síncrono: quien realiza la llamada recibe `POST_INSTALL_ERROR` (los cambios de esquema **no** se revierten) |
+| Uso típico | Hacer copia de seguridad o corregir datos que una migración perdería; rechazar una actualización arriesgada lanzando una excepción | Sembrar datos predeterminados, configurar el espacio de trabajo, registrar recursos externos |
-```ts src/logic-functions/post-install.ts
-import { definePostInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define';
+**Regla general:** usa post-install de forma predeterminada. Recurra a la pre-instalación solo cuando la propia migración sea destructiva y necesite interceptar el estado anterior antes de que desaparezca.
-const handler = async (payload: InstallPayload): Promise => {
- console.log('Post install logic function executed successfully!', payload.previousVersion);
-};
+| Quiere... | Usar |
+| ------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------- |
+| Sembrar datos, configurar el espacio de trabajo, registrar recursos externos | `post-install` |
+| Trabajo de larga duración que no debería bloquear la respuesta de instalación | `post-install` (modo asíncrono predeterminado, con reintentos del worker) |
+| Configuración rápida de la que el cliente depende inmediatamente después de que finaliza la instalación | `post-install` con `shouldRunSynchronously: true` |
+| Leer o hacer copia de seguridad de datos que la próxima migración perdería | `pre-install` |
+| Rechazar una actualización que corrompería datos existentes | `pre-install` (lanzar desde el controlador) |
+| Reconciliación en cada actualización | Cualquiera de los hooks con `shouldRunOnVersionUpgrade: true` |
-export default definePostInstallLogicFunction({
- universalIdentifier: 'f7a2b9c1-3d4e-5678-abcd-ef9876543210',
- name: 'post-install',
- description: 'Runs after installation to set up the application.',
- timeoutSeconds: 300,
- shouldRunOnVersionUpgrade: false,
- shouldRunSynchronously: false,
- handler,
-});
-```
+## Comportamiento compartido por ambos hooks
-También puedes ejecutar manualmente la función de posinstalación en cualquier momento usando la CLI:
+* La configuración es una configuración de `defineLogicFunction` menos los ajustes de disparador, más `shouldRunOnVersionUpgrade`.
+* **Cuándo se ejecuta**: solo en instalaciones nuevas, de forma predeterminada. Configura `shouldRunOnVersionUpgrade: true` para que también se ejecute en las actualizaciones. Usa `previousVersion` / `newVersion` para ramificar según la ruta de actualización.
+* **La idempotencia es importante**: el post-install asíncrono puede reintentarse y cualquiera de los hooks se vuelve a ejecutar en las actualizaciones cuando `shouldRunOnVersionUpgrade` está activado.
+* El entorno habitual de las logic functions (`APPLICATION_ID`, `APP_ACCESS_TOKEN`, `API_URL`) se inyecta, por lo que puedes llamar a la API de Twenty con el token de tu app.
+* El hook se adjunta automáticamente al manifiesto de la aplicación en tiempo de compilación (`preInstallLogicFunction` / `postInstallLogicFunction`) — no hay nada que referenciar en [`defineApplication()`](/l/es/developers/extend/apps/config/application).
+* El `timeoutSeconds` predeterminado es 300 para permitir tareas de configuración más largas como la siembra de datos.
+* **No se ejecuta en modo de desarrollo**: `yarn twenty dev` omite el flujo de instalación y sincroniza los archivos directamente, por lo que los hooks nunca se ejecutan ahí. En su lugar, dispáralos manualmente:
```bash filename="Terminal"
yarn twenty dev:function:exec --postInstall
-```
-
-Puntos clave:
-* Las funciones de posinstalación usan `definePostInstallLogicFunction()` — una variante especializada que omite la configuración de desencadenadores (`cronTriggerSettings`, `databaseEventTriggerSettings`, `httpRouteTriggerSettings`, `toolTriggerSettings`, `workflowActionTriggerSettings`).
-* El controlador recibe un `InstallPayload` con `{ previousVersion?: string; newVersion: string }` — `newVersion` es la versión que se está instalando, y `previousVersion` es la versión que se instaló previamente (o `undefined` en una instalación nueva). Use estos valores para distinguir instalaciones nuevas de actualizaciones y para ejecutar lógica de migración específica de la versión.
-* **Cuándo se ejecuta el hook**: solo en instalaciones nuevas, de forma predeterminada. Pase `shouldRunOnVersionUpgrade: true` si también quiere que se ejecute cuando la app se actualice desde una versión anterior. Si se omite, el indicador es `false` por defecto y las actualizaciones omiten el hook.
-* **Modelo de ejecución — asíncrono por defecto, sincronía opcional**: el indicador `shouldRunSynchronously` controla *cómo* se ejecuta la post-instalación.
- * `shouldRunSynchronously: false` *(predeterminado)* — el hook se **encola en la cola de mensajes** con `retryLimit: 3` y se ejecuta de forma asíncrona en un worker. La respuesta de instalación se devuelve tan pronto como el trabajo se encola, por lo que un controlador lento o con fallos no bloquea al solicitante. El worker reintentará hasta tres veces. **Úselo para trabajos de larga duración** — sembrar conjuntos de datos grandes, llamar a APIs de terceros lentas, aprovisionar recursos externos, cualquier cosa que pueda exceder una ventana de respuesta HTTP razonable.
- * `shouldRunSynchronously: true` — el hook se ejecuta **en línea durante el flujo de instalación** (el mismo ejecutor que la pre-instalación). La solicitud de instalación se bloquea hasta que el controlador finaliza y, si arroja una excepción, quien realiza la instalación recibe un `POST_INSTALL_ERROR`. Sin reintentos automáticos. **Úselo para trabajo rápido que debe completarse antes de la respuesta** — por ejemplo, emitir un error de validación al usuario, o una configuración rápida de la que el cliente dependerá inmediatamente después de que regrese la llamada de instalación. Tenga en cuenta que la migración de metadatos ya se ha aplicado cuando se ejecuta la post-instalación, por lo que un fallo en modo síncrono **no** revierte los cambios de esquema — solo expone el error.
-* Asegúrese de que su controlador sea idempotente. En modo asíncrono, la cola puede reintentar hasta tres veces; en cualquier modo, el hook puede ejecutarse de nuevo en las actualizaciones cuando `shouldRunOnVersionUpgrade: true`.
-* Las variables de entorno `APPLICATION_ID`, `APP_ACCESS_TOKEN` y `API_URL` están disponibles dentro del controlador (igual que en cualquier otra función de lógica), por lo que puede llamar a la API de Twenty con un token de acceso de aplicación con alcance a su app.
-* Solo se permite una función de posinstalación por aplicación. La compilación del manifiesto generará un error si se detecta más de una.
-* Los `universalIdentifier`, `shouldRunOnVersionUpgrade` y `shouldRunSynchronously` de la función se adjuntan automáticamente al manifiesto de la aplicación en el campo `postInstallLogicFunction` durante la compilación; no es necesario que los referencies en [`defineApplication()`](/l/es/developers/extend/apps/config/application).
-* El tiempo de espera predeterminado se establece en 300 segundos (5 minutos) para permitir tareas de configuración más largas como la carga inicial de datos.
-* **No se ejecuta en modo de desarrollo**: cuando una app se registra localmente (mediante `yarn twenty dev`), el servidor omite por completo el flujo de instalación y sincroniza archivos directamente a través del observador de la CLI — por lo tanto, la post-instalación nunca se ejecuta en modo de desarrollo, independientemente de `shouldRunSynchronously`. Use `yarn twenty dev:function:exec --postInstall` para activarlo manualmente en un espacio de trabajo en ejecución.
-
-
-
-
-Una función de preinstalación se ejecuta automáticamente durante la instalación, **antes de que se aplique la migración de metadatos del espacio de trabajo**. Comparte la misma forma de payload que la post-instalación (`InstallPayload`), pero está situada antes en el flujo de instalación para poder preparar el estado del que depende la próxima migración — usos típicos incluyen hacer copias de seguridad de datos, validar la compatibilidad con el nuevo esquema o archivar registros que están a punto de ser reestructurados o eliminados.
-
-```ts src/logic-functions/pre-install.ts
-import { definePreInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define';
-
-const handler = async (payload: InstallPayload): Promise => {
- console.log('Pre install logic function executed successfully!', payload.previousVersion);
-};
-
-export default definePreInstallLogicFunction({
- universalIdentifier: 'a1b2c3d4-5678-90ab-cdef-1234567890ab',
- name: 'pre-install',
- description: 'Runs before installation to prepare the application.',
- timeoutSeconds: 300,
- shouldRunOnVersionUpgrade: true,
- handler,
-});
-```
-
-También puedes ejecutar manualmente la función de preinstalación en cualquier momento usando la CLI:
-
-```bash filename="Terminal"
yarn twenty dev:function:exec --preInstall
```
-Puntos clave:
-* Las funciones de pre-instalación usan `definePreInstallLogicFunction()` — la misma configuración especializada que la post-instalación, solo que adjunta a un punto diferente del ciclo de vida.
-* Tanto los controladores de pre- como de post-instalación reciben el mismo tipo `InstallPayload`: `{ previousVersion?: string; newVersion: string }`. Impórtelo una vez y reutilícelo para ambos hooks.
-* **Cuándo se ejecuta el hook**: se ubica justo antes de la migración de metadatos del espacio de trabajo (`synchronizeFromManifest`). Antes de ejecutarse, el servidor realiza una "sincronización simplificada" puramente aditiva que registra la función de pre-instalación de la versión **nueva** en los metadatos del espacio de trabajo — no se toca nada más — y luego la ejecuta. Debido a que esta sincronización es solo aditiva, los objetos, campos y datos de la versión anterior siguen intactos cuando se ejecuta su controlador: puede leer y respaldar de forma segura el estado premigración.
-* **Modelo de ejecución**: la pre-instalación se ejecuta **de forma síncrona** y **bloquea la instalación**. Si el controlador lanza una excepción, la instalación se aborta antes de que se apliquen cambios de esquema — el espacio de trabajo permanece en la versión anterior en un estado consistente. Esto es intencional: la pre-instalación es su última oportunidad para rechazar una actualización arriesgada.
-* Al igual que con la post-instalación, solo se permite una función de preinstalación por aplicación. Se adjunta automáticamente al manifiesto de la aplicación bajo `preInstallLogicFunction` durante la compilación.
-* **No se ejecuta en modo de desarrollo**: igual que la post-instalación — el flujo de instalación se omite por completo para las apps registradas localmente, por lo que la pre-instalación nunca se ejecuta con `yarn twenty dev`. Use `yarn twenty dev:function:exec --preInstall` para activarlo manualmente.
+
+
-
-
-
-Ambos hooks forman parte del mismo flujo de instalación y reciben el mismo `InstallPayload`. La diferencia es **cuándo** se ejecutan con respecto a la migración de metadatos del espacio de trabajo, y eso cambia qué datos pueden tocar de forma segura.
-
-La pre-instalación siempre es **síncrona** (bloquea la instalación y puede abortarla). La post-instalación es **asíncrona por defecto** — se pone en cola en un worker con reintentos automáticos — pero puede optar por ejecución síncrona con `shouldRunSynchronously: true`. Consulte el acordeón `definePostInstallLogicFunction` de arriba para saber cuándo usar cada modo.
-
-**Use `post-install` para cualquier cosa que necesite que exista el nuevo esquema.** Este es el caso más común:
-
-* Sembrar datos predeterminados (crear registros iniciales, vistas predeterminadas, contenido de demostración) sobre objetos y campos recién añadidos.
-* Registrar webhooks con servicios de terceros ahora que la app ya tiene sus credenciales.
-* Llamar a su propia API para finalizar una configuración que depende de los metadatos sincronizados.
-* Lógica idempotente de "asegurar que esto exista" que debe reconciliar el estado en cada actualización — combínela con `shouldRunOnVersionUpgrade: true`.
-
-Ejemplo — sembrar un registro `PostCard` predeterminado después de la instalación:
+Se ejecuta una vez que tu app ha terminado de instalarse: metadatos sincronizados, cliente SDK generado, nuevo esquema disponible para consulta. Ejemplo — sembrar un registro predeterminado en instalaciones nuevas:
```ts src/logic-functions/post-install.ts
import { definePostInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define';
-import { createClient } from './generated/client';
+import { CoreApiClient } from 'twenty-client-sdk/core';
const handler = async ({ previousVersion }: InstallPayload): Promise => {
if (previousVersion) return; // fresh installs only
- const client = createClient();
- await client.postCard.create({
- data: { title: 'Welcome to Postcard', content: 'Your first card!' },
+ const client = new CoreApiClient();
+ await client.mutation({
+ createPostCard: {
+ __args: { data: { name: 'Welcome to Postcard', content: 'Your first card!' } },
+ id: true,
+ },
});
};
@@ -133,22 +81,28 @@ export default definePostInstallLogicFunction({
description: 'Seeds a welcome post card after install.',
timeoutSeconds: 300,
shouldRunOnVersionUpgrade: false,
+ shouldRunSynchronously: false,
handler,
});
```
-**Use `pre-install` cuando una migración, de otro modo, destruiría o corrompería datos existentes.** Como la pre-instalación se ejecuta contra el esquema *anterior* y su fallo revierte la actualización, es el lugar adecuado para cualquier cosa arriesgada:
+El flag `shouldRunSynchronously` controla el modelo de ejecución:
-* **Hacer copia de seguridad de datos que están a punto de eliminarse o reestructurarse** — p. ej., está quitando un campo en la v2 y necesita copiar sus valores a otro campo o exportarlos a almacenamiento antes de que se ejecute la migración.
-* **Archivar registros que una nueva restricción invalidaría** — p. ej., un campo pasará a ser `NOT NULL` y primero necesita eliminar o corregir filas con valores nulos.
-* **Validar la compatibilidad y rechazar la actualización si los datos actuales no pueden migrarse limpiamente** — lance desde el controlador y la instalación se abortará sin aplicar cambios. Esto es más seguro que descubrir la incompatibilidad a mitad de la migración.
-* **Renombrar o reasignar claves de datos** antes de un cambio de esquema que perdería la asociación.
+* `false` *(predeterminado)* — encolado en la cola de mensajes (`retryLimit: 3`) y ejecutado por un worker. La respuesta de instalación se devuelve tan pronto como el trabajo se pone en la cola. **Usar para trabajo de larga duración** — siembra de grandes conjuntos de datos, APIs de terceros lentas.
+* `true` — se ejecuta en línea durante el flujo de instalación. La solicitud de instalación se bloquea hasta que el handler finaliza; un error lanzado aparece como `POST_INSTALL_ERROR` para quien realiza la llamada (sin reintentos). **Usar para trabajo rápido que debe completarse antes de la respuesta.** La migración ya se ha aplicado en este punto, por lo que un fallo no revierte los cambios de esquema — solo expone el error.
-Ejemplo — archivar registros antes de una migración destructiva:
+
+
+
+Se ejecuta antes de la migración de metadatos, contra el esquema **anterior** — el lugar adecuado para hacer una copia de seguridad de los datos que una migración perdería o para rechazar una actualización arriesgada. Antes de ejecutarse, el servidor realiza una "sincronización simplificada" puramente aditiva que registra solo la función de pre-instalación de la versión nueva; todo lo demás — los objetos, campos y datos de la versión anterior — permanece sin cambios cuando se ejecuta tu handler.
+
+La pre-instalación siempre es **síncrona** y bloquea la instalación. Si el handler lanza una excepción, la instalación se aborta antes de cualquier cambio de esquema — el espacio de trabajo permanece en la versión anterior en un estado consistente. Esto es intencional: la pre-instalación es su última oportunidad para rechazar una actualización arriesgada.
+
+Ejemplo — copiar los valores de un campo heredado antes de que la migración lo elimine:
```ts src/logic-functions/pre-install.ts
import { definePreInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define';
-import { createClient } from './generated/client';
+import { CoreApiClient } from 'twenty-client-sdk/core';
const handler = async ({ previousVersion, newVersion }: InstallPayload): Promise => {
// Only the 1.x → 2.x upgrade drops the legacy `notes` field.
@@ -156,24 +110,24 @@ const handler = async ({ previousVersion, newVersion }: InstallPayload): Promise
return;
}
- const client = createClient();
- const legacyRecords = await client.postCard.findMany({
- where: { notes: { isNotNull: true } },
+ const client = new CoreApiClient();
+ const { postCards } = await client.query({
+ postCards: {
+ __args: { filter: { notes: { isNot: null } } },
+ edges: { node: { id: true, notes: true } },
+ },
});
- if (legacyRecords.length === 0) return;
-
- // Copy legacy `notes` into the new `description` field before the migration
- // drops the `notes` column. If this fails, the upgrade is aborted and the
- // workspace stays on v1 with all data intact.
- await Promise.all(
- legacyRecords.map((record) =>
- client.postCard.update({
- where: { id: record.id },
- data: { description: record.notes },
- }),
- ),
- );
+ // Copy legacy `notes` into `description` before the migration drops the
+ // column. If this fails, the upgrade aborts and the workspace stays on v1.
+ for (const { node } of postCards.edges) {
+ await client.mutation({
+ updatePostCard: {
+ __args: { id: node.id, data: { description: node.notes } },
+ id: true,
+ },
+ });
+ }
};
export default definePreInstallLogicFunction({
@@ -186,21 +140,5 @@ export default definePreInstallLogicFunction({
});
```
-**Regla general:**
-
-| Quiere... | Usar |
-| -------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- |
-| Sembrar datos predeterminados, configurar el espacio de trabajo, registrar recursos externos | `post-install` |
-| Ejecutar siembras de larga duración o llamadas a terceros que no deberían bloquear la respuesta de instalación | `post-install` (predeterminado — `shouldRunSynchronously: false`, con reintentos del worker) |
-| Ejecutar una configuración rápida de la que el cliente dependerá inmediatamente después de que regrese la llamada de instalación | `post-install` con `shouldRunSynchronously: true` |
-| Leer o hacer copia de seguridad de datos que la próxima migración perdería | `pre-install` |
-| Rechazar una actualización que corrompería datos existentes | `pre-install` (lanzar desde el controlador) |
-| Ejecutar reconciliación en cada actualización | `post-install` con `shouldRunOnVersionUpgrade: true` |
-| Realizar una configuración única solo en la primera instalación | `post-install` con `shouldRunOnVersionUpgrade: false` (predeterminado) |
-
-
-En caso de duda, elija **post-install** como predeterminado. Recurra a la pre-instalación solo cuando la propia migración sea destructiva y necesite interceptar el estado anterior antes de que desaparezca.
-
-
diff --git a/packages/twenty-docs/l/es/developers/extend/apps/data/objects.mdx b/packages/twenty-docs/l/es/developers/extend/apps/data/objects.mdx
index 8a03becda1..b0e3c4da53 100644
--- a/packages/twenty-docs/l/es/developers/extend/apps/data/objects.mdx
+++ b/packages/twenty-docs/l/es/developers/extend/apps/data/objects.mdx
@@ -86,6 +86,22 @@ export default defineObject({
**Los campos base se añaden automáticamente.** Cuando defines un objeto personalizado, Twenty crea campos estándar como `id`, `name`, `createdAt`, `updatedAt`, `createdBy`, `updatedBy` y `deletedAt` por ti. No necesitas declararlos en tu matriz `fields`, solo tus campos personalizados. Puedes sobrescribir un campo predeterminado declarando uno con el mismo nombre, pero esto rara vez es una buena idea.
+## Tipos de campo
+
+El conjunto completo de valores de `FieldType`, exportados desde `twenty-sdk/define`:
+
+| Categoría | Tipos |
+| ---------------------------- | --------------------------------------------------------------------------------------------------------------------------------- |
+| Texto | `TEXT`, `RICH_TEXT`, `ARRAY` (de cadenas), `RAW_JSON` |
+| Numérico | `NUMBER` (`universalSettings.dataType`: `'float'` / `'int'` / `'bigint'`), `NUMERIC` (precisión arbitraria), `RATING`, `POSITION` |
+| Fechas | `DATE`, `DATE_TIME` |
+| Opción | `BOOLEAN`, `SELECT`, `MULTI_SELECT` |
+| Compuesto | `FULL_NAME`, `ADDRESS`, `EMAILS`, `PHONES`, `LINKS`, `CURRENCY`, `ACTOR`, `FILES` |
+| Identificadores y relaciones | `UUID`, `RELATION`, `MORPH_RELATION` (ver [Relations](/l/es/developers/extend/apps/data/relations)) |
+| Sistema | `TS_VECTOR` (vector de búsqueda de texto completo, gestionado por el servidor) |
+
+Los tipos compuestos almacenan múltiples subcampos (por ejemplo, `FULL_NAME` = nombre + apellido; `CURRENCY` = `amountMicros` + `currencyCode`). `SELECT` y `MULTI_SELECT` requieren un arreglo `options` como en el ejemplo anterior.
+
## Valores predeterminados
Los valores predeterminados de cadenas literales deben ir entre comillas simples **dentro** de la cadena — `defaultValue: "'Draft'"`, no `defaultValue: "Draft"`. Por eso el campo `status` anterior utiliza `` `'${PostCardStatus.DRAFT}'` ``.
diff --git a/packages/twenty-docs/l/es/developers/extend/apps/getting-started/project-structure.mdx b/packages/twenty-docs/l/es/developers/extend/apps/getting-started/project-structure.mdx
index 11ed884f9b..1a4154d85f 100644
--- a/packages/twenty-docs/l/es/developers/extend/apps/getting-started/project-structure.mdx
+++ b/packages/twenty-docs/l/es/developers/extend/apps/getting-started/project-structure.mdx
@@ -14,26 +14,39 @@ my-twenty-app/
default-role.ts # Permissions for logic functions
constants/
universal-identifiers.ts # Auto-generated UUIDs and metadata
+ front-components/
+ main-page.tsx # Welcome page component
+ navigation-menu-items/
+ main-page.navigation-menu-item.ts # Sidebar entry for the welcome page
+ page-layouts/
+ main-page.page-layout.ts # Standalone page hosting the component
__tests__/
- setup-test.ts
- app-install.integration-test.ts
- .github/workflows/ci.yml # GitHub Actions
- public/ # Static assets
- vitest.config.ts # Test runner config
+ application-config.test.ts # Unit test
+ global-setup.ts # Integration test setup (sync + uninstall)
+ schema.integration-test.ts # Integration test against a live server
+ .github/workflows/
+ ci.yml # Lint, typecheck, unit + integration tests
+ cd.yml # Deploy + install on push to main
+ public/
+ logo.svg # Static assets
+ vitest.config.ts # Integration test runner config
+ vitest.unit.config.ts # Unit test runner config
tsconfig.json, tsconfig.spec.json
.nvmrc, .yarnrc.yml, .oxlintrc.json
- README.md, LLMS.md
+ README.md, AGENTS.md, CLAUDE.md
```
## Archivos clave
-| Archivo / Carpeta | Propósito |
-| ---------------------------------------- | ----------------------------------------------------------------------------------------- |
-| `src/application-config.ts` | **Obligatorio.** El archivo de configuración principal de tu app. |
-| `src/default-role.ts` | Rol predeterminado que controla a qué pueden acceder tus funciones lógicas. |
-| `src/constants/universal-identifiers.ts` | UUIDs generados automáticamente y metadatos de la app (nombre para mostrar, descripción). |
-| `src/__tests__/` | Pruebas de integración (configuración + prueba de ejemplo). |
-| `public/` | Recursos estáticos (imágenes, fuentes) servidos con tu app. |
+| Archivo / Carpeta | Propósito |
+| -------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
+| `src/application-config.ts` | **Obligatorio.** El archivo de configuración principal de tu app. |
+| `src/default-role.ts` | Rol predeterminado que controla a qué pueden acceder tus funciones lógicas. |
+| `src/constants/universal-identifiers.ts` | UUIDs generados automáticamente y metadatos de la app (nombre para mostrar, descripción). |
+| `src/front-components/`, `src/navigation-menu-items/`, `src/page-layouts/` | Una página de bienvenida inicial: un front component renderizado por un page layout independiente, accesible desde la barra lateral. |
+| `src/__tests__/` | Una prueba unitaria más una prueba de integración (con su configuración global) que sincroniza la aplicación contra un servidor real. |
+| `public/` | Recursos estáticos (imágenes, fuentes) servidos con tu app. |
+| `AGENTS.md` / `CLAUDE.md` | Guía para agentes de IA de programación que trabajan en la aplicación. |
**La organización de archivos depende de ti.** Las carpetas anteriores son convenciones: el SDK detecta entidades mediante análisis AST en llamadas a `export default defineEntity(...)`, sin importar dónde se encuentre el archivo.
@@ -47,15 +60,18 @@ Ambos paquetes del SDK de Twenty pertenecen a `devDependencies`, no a `dependenc
{
"dependencies": {},
"devDependencies": {
- "twenty-client-sdk": "^2.13.0",
- "twenty-sdk": "^2.13.0"
+ "twenty-client-sdk": "2.20.0",
+ "twenty-sdk": "2.20.0",
+ "twenty-ui": "1.0.0-alpha.1"
}
}
```
+El generador fija `twenty-sdk` y `twenty-client-sdk` a su propia versión; mantén ambos sincronizados al actualizar.
+
* **`twenty-sdk`** incluye el CLI `twenty` y las herramientas de build/scaffolding. Solo se ejecuta en el desarrollo y durante el build, y nunca lo importa el runtime de la app que publicas.
* **`twenty-client-sdk`** *sí* es importado por el código de tu app (`CoreApiClient`, `MetadataApiClient`, `RestApiClient`), pero Twenty lo proporciona en tiempo de ejecución: las funciones lógicas lo obtienen de una capa SDK generada y los componentes de front lo resuelven desde módulos servidos por el servidor. Tu copia instalada solo se utiliza para la comprobación de tipos y el build en tiempo de despliegue, por lo que nunca necesita incluirse en el bundle desplegado.
-Mantener cualquiera de los paquetes bajo `dependencies` lo introduce en el bundle de runtime de la app instalada, donde es peso muerto. `twenty build` emite una advertencia cuando cualquiera de ellos sigue listado bajo `dependencies`.
+Mantener cualquiera de los paquetes bajo `dependencies` lo introduce en el bundle de runtime de la app instalada, donde es peso muerto. `twenty dev:build` emite una advertencia cuando cualquiera de ellos sigue listado bajo `dependencies`.
Añade las dependencias de runtime propias de tu app (las bibliotecas que tus funciones lógicas realmente importan en tiempo de ejecución) bajo `dependencies` como de costumbre.
diff --git a/packages/twenty-docs/l/es/developers/extend/apps/getting-started/quick-start.mdx b/packages/twenty-docs/l/es/developers/extend/apps/getting-started/quick-start.mdx
index 5c11a87152..e2a6c1ccd4 100644
--- a/packages/twenty-docs/l/es/developers/extend/apps/getting-started/quick-start.mdx
+++ b/packages/twenty-docs/l/es/developers/extend/apps/getting-started/quick-start.mdx
@@ -6,17 +6,17 @@ description: Crea tu primera aplicación de Twenty en minutos.
## Prerrequisitos
-* **Node.js 24+** — [Descargar](https://nodejs.org/)
+* **Node.js 24.5+** — [Descargar](https://nodejs.org/)
* **Yarn 4** — incluido con Node.js a través de Corepack. Actívalo: `corepack enable`
* **Docker** — [Descargar](https://www.docker.com/products/docker-desktop/). Necesario para ejecutar un servidor local de Twenty. Omítelo si ya tienes Twenty ejecutándose en otro lugar.
La creación de una app de Twenty tiene tres fases. El generador las combina en un único comando de ruta ideal, pero cada fase es un concepto independiente — cuando algo falla, saber en qué fase estás te indica qué debes corregir.
-| Fase | Qué haces | Herramienta | Resultado |
-| --------------------------- | --------------------------------------------------- | ----------------------------- | ------------------------------------ |
-| **1. Generar estructura** | Genera el código fuente de la app | `npx create-twenty-app` | Un proyecto de TypeScript en disco |
-| **2. Ejecutar un servidor** | Inicia un servidor de Twenty con el que sincronizar | Docker + `yarn twenty server` | Una instancia de Twenty en ejecución |
-| **3. Sincronizar** | Sincroniza en vivo tu código con el servidor | `yarn twenty dev` | Tus cambios aparecen en la UI |
+| Fase | Qué haces | Herramienta | Resultado |
+| --------------------------- | --------------------------------------------------- | ----------------------------------- | ------------------------------------ |
+| **1. Generar estructura** | Genera el código fuente de la app | `npx create-twenty-app` | Un proyecto de TypeScript en disco |
+| **2. Ejecutar un servidor** | Inicia un servidor de Twenty con el que sincronizar | Docker + `yarn twenty docker:start` | Una instancia de Twenty en ejecución |
+| **3. Sincronizar** | Sincroniza en vivo tu código con el servidor | `yarn twenty dev` | Tus cambios aparecen en la UI |
---
@@ -28,7 +28,7 @@ Crea una app nueva a partir de la plantilla:
npx create-twenty-app@latest my-twenty-app
```
-Se te pedirá un nombre y una descripción — pulsa **Enter** para usar los valores predeterminados. Esto genera un proyecto de TypeScript en `my-twenty-app/` con un `application-config.ts` inicial, un rol predeterminado, un flujo de trabajo de CI y una prueba de integración.
+El generador no es interactivo: el nombre del directorio se convierte en el nombre de la aplicación. Pasa `--display-name` y `--description` para personalizar los metadatos generados (también puedes editarlos más tarde en `src/constants/universal-identifiers.ts`). Esto genera un proyecto de TypeScript en `my-twenty-app/` con un `application-config.ts` inicial, un rol predeterminado, flujos de trabajo de CI/CD y una prueba de integración.
**Después de esta fase:** tienes el código fuente de tu app en tu máquina. Aún no se está ejecutando — esa es la Fase 2.
@@ -38,28 +38,14 @@ Se te pedirá un nombre y una descripción — pulsa **Enter** para usar los val
Tu app necesita un servidor de Twenty con el que sincronizar. El servidor es una instancia completa de Twenty — UI, API GraphQL, PostgreSQL — ejecutándose localmente en Docker. Tu código local sube sus definiciones a ese servidor, lo que hace que aparezcan en la UI.
-El generador ofrece iniciar uno por ti:
+El generador inicia uno por ti: con Docker en ejecución, extrae la imagen `twentycrm/twenty-app-dev`, la inicia en el puerto `2020` y autentica la CLI contra el espacio de trabajo de demostración preconfigurado (`tim@apple.dev`), sin necesidad de iniciar sesión.
-> **¿Te gustaría configurar una instancia local de Twenty?**
-
-* **Sí (recomendado)** — descarga la imagen de Docker `twentycrm/twenty-app-dev` y la inicia en el puerto `2020`. Asegúrate de que Docker esté en ejecución antes.
-* **No** — elige esto si ya tienes un servidor de Twenty al que te quieres conectar. Puedes conectarlo más tarde con `yarn twenty remote:add`.
-
-
-

-
-
-Una vez que el servidor esté en marcha, se abrirá un navegador para iniciar sesión. Inicia sesión con la cuenta de demostración precargada:
-
-* **Correo electrónico:** `tim@apple.dev`
-* **Contraseña:** `tim@apple.dev`
+Para conectarte a un servidor Twenty existente en su lugar, pasa `--url \`. Los servidores remotos se autentican con OAuth: se abre un navegador para que puedas iniciar sesión y hacer clic en **Authorize**, lo que le da a la CLI acceso a tu espacio de trabajo. (También puedes optar por usar OAuth localmente con `--authentication-method oauth`: inicia sesión con `tim@apple.dev` / `tim@apple.dev`.)
-Haz clic en **Authorize** en la siguiente pantalla — esto le da a la CLI acceso a tu espacio de trabajo.
-
@@ -117,27 +103,31 @@ Haz clic en **View installed app** para ver la instalación en el espacio de tra
### Sincronización de una sola vez para CI y scripts
-Pasa `--once` para ejecutar una sola compilación + sincronización y salir — mismo pipeline, sin watcher:
+Usa `plan` y `apply` para ejecutar la misma canalización una vez, sin observador:
```bash filename="Terminal"
-yarn twenty dev --once
+yarn twenty plan # preview the metadata changes without applying them
+yarn twenty apply # show the plan, then apply it
```
-| Comando | Comportamiento | Cuándo usarlo |
-| ---------------------------------- | ----------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------- |
-| `yarn twenty dev` | Supervisa tus archivos fuente y vuelve a sincronizar en cada cambio. Se ejecuta hasta que lo detengas. | Desarrollo local interactivo. |
-| `yarn twenty dev --once` | Realiza una sola compilación + sincronización y luego sale con el código `0` si tiene éxito o `1` si falla. | CI, hooks de pre-commit, agentes de IA, flujos de trabajo con scripts. |
-| `yarn twenty dev --once --dry-run` | Genera y muestra los cambios de metadatos **sin aplicarlos**. | Inspeccionar qué cambiaría una sincronización antes de confirmarla. |
+| Comando | Comportamiento | Cuándo usarlo |
+| ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------- |
+| `yarn twenty dev` | Supervisa tus archivos fuente y vuelve a sincronizar en cada cambio. Se ejecuta hasta que lo detengas. | Desarrollo local interactivo. |
+| `yarn twenty apply` | Realiza una sola compilación + sincronización y luego sale con el código `0` si tiene éxito o `1` si falla. Pide confirmación para cambios destructivos (pasa `--force` para omitirla). | CI, hooks de pre-commit, agentes de IA, flujos de trabajo con scripts. |
+| `yarn twenty plan` | Genera y muestra los cambios de metadatos **sin aplicarlos**. | Inspeccionar qué cambiaría una sincronización antes de confirmarla. |
-Ambos modos necesitan un remoto autenticado. Consulta [Sincronización y recuperación](/l/es/developers/extend/apps/operations/sync-and-recovery#previewing-changes-dry-run) para obtener más información sobre `--dry-run`.
+Todos los modos necesitan un remoto autenticado. Consulta [Sincronización y recuperación](/l/es/developers/extend/apps/operations/sync-and-recovery#previewing-changes-plan) para obtener más información sobre `plan`.
+
+
+`yarn twenty dev --once` y `yarn twenty dev --once --dry-run` son alias obsoletos de `yarn twenty apply` y `yarn twenty plan`.
+
### Opciones del modo de desarrollo
| Opción | Descripción |
| ------------------------------------- | -------------------------------------------------------------------------------------------------------------- |
-| `--once` | Compila y sincroniza una vez y luego finaliza. |
-| `--dry-run` | Con `--once`, obtén una vista previa de los cambios de metadatos sin aplicarlos. No escribe nada. |
-| `--debounceMs \` | Establece el tiempo de antirrebote para los cambios de archivo en milisegundos (valor predeterminado: `2000`). |
+| `--force` | Aplica cambios destructivos (eliminaciones) sin confirmación. |
+| `--debounceMs \` | Establece el tiempo de antirrebote para los cambios de archivo en milisegundos (valor predeterminado: `1000`). |
| `--verbose` / `--debug` | Muestra registros de compilación detallados, solicitudes de sincronización y seguimientos de errores. |
## Lo que puedes crear
diff --git a/packages/twenty-docs/l/es/developers/extend/apps/getting-started/scaffolding.mdx b/packages/twenty-docs/l/es/developers/extend/apps/getting-started/scaffolding.mdx
index 38a5fd57bb..0fb24fe6e7 100644
--- a/packages/twenty-docs/l/es/developers/extend/apps/getting-started/scaffolding.mdx
+++ b/packages/twenty-docs/l/es/developers/extend/apps/getting-started/scaffolding.mdx
@@ -34,6 +34,10 @@ yarn twenty dev:add frontComponent
| Vista | `yarn twenty dev:add view` | `src/views/\.ts` |
| Elemento del menú de navegación | `yarn twenty dev:add navigationMenuItem` | `src/navigation-menu-items/\.ts` |
| Diseño de página | `yarn twenty dev:add pageLayout` | `src/page-layouts/\.ts` |
+| Pestaña Diseño de página | `yarn twenty dev:add pageLayoutTab` | `src/page-layout-tabs/\.ts` |
+| Elemento del menú de comandos | `yarn twenty dev:add commandMenuItem` | `src/command-menu-items/\.ts` |
+| Campo de vista | `yarn twenty dev:add viewField` | `src/view-fields/\.ts` |
+| Proveedor de conexión | `yarn twenty dev:add connectionProvider` | `src/connection-providers/\.ts` |
## Qué genera el generador
diff --git a/packages/twenty-docs/l/es/developers/extend/apps/getting-started/troubleshooting.mdx b/packages/twenty-docs/l/es/developers/extend/apps/getting-started/troubleshooting.mdx
index 34f2c14a48..3302d82f04 100644
--- a/packages/twenty-docs/l/es/developers/extend/apps/getting-started/troubleshooting.mdx
+++ b/packages/twenty-docs/l/es/developers/extend/apps/getting-started/troubleshooting.mdx
@@ -5,10 +5,10 @@ icon: wrench
---
* **Errores de Docker** — Asegúrate de que Docker Desktop (o el daemon) esté en ejecución antes de `yarn twenty docker:start`. El mensaje de error mostrará el comando de inicio correcto para tu sistema operativo.
-* **Versión de Node incorrecta** — Se requiere 24+. Compruébalo con `node -v`.
+* **Versión de Node incorrecta** — Se necesita la 24.5+ (`engines.node: ^24.5.0`). Compruébalo con `node -v`.
* **Falta Yarn 4** — Ejecuta `corepack enable`.
* **Dependencias rotas** — `rm -rf node_modules && yarn install`.
* **Errores de `twenty-sdk` tras actualizar a la v2.8.0** — Pasó de `dependencies` a `devDependencies` en la v2.8.0. Consulta [Estructura del proyecto → Dependencias](/l/es/developers/extend/apps/getting-started/project-structure#dependencies).
-* **`twenty build` muestra una advertencia sobre `twenty-client-sdk` en `dependencies`** — Twenty lo proporciona en tiempo de ejecución, por lo que debería trasladarse a `devDependencies` junto con `twenty-sdk`. Consulta [Estructura del proyecto → Dependencias](/l/es/developers/extend/apps/getting-started/project-structure#dependencies).
+* **`twenty dev:build` muestra una advertencia sobre `twenty-client-sdk` en `dependencies`** — Twenty lo proporciona en tiempo de ejecución, por lo que debería trasladarse a `devDependencies` junto con `twenty-sdk`. Consulta [Estructura del proyecto → Dependencias](/l/es/developers/extend/apps/getting-started/project-structure#dependencies).
¿Atascado? Pide ayuda en el [Discord de Twenty](https://discord.com/channels/1130383047699738754/1130386664812982322).
diff --git a/packages/twenty-docs/l/es/developers/extend/apps/layout/command-menu-items.mdx b/packages/twenty-docs/l/es/developers/extend/apps/layout/command-menu-items.mdx
index c02fd35d6b..893bc6d581 100644
--- a/packages/twenty-docs/l/es/developers/extend/apps/layout/command-menu-items.mdx
+++ b/packages/twenty-docs/l/es/developers/extend/apps/layout/command-menu-items.mdx
@@ -13,7 +13,6 @@ export default defineCommandMenuItem({
universalIdentifier: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
label: 'Open Dashboard',
shortLabel: 'Dashboard',
- icon: 'IconLayoutDashboard',
isPinned: true,
availabilityType: 'GLOBAL',
frontComponentUniversalIdentifier: '74c526eb-cb68-4cf7-b05c-0dd8c288d948',
@@ -22,51 +21,23 @@ export default defineCommandMenuItem({
## Campos de configuración
-| Campo | Obligatorio | Descripción |
-| --------------------------------------- | ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| `universalIdentifier` | Sí | ID único estable para el comando |
-| `label` | Sí | Etiqueta completa mostrada en el menú de comandos (Cmd+K) |
-| `frontComponentUniversalIdentifier` | Sí | El `universalIdentifier` del componente de frontend que abre este comando |
-| `shortLabel` | No | Etiqueta corta mostrada en el botón de acción rápida anclado |
-| `icon` | No | Nombre del ícono mostrado junto a la etiqueta (p. ej., 'IconBolt', 'IconSend') |
-| `isPinned` | No | Cuando es `true`, muestra el comando como un botón de acción rápida en la esquina superior derecha de la página |
-| `availabilityType` | No | Controla dónde aparece el comando: 'GLOBAL' (siempre disponible), 'RECORD_SELECTION' (solo cuando hay registros seleccionados) o 'FALLBACK' (se muestra cuando ningún otro comando coincide) |
-| `availabilityObjectUniversalIdentifier` | No | Restringe el comando a páginas de un tipo de objeto específico (p. ej., solo en registros de Company) |
-| `conditionalAvailabilityExpression` | No | Una expresión booleana que controla dinámicamente la visibilidad (ver abajo) |
+| Campo | Obligatorio | Descripción |
+| --------------------------------------- | ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `universalIdentifier` | Sí | ID único estable para el comando |
+| `label` | Sí | Etiqueta completa mostrada en el menú de comandos (Cmd+K) |
+| `frontComponentUniversalIdentifier` | Sí | El `universalIdentifier` del componente de frontend que abre este comando |
+| `shortLabel` | No | Etiqueta corta mostrada en el botón de acción rápida anclado |
+| `icon` | No | **Obsoleto**: se ignora en favor del icono de la aplicación; la compilación emite una advertencia si se establece |
+| `isPinned` | No | Cuando es `true`, muestra el comando como un botón de acción rápida en la esquina superior derecha de la página |
+| `availabilityType` | No | Controla dónde aparece el comando: `'GLOBAL'` (siempre disponible), `'GLOBAL_OBJECT_CONTEXT'` (solo en páginas con un contexto de objeto: páginas de índice y de registro), `'RECORD_SELECTION'` (solo cuando hay registros seleccionados) o `'FALLBACK'` (se muestra cuando ningún otro comando coincide) |
+| `availabilityObjectUniversalIdentifier` | No | Restringe el comando a páginas de un tipo de objeto específico (p. ej., solo en registros de Company) |
+| `conditionalAvailabilityExpression` | No | Una expresión booleana que controla dinámicamente la visibilidad (ver abajo) |
## Comandos sin interfaz
Un elemento del menú de comandos emparejado con un [headless front component](/l/es/developers/extend/apps/layout/front-components#headless-vs-non-headless) es la forma idónea de ofrecer una acción de un solo clic: ejecutar código, navegar o confirmar y ejecutar. La página Front Components abarca los [SDK Command components](/l/es/developers/extend/apps/layout/front-components#sdk-command-components) (`Command`, `CommandLink`, `CommandModal`, `CommandOpenSidePanelPage`) que gestionan el patrón de acción y desmontaje.
-Un flujo típico:
-
-```tsx src/front-components/run-action.tsx
-import { defineFrontComponent } from 'twenty-sdk/define';
-import { Command } from 'twenty-sdk/command';
-import { CoreApiClient } from 'twenty-sdk/clients';
-
-const RunAction = () => {
- const execute = async () => {
- const client = new CoreApiClient();
- await client.mutation({
- createTask: {
- __args: { data: { title: 'Created by my app' } },
- id: true,
- },
- });
- };
-
- return ;
-};
-
-export default defineFrontComponent({
- universalIdentifier: 'e5f6a7b8-c9d0-1234-efab-345678901234',
- name: 'run-action',
- description: 'Creates a task from the command menu',
- component: RunAction,
- isHeadless: true,
-});
-```
+Un flujo típico: un componente sin interfaz gráfica renderiza `` (consulta el [ejemplo completo](/l/es/developers/extend/apps/layout/front-components#sdk-command-components)), y el elemento del menú de comandos lo señala:
```ts src/command-menu-items/run-action.command-menu-item.ts
import { defineCommandMenuItem } from 'twenty-sdk/define';
@@ -74,7 +45,6 @@ import { defineCommandMenuItem } from 'twenty-sdk/define';
export default defineCommandMenuItem({
universalIdentifier: 'f6a7b8c9-d0e1-2345-fabc-456789012345',
label: 'Run my action',
- icon: 'IconPlayerPlay',
frontComponentUniversalIdentifier: 'e5f6a7b8-c9d0-1234-efab-345678901234',
});
```
diff --git a/packages/twenty-docs/l/es/developers/extend/apps/layout/front-components.mdx b/packages/twenty-docs/l/es/developers/extend/apps/layout/front-components.mdx
index 603e299bd5..9ab4ad1e97 100644
--- a/packages/twenty-docs/l/es/developers/extend/apps/layout/front-components.mdx
+++ b/packages/twenty-docs/l/es/developers/extend/apps/layout/front-components.mdx
@@ -49,14 +49,13 @@ export default defineCommandMenuItem({
universalIdentifier: 'd4e5f6a7-b8c9-0123-defa-456789012345',
shortLabel: 'Hello',
label: 'Hello World',
- icon: 'IconBolt',
isPinned: true,
availabilityType: 'GLOBAL',
frontComponentUniversalIdentifier: '74c526eb-cb68-4cf7-b05c-0dd8c288d948',
});
```
-Después de sincronizar con `yarn twenty dev` (o ejecutar una sola vez `yarn twenty dev --once`), la acción rápida aparece en la esquina superior derecha de la página:
+Después de sincronizar con `yarn twenty dev` (o ejecutar una sola vez `yarn twenty apply`), la acción rápida aparece en la esquina superior derecha de la página:

@@ -88,11 +87,11 @@ Los componentes de front vienen en dos modos de renderizado controlados por la o
```tsx src/front-components/sync-tracker.tsx
import { defineFrontComponent } from 'twenty-sdk/define';
-import { useRecordId, enqueueSnackbar } from 'twenty-sdk/front-component';
+import { useSelectedRecordIds, enqueueSnackbar } from 'twenty-sdk/front-component';
import { useEffect } from 'react';
const SyncTracker = () => {
- const recordId = useRecordId();
+ const [recordId] = useSelectedRecordIds();
useEffect(() => {
enqueueSnackbar({ message: `Tracking record ${recordId}`, variant: 'info' });
@@ -116,7 +115,7 @@ Como el componente devuelve `null`, Twenty omite renderizar un contenedor para
El paquete `twenty-sdk` proporciona cuatro componentes auxiliares Command diseñados para componentes de front headless. Cada componente ejecuta una acción al montarse, gestiona los errores mostrando una notificación tipo snackbar y desmonta automáticamente el componente de front al finalizar.
-Impórtalos desde `twenty-sdk/command`:
+Impórtalos desde `twenty-sdk/front-component`:
* **`Command`** — Ejecuta un callback asíncrono mediante la prop `execute`.
* **`CommandLink`** — Navega a una ruta de la aplicación. Props: `to`, `params`, `queryParams`, `options`.
@@ -127,8 +126,8 @@ Aquí tienes un ejemplo completo de un componente de front headless que usa `Com
```tsx src/front-components/run-action.tsx
import { defineFrontComponent } from 'twenty-sdk/define';
-import { Command } from 'twenty-sdk/command';
-import { CoreApiClient } from 'twenty-sdk/clients';
+import { Command } from 'twenty-sdk/front-component';
+import { CoreApiClient } from 'twenty-client-sdk/core';
const RunAction = () => {
const execute = async () => {
@@ -160,7 +159,6 @@ import { defineCommandMenuItem } from 'twenty-sdk/define';
export default defineCommandMenuItem({
universalIdentifier: 'f6a7b8c9-d0e1-2345-fabc-456789012345',
label: 'Run my action',
- icon: 'IconPlayerPlay',
frontComponentUniversalIdentifier: 'e5f6a7b8-c9d0-1234-efab-345678901234',
});
```
@@ -169,7 +167,7 @@ Y un ejemplo que usa `CommandModal` para pedir confirmación antes de ejecutar:
```tsx src/front-components/delete-draft.tsx
import { defineFrontComponent } from 'twenty-sdk/define';
-import { CommandModal } from 'twenty-sdk/command';
+import { CommandModal } from 'twenty-sdk/front-component';
const DeleteDraft = () => {
const execute = async () => {
@@ -202,7 +200,7 @@ Los componentes de front se ejecutan en el navegador dentro de un Web Worker ais
Una función de lógica declarada con `httpRouteTriggerSettings` es accesible por HTTP en su ruta. Twenty inyecta en el worker la URL base desde la que se sirven tus funciones como `TWENTY_FUNCTIONS_URL`, junto con el `TWENTY_APP_ACCESS_TOKEN` que autentica la llamada. Todavía no hay un cliente SDK dedicado para invocar tus propias funciones, así que llámalas con un simple `fetch`:
-> **En Twenty Cloud, las funciones de lógica activadas por HTTP se sirven en un dominio dedicado por espacio de trabajo** en `https://\
.twenty.com\` — esto es exactamente a lo que se resuelve `TWENTY_FUNCTIONS_URL`. Para clientes externos, copia la URL exacta desde la configuración de **HTTP trigger** de la función o desde la pestaña **Settings** de la aplicación.
+> **En Twenty Cloud, las funciones de lógica activadas por HTTP se sirven en un dominio dedicado por espacio de trabajo** en `https://\.withtwenty.com\` — esto es exactamente a lo que se resuelve `TWENTY_FUNCTIONS_URL`. Para clientes externos, copia la URL exacta desde la configuración de **HTTP trigger** de la función o desde la pestaña **Settings** de la aplicación.
La ruta heredada de la función `/s/` está **obsoleta** y será **desactivada el 2026-07-24**. En su lugar, utiliza `TWENTY_FUNCTIONS_URL` (arriba) y migra cualquier URL de `/s/` codificada de forma fija antes de esa fecha. La ruta `/s/` sigue disponible para autoalojamiento.
@@ -212,7 +210,7 @@ Un componente de front sin interfaz (headless) puede ejecutar la llamada al mont
```tsx src/front-components/sync-prs.tsx
import { defineFrontComponent } from 'twenty-sdk/define';
-import { Command } from 'twenty-sdk/command';
+import { Command } from 'twenty-sdk/front-component';
const SyncPrs = () => {
const execute = async () => {
@@ -316,13 +314,13 @@ Dentro de tu componente, usa hooks del SDK para acceder al usuario actual, el re
import { defineFrontComponent } from 'twenty-sdk/define';
import {
useUserId,
- useRecordId,
+ useSelectedRecordIds,
useFrontComponentId,
} from 'twenty-sdk/front-component';
const RecordInfo = () => {
const userId = useUserId();
- const recordId = useRecordId();
+ const [recordId] = useSelectedRecordIds();
const componentId = useFrontComponentId();
return (
@@ -405,12 +403,11 @@ Aquí tienes un ejemplo que usa la API del host para mostrar un snackbar y cerra
```tsx src/front-components/archive-record.tsx
import { defineFrontComponent } from 'twenty-sdk/define';
-import { useRecordId } from 'twenty-sdk/front-component';
-import { enqueueSnackbar, closeSidePanel } from 'twenty-sdk/front-component';
-import { CoreApiClient } from 'twenty-sdk/clients';
+import { enqueueSnackbar, closeSidePanel, useSelectedRecordIds } from 'twenty-sdk/front-component';
+import { CoreApiClient } from 'twenty-client-sdk/core';
const ArchiveRecord = () => {
- const recordId = useRecordId();
+ const [recordId] = useSelectedRecordIds();
const handleArchive = async () => {
const client = new CoreApiClient();
@@ -451,10 +448,10 @@ export default defineFrontComponent({
Usa `useSelectedRecordIds()` para manejar varios registros seleccionados. Esto es útil para operaciones por lotes:
```tsx src/front-components/bulk-export.tsx
-import { defineFrontComponent, numberOfSelectedRecords } from 'twenty-sdk/define';
+import { defineFrontComponent } from 'twenty-sdk/define';
import { useSelectedRecordIds } from 'twenty-sdk/front-component';
import { enqueueSnackbar, closeSidePanel } from 'twenty-sdk/front-component';
-import { CoreApiClient } from 'twenty-sdk/clients';
+import { CoreApiClient } from 'twenty-client-sdk/core';
const BulkExport = () => {
const selectedRecordIds = useSelectedRecordIds();
@@ -492,12 +489,19 @@ export default defineFrontComponent({
name: 'bulk-export',
description: 'Export selected records',
component: BulkExport,
- command: {
- universalIdentifier: 'd0e1f2a3-b4c5-6789-defa-012345678902',
- label: 'Bulk Export',
- availabilityType: 'RECORD_SELECTION',
- conditionalAvailabilityExpression: numberOfSelectedRecords > 0,
- },
+});
+```
+
+Muéstralo con un [elemento de menú de comando](/l/es/developers/extend/apps/layout/command-menu-items) restringido a selecciones de registros:
+
+```ts src/command-menu-items/bulk-export.command-menu-item.ts
+import { defineCommandMenuItem } from 'twenty-sdk/define';
+
+export default defineCommandMenuItem({
+ universalIdentifier: 'd0e1f2a3-b4c5-6789-defa-012345678902',
+ label: 'Bulk Export',
+ availabilityType: 'RECORD_SELECTION',
+ frontComponentUniversalIdentifier: 'd0e1f2a3-b4c5-6789-defa-012345678901',
});
```
diff --git a/packages/twenty-docs/l/es/developers/extend/apps/layout/navigation-menu-items.mdx b/packages/twenty-docs/l/es/developers/extend/apps/layout/navigation-menu-items.mdx
index bd0f1b223e..096bf3a740 100644
--- a/packages/twenty-docs/l/es/developers/extend/apps/layout/navigation-menu-items.mdx
+++ b/packages/twenty-docs/l/es/developers/extend/apps/layout/navigation-menu-items.mdx
@@ -35,6 +35,8 @@ export default defineNavigationMenuItem({
* `position` controla el orden en la barra lateral.
+* El enum también contiene `NavigationMenuItemType.RECORD`, que se usa internamente para los favoritos de registros creados por el usuario; no se puede usar desde un manifiesto de aplicación (no hay ningún campo para hacer referencia a un registro).
+
* `icon` y `color` son opcionales y personalizan el aspecto de la entrada.
* `folderUniversalIdentifier` también está disponible en cualquier elemento para anidarlo dentro de un elemento padre de tipo `FOLDER`.
diff --git a/packages/twenty-docs/l/es/developers/extend/apps/layout/views.mdx b/packages/twenty-docs/l/es/developers/extend/apps/layout/views.mdx
index cc370f4e81..83e6ed142e 100644
--- a/packages/twenty-docs/l/es/developers/extend/apps/layout/views.mdx
+++ b/packages/twenty-docs/l/es/developers/extend/apps/layout/views.mdx
@@ -33,17 +33,32 @@ export default defineView({
## Puntos clave
* `objectUniversalIdentifier` especifica a qué objeto se aplica esta vista. Puede ser un objeto personalizado que hayas definido o un objeto estándar de Twenty.
-* `key` determina el tipo de vista — `ViewKey.INDEX` es la vista de lista principal para el objeto.
+* `key: ViewKey.INDEX` marca la vista como la vista de lista principal del objeto (la que abre un elemento de navegación `OBJECT`).
* `fields` controla qué columnas aparecen y en qué orden. Cada campo referencia un `fieldMetadataUniversalIdentifier`.
-* También puedes definir `filters`, `filterGroups`, `groups` y `fieldGroups` para configuraciones avanzadas.
+* También puedes declarar `filters`, `filterGroups`, `sorts`, `groups` y `fieldGroups` para configuraciones avanzadas.
* `position` controla el orden cuando existen múltiples vistas para el mismo objeto.
+## Propiedades opcionales
+
+| Propiedad | Valores | Descripción |
+| ----------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
+| `type` | `ViewType.TABLE` (predeterminado), `ViewType.KANBAN`, `ViewType.CALENDAR` | Cómo se presentan los registros. (`FIELDS_WIDGET` / `TABLE_WIDGET` también existen, pero son usados internamente por los widgets de diseño de página). |
+| `visibility` | `ViewVisibility.WORKSPACE` (predeterminado), `ViewVisibility.UNLISTED` | Si la vista se muestra para todo el espacio de trabajo o se oculta en los selectores. |
+| `openRecordIn` | `ViewOpenRecordIn.SIDE_PANEL` (predeterminado), `ViewOpenRecordIn.RECORD_PAGE` | Dónde se abre un registro al hacer clic en él. |
+| `criterios de ordenación` | `{ fieldMetadataUniversalIdentifier, direction: ViewSortDirection.ASC \| DESC }[]` | Orden de clasificación predeterminado. |
+| `isCompact` | `boolean` | Visualización compacta de filas. |
+| `mainGroupByFieldMetadataUniversalIdentifier` + `shouldHideEmptyGroups` | — | Agrupar registros (por ejemplo, columnas de kanban) por un campo. |
+| `kanbanAggregateOperation`, `kanbanAggregateOperationFieldMetadataUniversalIdentifier`, `kanbanColumnWidth` | `AggregateOperations.*` | Agregados y tamaño de columnas kanban. |
+| `calendarLayout`, `calendarFieldMetadataUniversalIdentifier` | `ViewCalendarLayout.DAY` / `WEEK` / `MONTH` | Vistas de calendario: diseño y el campo de fecha que posiciona los registros. |
+
+Todos los enums anteriores se exportan desde `twenty-sdk/define`.
+
## Filtros
Una vista puede incluir filtros preaplicados. Cada filtro tiene tres coordenadas: el **campo** que se está filtrando, el **operando** (cómo comparar) y el **valor** (contra qué comparar). Las tres deben alinearse: usar un operando que no aplique a un tipo de campo será rechazado en el momento de la sincronización.
```ts
-import { ViewFilterOperand } from 'twenty-shared/types';
+import { ViewFilterOperand } from 'twenty-sdk/define';
filters: [
{
diff --git a/packages/twenty-docs/l/es/developers/extend/apps/logic/logic-functions.mdx b/packages/twenty-docs/l/es/developers/extend/apps/logic/logic-functions.mdx
index 54f8780257..5af78c98b4 100644
--- a/packages/twenty-docs/l/es/developers/extend/apps/logic/logic-functions.mdx
+++ b/packages/twenty-docs/l/es/developers/extend/apps/logic/logic-functions.mdx
@@ -51,8 +51,12 @@ export default defineLogicFunction({
```
Tipos de desencadenadores disponibles:
-* **httpRoute**: Expone tu función en una ruta y método HTTP **bajo el endpoint `/s/`**:
-> p. ej., `path: '/post-card/create'` se puede invocar en `https://your-twenty-server.com/s/post-card/create`
+* **httpRoute**: Expone tu función en una ruta HTTP y método en la **URL base de las funciones de tu espacio de trabajo** — el valor Veinte inyectos como `TWENTY_FUNCTIONS_URL` (en la nube veinte, un dominio dedicado por área de trabajo):
+> p. ej., `path: '/post-card/create'` se puede invocar en `https://your-workspace.withtwenty.com/post-card/create`
+
+
+El prefijo heredado `/s/` (`https://your-twenty-server.com/s/post-card/create`) está \*\*obsoleto en 20 nubes y será desactivado en **2026-07-24**. Sigue disponible para instancias locales y autosuficientes que no configuran un dominio de funciones aisladas — use `TWENTY_FUNCTIONS_URL` cuando está definido. y vuelve a `\/s/\` de lo contrario.
+
Para invocar una función de lógica activada por una ruta desde un componente de frontend (headless), consulta [Llamar a una función de lógica](/l/es/developers/extend/apps/layout/front-components#calling-a-logic-function).
diff --git a/packages/twenty-docs/l/es/developers/extend/apps/logic/overview.mdx b/packages/twenty-docs/l/es/developers/extend/apps/logic/overview.mdx
index a7475eedf6..a52379f229 100644
--- a/packages/twenty-docs/l/es/developers/extend/apps/logic/overview.mdx
+++ b/packages/twenty-docs/l/es/developers/extend/apps/logic/overview.mdx
@@ -42,7 +42,7 @@ Una función de lógica selecciona uno o más disparadores: cada entrada a conti
| Disparador | Cuándo se ejecuta | Configuración |
| ------------------------------- | --------------------------------------------------------------- | ------------------------------- |
-| **Ruta HTTP** | Una solicitud llega a tu endpoint `/s/\` | `httpRouteTriggerSettings` |
+| **Ruta HTTP** | Una solicitud llega a la URL pública de tu función | `httpRouteTriggerSettings` |
| **Cron** | Coincide una expresión CRON | `cronTriggerSettings` |
| **Evento de base de datos** | Se crea, actualiza o elimina un registro del espacio de trabajo | `databaseEventTriggerSettings` |
| **Herramienta de IA** | Una funcionalidad de IA de Twenty decide llamar a tu función | `toolTriggerSettings` |
diff --git a/packages/twenty-docs/l/es/developers/extend/apps/operations/cli.mdx b/packages/twenty-docs/l/es/developers/extend/apps/operations/cli.mdx
index 7a26b76116..e22e17b3da 100644
--- a/packages/twenty-docs/l/es/developers/extend/apps/operations/cli.mdx
+++ b/packages/twenty-docs/l/es/developers/extend/apps/operations/cli.mdx
@@ -4,7 +4,25 @@ description: Comandos de `yarn twenty` para ejecutar funciones, transmitir regis
icon: terminal
---
-Más allá de `dev`, `dev:build`, `dev:add` y `dev:typecheck`, la CLI de `yarn twenty` proporciona comandos para ejecutar funciones, ver registros y gestionar instalaciones de aplicaciones.
+La CLI de `yarn twenty` es tu interfaz para todo lo relacionado con la aplicación. Lista completa de comandos:
+
+| Comando | Qué hace | Documentado en |
+| ----------------------------------------------- | --------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- |
+| `dev` | Supervisar archivos fuente y sincronizar en vivo los cambios | [Inicio rápido](/l/es/developers/extend/apps/getting-started/quick-start) |
+| `plan` | Previsualizar cambios de metadatos sin aplicarlos | [Sincronización y recuperación](/l/es/developers/extend/apps/operations/sync-and-recovery#previewing-changes-plan) |
+| `apply` | Aplicar cambios de metadatos después de mostrar el plan | [Sincronización y recuperación](/l/es/developers/extend/apps/operations/sync-and-recovery) |
+| `dev:build` | Compilar la aplicación y generar el cliente de la API (`--tarball` para empaquetar un `.tgz`) | [Publicación](/l/es/developers/extend/apps/operations/publishing) |
+| `dev:typecheck` | Ejecutar la comprobación de tipos de TypeScript | [Pruebas](/l/es/developers/extend/apps/operations/testing) |
+| `dev:add` | Crear una nueva entidad con scaffolding | [Scaffolding](/l/es/developers/extend/apps/getting-started/scaffolding) |
+| `dev:generate-client` | Regenerar el cliente de API tipado | esta página |
+| `dev:function:exec` / `dev:function:logs` | Ejecutar funciones y transmitir sus registros | esta página |
+| `dev:translations-extract` | Extraer cadenas traducibles en catálogos de `locales/` | [Traducciones](/l/es/developers/extend/apps/translations/overview) |
+| `dev:catalog-sync` | Activar la sincronización del catálogo del marketplace | [Publicación](/l/es/developers/extend/apps/operations/publishing#how-marketplace-discovery-works) |
+| `app:publish` / `app:install` / `app:uninstall` | Ciclo de vida de la publicación | [Publicación](/l/es/developers/extend/apps/operations/publishing) y esta página |
+| `docker:*` | Administrar el contenedor del servidor local de Twenty | [Servidor local](/l/es/developers/extend/apps/getting-started/local-server) |
+| `remote:*` | Administrar conexiones de servidor | esta página |
+
+Todos los comandos aceptan `-r, --remote \` para dirigirse a un remoto específico en lugar del predeterminado.
## Ejecutar funciones (`yarn twenty dev:function:exec`)
@@ -20,8 +38,9 @@ yarn twenty dev:function:exec -u e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf
# Pass a JSON payload
yarn twenty dev:function:exec -n create-new-post-card -p '{"name": "Hello"}'
-# Execute the post-install function
+# Execute the install hooks
yarn twenty dev:function:exec --postInstall
+yarn twenty dev:function:exec --preInstall
```
## Ver registros de funciones (`yarn twenty dev:function:logs`)
@@ -100,6 +119,12 @@ yarn twenty remote:list
# Set the active remote
yarn twenty remote:use
+
+# Check that the active remote's authentication is still valid
+yarn twenty remote:status
+
+# Remove a remote
+yarn twenty remote:remove
```
Tus credenciales se almacenan en `~/.twenty/config.json`.
diff --git a/packages/twenty-docs/l/es/developers/extend/apps/operations/publishing.mdx b/packages/twenty-docs/l/es/developers/extend/apps/operations/publishing.mdx
index b538b50974..8924f6a7c9 100644
--- a/packages/twenty-docs/l/es/developers/extend/apps/operations/publishing.mdx
+++ b/packages/twenty-docs/l/es/developers/extend/apps/operations/publishing.mdx
@@ -229,7 +229,7 @@ yarn twenty dev:catalog-sync
# yarn twenty dev:catalog-sync --remote production
```
-Los metadatos que se muestran en el marketplace provienen de tu configuración de `defineApplication()` — campos como `displayName`, `description`, `author`, `category`, `logoUrl`, `screenshots`, `aboutDescription`, `websiteUrl` y `termsUrl`.
+Los metadatos que se muestran en el marketplace provienen de tu configuración de `defineApplication()`; consulta [Metadatos del marketplace](#marketplace-metadata) arriba.
Si tu aplicación no define un `aboutDescription` en `defineApplication()`, el marketplace usará automáticamente el `README.md` de tu paquete en npm como el contenido de la página Acerca de. Esto significa que puedes mantener un único README tanto para npm como para el marketplace de Twenty. Si quieres una descripción diferente en el marketplace, establece explícitamente `aboutDescription`.
diff --git a/packages/twenty-docs/l/es/developers/extend/apps/operations/sync-and-recovery.mdx b/packages/twenty-docs/l/es/developers/extend/apps/operations/sync-and-recovery.mdx
index 69a094999c..2b210d6884 100644
--- a/packages/twenty-docs/l/es/developers/extend/apps/operations/sync-and-recovery.mdx
+++ b/packages/twenty-docs/l/es/developers/extend/apps/operations/sync-and-recovery.mdx
@@ -15,33 +15,44 @@ Para la iteración local del día a día casi siempre quieres `yarn twenty dev`.
| Quieres… | Comando | Notas |
| --------------------------------------------------- | ----------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| Iterar localmente con sincronización en tiempo real | `yarn twenty dev` | Supervisa tus archivos y sincroniza en cada cambio. |
-| Sincronizar una vez y salir (CI, scripts, hooks) | `yarn twenty dev --once` | Una compilación + sincronización, luego sale. |
-| Previsualizar cambios **sin aplicarlos** | `yarn twenty dev --once --dry-run` | Calcula e imprime el diff; no escribe nada. |
+| Sincronizar una vez y salir (CI, scripts, hooks) | `yarn twenty apply` | Una compilación + sincronización, luego sale. Añade `--force` para omitir la confirmación de cambio destructivo. |
+| Previsualizar cambios **sin aplicarlos** | `yarn twenty plan` | Calcula e imprime el diff; no escribe nada. |
| Eliminar la aplicación del espacio de trabajo | `yarn twenty app:uninstall` | Agrega `--yes` para omitir la confirmación. |
| Enviar un tarball a un servidor | `yarn twenty app:publish --private` | Requiere una versión de `package.json` **estrictamente superior**; consulta [Publicación](/l/es/developers/extend/apps/operations/publishing). |
| Publicar en el marketplace (npm) | `yarn twenty app:publish` | — |
| Instalar / actualizar una versión implementada | `yarn twenty app:install` | Instala la versión actualmente implementada. |
| Borrar el servidor local y empezar desde cero | `yarn twenty docker:reset` | Elimina **todos** los datos locales: último recurso. |
+
+`yarn twenty dev --once` y `yarn twenty dev --once --dry-run` siguen funcionando como alias obsoletos de `yarn twenty apply` y `yarn twenty plan`.
+
+
### La sincronización local no necesita un aumento de versión
La regla de `version` estrictamente creciente (`VERSION_ALREADY_EXISTS` al implementar, `APP_ALREADY_INSTALLED` / `CANNOT_DOWNGRADE_APPLICATION` al instalar) se aplica a **`app:publish` / `app:install`**: la ruta de publicación. `yarn twenty dev` sincroniza tu manifiesto en su lugar y nunca requiere un cambio de versión, por lo que no necesitas tocar `package.json` para iterar. Si te encuentras aumentando la versión para probar un cambio local, estás usando la ruta de publicación cuando lo que quieres es el ciclo de desarrollo.
## Leer la salida de la sincronización
-Cada sincronización muestra los cambios de metadatos que aplicó (o aplicaría, con `--dry-run`):
+Cada sincronización imprime los cambios de metadatos que aplicó (o aplicaría, con `plan`), al estilo de Terraform: un bloque por entidad con sus atributos y luego una línea de resumen:
```text filename="Terminal"
-Metadata changes: 2 created, 1 updated, 1 deleted
- created objectMetadata rocket
- created fieldMetadata timelineActivities
- updated fieldMetadata launchedAt
- deleted pageLayout legacyTab
-✓ Synced
+ # objectMetadata "rocket" will be created
+ + icon = "IconRocket"
+ + labelSingular = "Rocket"
+ + ...
+
+ # fieldMetadata "launchedAt" will be updated
+ ~ isNullable = false -> true
+
+Plan: 2 to add, 1 to change, 1 to destroy.
+
+✓ Synced My App (4 files)
```
Este es tu primer diagnóstico: te indica exactamente qué objetos, campos y diseños cambiaron, para que puedas confirmar que una sincronización hizo lo que esperabas antes de revisar la interfaz de usuario.
+Los cambios destructivos (`to destroy`) se enumeran con lo que eliminan (p. ej., `objectMetadata "auditNote" — drops the table and all its rows`) y requieren confirmación interactiva, o `--force` en scripts.
+
Cuando una sincronización falla en una sola entidad, el error nombra la entidad implicada y su `universalIdentifier`, por ejemplo:
```text
@@ -50,39 +61,42 @@ Migration action 'create' for 'fieldMetadata' (universalIdentifier: 2020...4337)
Usa ese identificador para encontrar la entidad en tu manifiesto (y, si es necesario, en el espacio de trabajo) en lugar de adivinar cuál entra en conflicto.
-## Previsualizar cambios (simulación)
+## Previsualizar cambios (plan)
-`yarn twenty dev --once --dry-run` compila tu manifiesto, le pide al servidor el plan de migración y lo imprime, **sin aplicar nada**. Es la forma segura de responder "¿qué cambiaría esta sincronización?" antes de comprometerte a ella.
+`yarn twenty plan` compila tu manifiesto, le pide al servidor el plan de migración y lo imprime, **sin aplicar nada**. Es la forma segura de responder "¿qué cambiaría esta sincronización?" antes de comprometerte a ella.
```bash filename="Terminal"
-yarn twenty dev --once --dry-run
+yarn twenty plan
```
```text filename="Terminal"
Building manifest...
-Computing metadata diff (dry run, nothing will be applied)...
-Metadata changes: 1 created, 1 updated
- created fieldMetadata timelineActivities
- updated objectMetadata rocket
-✓ Dry run complete for My App — no changes were applied
+Computing metadata plan (read-only, nothing will be applied)...
+
+ # fieldMetadata "timelineActivities" will be created
+ + ...
+
+Plan: 1 to add, 1 to change, 0 to destroy.
+
+✓ Plan complete for My App — no changes were applied
```
-Una simulación:
+Un plan:
* **No escribe nada**: sin migración de metadatos, sin actualización del registro de la aplicación, sin cambios de roles/pestañas predeterminados y sin generación del cliente de la API.
* Devuelve el **mismo diff** que aplicaría una sincronización real, para que puedas revisar por adelantado las entidades creadas/actualizadas/eliminadas.
* Es útil antes de un cambio arriesgado, al revisar un cambio generado por IA o en un script que deba fallar si está a punto de producirse un cambio inesperado.
-Una simulación solo previsualiza cambios de **metadatos** y requiere que la aplicación se haya sincronizado al menos una vez (para que el espacio de trabajo la conozca). Si la ejecutas con una aplicación que nunca se sincronizó, el servidor indicará que la aplicación no está instalada; ejecuta `yarn twenty dev` una vez primero.
+Un plan solo previsualiza cambios de **metadatos** y requiere que la aplicación se haya sincronizado al menos una vez (para que el espacio de trabajo la conozca). Si la ejecutas con una aplicación que nunca se sincronizó, el servidor indicará que la aplicación no está instalada; ejecuta `yarn twenty dev` una vez primero.
## Escalera de recuperación
Cuando los metadatos locales parezcan incorrectos, ve escalando en este orden y detente en cuanto te hayas desbloqueado. Cada paso es más disruptivo que el anterior.
-1. **Volver a sincronizar.** Ejecuta `yarn twenty dev --once` de nuevo. Las sincronizaciones son idempotentes: volver a ejecutar un manifiesto limpio es seguro y suele resolver un problema transitorio.
-2. **Previsualizar el plan.** Ejecuta `yarn twenty dev --once --dry-run` para ver exactamente qué pretende cambiar la siguiente sincronización, sin aplicarlo.
+1. **Volver a sincronizar.** Ejecuta `yarn twenty apply` de nuevo. Las sincronizaciones son idempotentes: volver a ejecutar un manifiesto limpio es seguro y suele resolver un problema transitorio.
+2. **Previsualizar el plan.** Ejecuta `yarn twenty plan` para ver exactamente qué pretende cambiar la siguiente sincronización, sin aplicarlo.
3. Lee el error identificado. Un conflicto suele señalar un identificador duplicado o reutilizado.
4. **Desinstalar y volver a instalar.** `yarn twenty app:uninstall`, luego vuelve a sincronizar (`yarn twenty dev`). Esto reconstruye los metadatos de la aplicación desde cero manteniendo intacto el resto de tu espacio de trabajo.
5. **Restablecimiento completo (último recurso).** `yarn twenty docker:reset`, luego vuelve a sembrar los datos y a sincronizar.
diff --git a/packages/twenty-docs/l/es/developers/extend/apps/operations/testing.mdx b/packages/twenty-docs/l/es/developers/extend/apps/operations/testing.mdx
index c15ba57c69..b2ec9d2673 100644
--- a/packages/twenty-docs/l/es/developers/extend/apps/operations/testing.mdx
+++ b/packages/twenty-docs/l/es/developers/extend/apps/operations/testing.mdx
@@ -78,6 +78,13 @@ Crea un `vitest.config.ts` en la raíz de tu aplicación:
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 ?? '';
+
+// 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({
@@ -88,66 +95,74 @@ export default defineConfig({
test: {
testTimeout: 120_000,
hookTimeout: 120_000,
+ fileParallelism: false,
include: ['src/**/*.integration-test.ts'],
- setupFiles: ['src/__tests__/setup-test.ts'],
+ globalSetup: ['src/__tests__/global-setup.ts'],
env: {
- TWENTY_API_URL: 'http://localhost:2020',
- TWENTY_API_KEY: 'your-api-key',
+ TWENTY_API_URL,
+ TWENTY_API_KEY,
},
},
});
```
-Crea un archivo de configuración que verifique que el servidor es accesible antes de ejecutar las pruebas:
+Crea un archivo de configuración global que verifique que el servidor es accesible, escriba una configuración de prueba para el SDK (`~/.twenty/config.test.json`) y sincronice la aplicación antes de que se ejecuten las pruebas:
-```ts src/__tests__/setup-test.ts
+```ts src/__tests__/global-setup.ts
import * as fs from 'fs';
import * as os from 'os';
import * as path from 'path';
-import { beforeAll } from 'vitest';
-const TWENTY_API_URL = process.env.TWENTY_API_URL ?? 'http://localhost:2020';
-const TEST_CONFIG_DIR = path.join(os.tmpdir(), '.twenty-sdk-test');
+import { appDevOnce, appUninstall } from 'twenty-sdk/cli';
+
+const APP_PATH = process.cwd();
+const CONFIG_DIR = path.join(os.homedir(), '.twenty');
+
+export async function setup() {
+ const apiUrl = process.env.TWENTY_API_URL!;
+ const apiKey = process.env.TWENTY_API_KEY!;
-beforeAll(async () => {
// Verify the server is running
- const response = await fetch(`${TWENTY_API_URL}/healthz`);
-
+ const response = await fetch(`${apiUrl}/healthz`);
if (!response.ok) {
- throw new Error(
- `Twenty server is not reachable at ${TWENTY_API_URL}. ` +
- 'Start the server before running integration tests.',
- );
+ throw new Error(`Twenty server is not reachable at ${apiUrl}.`);
}
- // Write a temporary config for the SDK
- fs.mkdirSync(TEST_CONFIG_DIR, { recursive: true });
-
+ // Write the SDK's test config (the CLI reads config.test.json when NODE_ENV=test)
+ fs.mkdirSync(CONFIG_DIR, { recursive: true });
fs.writeFileSync(
- path.join(TEST_CONFIG_DIR, 'config.json'),
+ path.join(CONFIG_DIR, 'config.test.json'),
JSON.stringify({
- remotes: {
- local: {
- apiUrl: process.env.TWENTY_API_URL,
- apiKey: process.env.TWENTY_API_KEY,
- },
- },
+ remotes: { local: { apiUrl, apiKey } },
defaultRemote: 'local',
}, null, 2),
);
-});
+
+ // Start from a clean slate, then sync the app
+ await appUninstall({ appPath: APP_PATH }).catch(() => {});
+
+ const result = await appDevOnce({ appPath: APP_PATH });
+ if (!result.success) {
+ throw new Error(`Dev sync failed: ${result.error?.message}`);
+ }
+}
+
+export async function teardown() {
+ await appUninstall({ appPath: APP_PATH });
+}
```
## APIs programáticas del SDK
La subruta `twenty-sdk/cli` exporta funciones que puedes invocar directamente desde el código de pruebas:
-| Función | Descripción |
-| -------------- | ------------------------------------------------------------ |
-| `appBuild` | Compilar la aplicación y opcionalmente empaquetar un tarball |
-| `appDeploy` | Subir un tarball al servidor |
-| `appInstall` | Instalar la aplicación en el espacio de trabajo activo |
-| `appUninstall` | Desinstalar la aplicación del espacio de trabajo activo |
+| Función | Descripción |
+| -------------- | -------------------------------------------------------------------------- |
+| `appBuild` | Compilar la aplicación y opcionalmente empaquetar un tarball |
+| `appDeploy` | Subir un tarball al servidor |
+| `appDevOnce` | Compila y sincroniza la aplicación una vez (igual que `yarn twenty apply`) |
+| `appInstall` | Instalar la aplicación en el espacio de trabajo activo |
+| `appUninstall` | Desinstalar la aplicación del espacio de trabajo activo |
Cada función devuelve un objeto de resultado con `success: boolean` y `data` o `error`.
@@ -238,64 +253,10 @@ También puedes ejecutar la comprobación de tipos en tu aplicación sin ejecuta
yarn twenty dev:typecheck
```
-Esto ejecuta `tsc --noEmit` e informa cualquier error de tipo.
+Esto ejecuta `tsc --noEmit` contra el `tsconfig.json` de tu aplicación e informa cualquier error de tipo. Las aplicaciones generadas también incluyen un script `yarn typecheck` que también cubre los archivos de prueba (`tsconfig.spec.json`).
## CI con GitHub Actions
-El generador crea un flujo de trabajo de GitHub Actions listo para usar en `.github/workflows/ci.yml`. Ejecuta tus pruebas de integración automáticamente en cada push a `main` y en los pull requests.
+El generador crea un flujo de trabajo listo para usar en `.github/workflows/ci.yml`. En cada push a `main` y en cada pull request, inicia un servidor efímero de Twenty en el runner (mediante la acción `twentyhq/twenty/.github/actions/spawn-twenty-app-dev-test`), luego ejecuta `yarn lint`, `yarn typecheck`, `yarn test:unit` y `yarn test` con `TWENTY_API_URL` / `TWENTY_API_KEY` apuntando a ese servidor. No se requieren secretos y puedes fijar la versión del servidor mediante la variable de entorno `TWENTY_VERSION` en la parte superior del flujo de trabajo.
-El flujo de trabajo:
-
-1. Obtiene tu código
-2. Inicia un servidor temporal de Twenty usando la acción `twentyhq/twenty/.github/actions/spawn-twenty-docker-image`
-3. Instala las dependencias con `yarn install --immutable`
-4. Ejecuta `yarn test` con `TWENTY_API_URL` y `TWENTY_API_KEY` inyectados a partir de las salidas de la acción
-
-```yaml .github/workflows/ci.yml
-name: CI
-
-on:
- push:
- branches:
- - main
- pull_request: {}
-
-env:
- TWENTY_VERSION: latest
-
-jobs:
- test:
- runs-on: ubuntu-latest
- steps:
- - name: Checkout
- uses: actions/checkout@v4
-
- - name: Spawn Twenty instance
- id: twenty
- uses: twentyhq/twenty/.github/actions/spawn-twenty-docker-image@main
- with:
- twenty-version: ${{ env.TWENTY_VERSION }}
- github-token: ${{ secrets.GITHUB_TOKEN }}
-
- - name: Enable Corepack
- run: corepack enable
-
- - name: Setup Node.js
- uses: actions/setup-node@v4
- with:
- node-version-file: '.nvmrc'
- cache: 'yarn'
-
- - name: Install dependencies
- run: yarn install --immutable
-
- - name: Run integration tests
- run: yarn test
- env:
- TWENTY_API_URL: ${{ steps.twenty.outputs.server-url }}
- TWENTY_API_KEY: ${{ steps.twenty.outputs.access-token }}
-```
-
-No necesitas configurar secretos: la acción `spawn-twenty-docker-image` inicia un servidor efímero de Twenty directamente en el runner y devuelve los detalles de conexión. El secreto `GITHUB_TOKEN` lo proporciona GitHub automáticamente.
-
-Para fijar una versión específica de Twenty en lugar de `latest`, cambia la variable de entorno `TWENTY_VERSION` al inicio del flujo de trabajo.
+Consulta [Publicación → CI/CD automatizado](/l/es/developers/extend/apps/operations/publishing#automated-cicd-scaffolded-workflows) para ver una guía completa de ambos flujos de trabajo generados (`ci.yml` y la canalización de despliegue `cd.yml`).
diff --git a/packages/twenty-docs/l/es/developers/extend/apps/tutorials/document-generator/building-the-ui.mdx b/packages/twenty-docs/l/es/developers/extend/apps/tutorials/document-generator/building-the-ui.mdx
index 22b0d5accb..bdf81e4cac 100644
--- a/packages/twenty-docs/l/es/developers/extend/apps/tutorials/document-generator/building-the-ui.mdx
+++ b/packages/twenty-docs/l/es/developers/extend/apps/tutorials/document-generator/building-the-ui.mdx
@@ -91,9 +91,11 @@ const GenerateDocumentForm = () => {
}, []);
const generate = async () => {
- const apiBaseUrl = process.env.TWENTY_API_URL;
+ // Prefer the injected functions URL; fall back to the legacy /s prefix (self-hosted/local)
+ const functionsBaseUrl =
+ process.env.TWENTY_FUNCTIONS_URL || `${process.env.TWENTY_API_URL}/s`;
const token = process.env.TWENTY_APP_ACCESS_TOKEN ?? process.env.TWENTY_API_KEY;
- const res = await fetch(`${apiBaseUrl}/s/documents/generate`, {
+ const res = await fetch(`${functionsBaseUrl}/documents/generate`, {
method: 'POST',
headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` },
body: JSON.stringify({ templateId, recordId }),
@@ -185,7 +187,9 @@ const DocumentViewer = () => {
const recordId = useFrontComponentExecutionContext((c) => c.recordId ?? null);
// ...load { content, file } for recordId, then derive the links:
const pdfUrl = document.file?.[0]?.url;
- const webUrl = `${process.env.TWENTY_API_URL ?? ''}/s/documents/view?id=${recordId}`;
+ const functionsBaseUrl =
+ process.env.TWENTY_FUNCTIONS_URL || `${process.env.TWENTY_API_URL ?? ''}/s`;
+ const webUrl = `${functionsBaseUrl}/documents/view?id=${recordId}`;
// Render the template body, plus quick links to the web page and the PDF.
// Links open in a new tab so they don't navigate the embedded component.
diff --git a/packages/twenty-docs/l/es/developers/extend/apps/tutorials/document-generator/http-routes.mdx b/packages/twenty-docs/l/es/developers/extend/apps/tutorials/document-generator/http-routes.mdx
index d32ce2dc7b..1eb032df30 100644
--- a/packages/twenty-docs/l/es/developers/extend/apps/tutorials/document-generator/http-routes.mdx
+++ b/packages/twenty-docs/l/es/developers/extend/apps/tutorials/document-generator/http-routes.mdx
@@ -9,8 +9,15 @@ El mismo manejador también puede responder a peticiones HTTP. Añadiremos dos r
* un endpoint **POST** para generar un documento, y
* un endpoint público **GET** que renderiza un documento como una página web imprimible.
-Ambos usan `httpRouteTriggerSettings`. Las rutas de la aplicación se sirven bajo `/s` en tu servidor
-Veenty (por ejemplo, `http://localhost:2020/s/documents/generate`).
+Ambos usan `httpRouteTriggerSettings`. En el servidor dev local, las rutas de la aplicación son
+servidas bajo el prefijo `/s` (por ejemplo, `http://localhost:2020/s/documents/generate`).
+
+
+En 20 nubes las rutas se sirven en el dominio
+de funciones dedicadas del espacio de trabajo — la URL Veinte inyectos como `TWENTY_FUNCTIONS_URL`, sin prefijo `/s`. El prefijo
+`/s` está obsoleto allí y sólo permanece para instancias locales y autoalojadas.
+Ver [Llamar a una función lógica](/l/es/developers/extend/apps/layout/front-components#calling-a-logic-function).
+
## Ruta POST — generar bajo demanda
diff --git a/packages/twenty-docs/l/es/developers/extend/apps/tutorials/document-generator/publishing.mdx b/packages/twenty-docs/l/es/developers/extend/apps/tutorials/document-generator/publishing.mdx
index a2744fe836..8381f52926 100644
--- a/packages/twenty-docs/l/es/developers/extend/apps/tutorials/document-generator/publishing.mdx
+++ b/packages/twenty-docs/l/es/developers/extend/apps/tutorials/document-generator/publishing.mdx
@@ -77,11 +77,11 @@ Ejecuta las mismas puertas que CI hace:
yarn lint # oxlint
yarn typecheck # tsgo
yarn test:unit # unit tests
-yarn twenty dev --once --dry-run # preview the metadata diff
+yarn twenty plan # preview the metadata diff
```
-La ejecución seca imprime exactamente lo que cambiaría en el servidor sin aplicarlo —
-una buena comprobación final de sanidad. Ver
+El plan muestra exactamente qué cambiaría en el servidor sin aplicarlos —
+una buena comprobación final. Ver
[Testing](/l/es/developers/extend/apps/operations/testing) y
[Sincronizando y recuperando](/l/es/developers/extend/apps/operations/sync-and-recovery).
diff --git a/packages/twenty-docs/l/fr/developers/extend/apps/config/install-hooks.mdx b/packages/twenty-docs/l/fr/developers/extend/apps/config/install-hooks.mdx
index 3878b82a97..af69618f01 100644
--- a/packages/twenty-docs/l/fr/developers/extend/apps/config/install-hooks.mdx
+++ b/packages/twenty-docs/l/fr/developers/extend/apps/config/install-hooks.mdx
@@ -4,9 +4,9 @@ description: Exécutez de la logique avant ou après l'installation — initiali
icon: wrench
---
-Les hooks d'installation sont des fonctions logiques spéciales qui s'exécutent pendant le cycle de vie d'installation ou de mise à niveau. Ils partagent le même environnement d'exécution que les [fonctions logiques](/l/fr/developers/extend/apps/logic/logic-functions) classiques et reçoivent un `InstallPayload`, mais ils sont déclarés avec leurs propres fonctions de définition — `definePostInstallLogicFunction()` et `definePreInstallLogicFunction()` — et ne relèvent pas du modèle de déclencheur habituel (HTTP, cron, événements de base de données).
+Les hooks d'installation sont des fonctions logiques spéciales qui s'exécutent pendant le cycle de vie d'installation ou de mise à niveau. Ils partagent le même environnement d'exécution de gestionnaire que les [fonctions logiques](/l/fr/developers/extend/apps/logic/logic-functions) classiques et reçoivent un `InstallPayload` (`{ previousVersion?: string; newVersion: string }` — `previousVersion` est `undefined` lors d'une nouvelle installation), mais ils sont déclarés avec leurs propres fonctions de définition et vivent en dehors du modèle de déclencheur normal (HTTP, cron, événements de base de données).
-Chaque application peut définir **au maximum une pré-installation** et **au maximum une post-installation**. La génération du manifeste renverra une erreur si plus d'une fonction de l'un ou l'autre type est détectée.
+Chaque application peut définir **au maximum une pré-installation** et **au maximum une post-installation**. La génération du manifeste renvoie une erreur si plus d'une fonction de l'un ou l'autre type est détectée.
```
┌─────────────────────────────────────────────────────────────┐
@@ -19,111 +19,59 @@ Chaque application peut définir **au maximum une pré-installation** et **au ma
└─────────────────────────────────────────────────────────────┘
```
-
-
+## En un coup d'œil
-Une fonction post-installation s'exécute automatiquement une fois l'installation de votre application sur un espace de travail terminée. Le serveur l'exécute **après** que les métadonnées de l'application ont été synchronisées et que le client du SDK a été généré, afin que l'espace de travail soit entièrement prêt à l'emploi et que le nouveau schéma soit en place. Les cas d'utilisation typiques incluent le préremplissage de données par défaut, la création d'enregistrements initiaux, la configuration des paramètres de l'espace de travail ou le provisionnement de ressources sur des services tiers.
+| | `definePreInstallLogicFunction` | `definePostInstallLogicFunction` |
+| ------------------- | ------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| Exécutions | Avant la migration des métadonnées — le schéma et les données **précédents** sont toujours intacts | Après la migration et la génération du SDK — le **nouveau** schéma est en place |
+| Exécution | Toujours synchrone ; bloque l'installation | Asynchrone par défaut (mis en file d'attente, 3 nouvelles tentatives) ; mode synchrone en option via `shouldRunSynchronously: true` |
+| En cas d'échec | L'installation est **abandonnée** avant toute modification du schéma | Asynchrone : nouvelle tentative jusqu'à 3 fois. Synchrone : l'appelant reçoit `POST_INSTALL_ERROR` (les modifications de schéma **ne** sont pas annulées) |
+| Utilisation typique | Sauvegarder ou corriger les données qu'une migration ferait perdre ; refuser une mise à niveau risquée en levant une exception | Initialiser des données par défaut, configurer l'espace de travail, enregistrer des ressources externes |
-```ts src/logic-functions/post-install.ts
-import { definePostInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define';
+**Règle empirique :** privilégiez post-install par défaut. Ne recourez à la pré-installation que lorsque la migration elle-même est destructive et que vous devez intercepter l'état précédent avant qu'il ne disparaisse.
-const handler = async (payload: InstallPayload): Promise => {
- console.log('Post install logic function executed successfully!', payload.previousVersion);
-};
+| Vous souhaitez... | Utiliser |
+| -------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- |
+| Initialiser des données, configurer l'espace de travail, enregistrer des ressources externes | `post-install` |
+| Travail de longue durée qui ne doit pas bloquer la réponse d'installation | `post-install` (mode asynchrone par défaut, avec nouvelles tentatives du worker) |
+| Configuration rapide dont l'appelant dépend immédiatement après le retour de l'installation | `post-install` avec `shouldRunSynchronously: true` |
+| Lire ou sauvegarder des données que la migration à venir ferait perdre | `pre-install` |
+| Rejeter une mise à niveau qui corromprait des données existantes | `pre-install` (lancer une exception depuis le gestionnaire) |
+| Réconciliation à chaque mise à niveau | N'importe quel hook avec `shouldRunOnVersionUpgrade: true` |
-export default definePostInstallLogicFunction({
- universalIdentifier: 'f7a2b9c1-3d4e-5678-abcd-ef9876543210',
- name: 'post-install',
- description: 'Runs after installation to set up the application.',
- timeoutSeconds: 300,
- shouldRunOnVersionUpgrade: false,
- shouldRunSynchronously: false,
- handler,
-});
-```
+## Comportement partagé par les deux hooks
-Vous pouvez également exécuter manuellement la fonction de post-installation à tout moment à l'aide de la CLI :
+* La configuration est une configuration `defineLogicFunction` moins les paramètres de déclencheur, plus `shouldRunOnVersionUpgrade`.
+* **Quand il s'exécute** : uniquement lors des nouvelles installations, par défaut. Définissez `shouldRunOnVersionUpgrade: true` pour qu'il s'exécute également lors des mises à niveau. Utilisez `previousVersion` / `newVersion` pour bifurquer selon le chemin de mise à niveau.
+* **L'idempotence est importante** : le post-install asynchrone peut être relancé, et chaque hook est réexécuté lors des mises à niveau lorsque `shouldRunOnVersionUpgrade` est activé.
+* L'environnement habituel des fonctions logiques (`APPLICATION_ID`, `APP_ACCESS_TOKEN`, `API_URL`) est injecté, ce qui vous permet d'appeler l'API Twenty avec le jeton de votre application.
+* Le hook est rattaché automatiquement au manifeste de l'application au moment de la compilation (`preInstallLogicFunction` / `postInstallLogicFunction`) — rien à référencer dans [`defineApplication()`](/l/fr/developers/extend/apps/config/application).
+* La valeur par défaut de `timeoutSeconds` est 300 pour permettre des tâches de configuration plus longues comme l'initialisation des données.
+* **Non exécuté en mode dev** : `yarn twenty dev` ignore le flux d'installation et synchronise directement les fichiers, donc les hooks ne s'exécutent jamais dans ce cas. Déclenchez-les manuellement à la place :
```bash filename="Terminal"
yarn twenty dev:function:exec --postInstall
-```
-
-Points clés :
-* Les fonctions de post-installation utilisent `definePostInstallLogicFunction()` — une variante spécialisée qui omet les paramètres de déclencheur (`cronTriggerSettings`, `databaseEventTriggerSettings`, `httpRouteTriggerSettings`, `toolTriggerSettings`, `workflowActionTriggerSettings`).
-* Le gestionnaire reçoit un `InstallPayload` avec `{ previousVersion?: string; newVersion: string }` — `newVersion` est la version en cours d'installation, et `previousVersion` est la version précédemment installée (ou `undefined` lors d'une nouvelle installation). Utilisez ces valeurs pour distinguer les nouvelles installations des mises à niveau et pour exécuter une logique de migration spécifique à la version.
-* **Quand le hook s'exécute** : uniquement lors des nouvelles installations, par défaut. Passez `shouldRunOnVersionUpgrade: true` si vous souhaitez également qu'il s'exécute lorsque l'application est mise à niveau depuis une version précédente. S'il est omis, l'indicateur vaut `false` par défaut et les mises à niveau ignorent le hook.
-* **Modèle d'exécution — asynchrone par défaut, synchrone sur opt-in** : l'indicateur `shouldRunSynchronously` contrôle *comment* post-install est exécuté.
- * `shouldRunSynchronously: false` *(par défaut)* — le hook est **placé dans la file de messages** avec `retryLimit: 3` et s'exécute de manière asynchrone dans un worker. La réponse d'installation est renvoyée dès que la tâche est mise en file d'attente, de sorte qu'un gestionnaire lent ou défaillant ne bloque pas l'appelant. Le worker réessaiera jusqu'à trois fois. **Utilisez ceci pour les tâches de longue durée** — initialisation de grands jeux de données, appel d'API tierces lentes, provisionnement de ressources externes, tout ce qui pourrait dépasser une fenêtre de réponse HTTP raisonnable.
- * `shouldRunSynchronously: true` — le hook est exécuté **en ligne pendant le flux d'installation** (même exécuteur que pre-install). La requête d'installation est bloquée jusqu'à la fin du gestionnaire et, s'il lève une exception, l'appelant de l'installation reçoit un `POST_INSTALL_ERROR`. Aucun réessai automatique. **Utilisez ceci pour un travail rapide devant être terminé avant la réponse** — par exemple, émettre une erreur de validation à l'utilisateur, ou une configuration rapide dont le client dépendra immédiatement après le retour de l'appel d'installation. Gardez à l'esprit que la migration des métadonnées a déjà été appliquée au moment où post-install s'exécute, donc un échec en mode synchrone ne **rétablit pas** les modifications du schéma — il ne fait qu'exposer l'erreur.
-* Assurez-vous que votre gestionnaire est idempotent. En mode asynchrone, la file peut réessayer jusqu'à trois fois ; dans les deux modes, le hook peut s'exécuter à nouveau lors des mises à niveau lorsque `shouldRunOnVersionUpgrade: true`.
-* Les variables d'environnement `APPLICATION_ID`, `APP_ACCESS_TOKEN` et `API_URL` sont disponibles dans le gestionnaire (comme pour toute autre fonction logique), vous pouvez donc appeler l'API Twenty avec un jeton d'accès d'application limité à votre application.
-* Une seule fonction de post-installation est autorisée par application. La génération du manifeste renverra une erreur si plusieurs sont détectées.
-* Les propriétés `universalIdentifier`, `shouldRunOnVersionUpgrade` et `shouldRunSynchronously` de la fonction sont automatiquement attachées au manifeste de l'application sous le champ `postInstallLogicFunction` pendant le build — vous n'avez pas besoin de les référencer dans [`defineApplication()`](/l/fr/developers/extend/apps/config/application).
-* Le délai d'expiration par défaut est défini à 300 secondes (5 minutes) pour permettre des tâches de configuration plus longues comme l'initialisation des données.
-* **Non exécuté en mode dev** : lorsqu'une application est enregistrée localement (via `yarn twenty dev`), le serveur saute complètement le flux d'installation et synchronise les fichiers directement via le watcher de la CLI — ainsi, post-install ne s'exécute jamais en mode dev, quel que soit `shouldRunSynchronously`. Utilisez `yarn twenty dev:function:exec --postInstall` pour le déclencher manuellement sur un espace de travail en cours d'exécution.
-
-
-
-
-Une fonction de pré-installation s'exécute automatiquement pendant l'installation, **avant que la migration des métadonnées de l'espace de travail soit appliquée**. Elle partage la même forme de payload que post-install (`InstallPayload`), mais elle est positionnée plus tôt dans le flux d'installation afin de pouvoir préparer l'état dont dépend la migration à venir — les usages typiques incluent la sauvegarde de données, la validation de la compatibilité avec le nouveau schéma, ou l'archivage d'enregistrements sur le point d'être restructurés ou supprimés.
-
-```ts src/logic-functions/pre-install.ts
-import { definePreInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define';
-
-const handler = async (payload: InstallPayload): Promise => {
- console.log('Pre install logic function executed successfully!', payload.previousVersion);
-};
-
-export default definePreInstallLogicFunction({
- universalIdentifier: 'a1b2c3d4-5678-90ab-cdef-1234567890ab',
- name: 'pre-install',
- description: 'Runs before installation to prepare the application.',
- timeoutSeconds: 300,
- shouldRunOnVersionUpgrade: true,
- handler,
-});
-```
-
-Vous pouvez également exécuter manuellement la fonction de pré-installation à tout moment à l'aide de la CLI :
-
-```bash filename="Terminal"
yarn twenty dev:function:exec --preInstall
```
-Points clés :
-* Les fonctions de pré-installation utilisent `definePreInstallLogicFunction()` — même configuration spécialisée que post-install, simplement attachée à un autre emplacement du cycle de vie.
-* Les gestionnaires de pré- et post-install reçoivent le même type `InstallPayload` : `{ previousVersion?: string; newVersion: string }`. Importez-le une fois et réutilisez-le pour les deux hooks.
-* **Quand le hook s'exécute** : positionné juste avant la migration des métadonnées de l'espace de travail (`synchronizeFromManifest`). Avant l'exécution, le serveur lance une « synchronisation réduite » purement additive qui enregistre la fonction de pré-installation de la **nouvelle** version dans les métadonnées de l'espace de travail — rien d'autre n'est modifié — puis l'exécute. Comme cette synchronisation est uniquement additive, les objets, champs et données de la version précédente sont toujours intacts lorsque votre gestionnaire s'exécute : vous pouvez lire et sauvegarder en toute sécurité l'état pré-migration.
-* **Modèle d'exécution** : la pré-installation est exécutée **de façon synchrone** et **bloque l'installation**. Si le gestionnaire lève une exception, l'installation est abandonnée avant que des modifications du schéma ne soient appliquées — l'espace de travail reste sur la version précédente dans un état cohérent. C'est intentionnel : la pré-installation est votre dernière chance de refuser une mise à niveau risquée.
-* Comme pour post-install, une seule fonction de pré-installation est autorisée par application. Elle est automatiquement attachée au manifeste de l'application sous `preInstallLogicFunction` pendant le build.
-* **Non exécuté en mode dev** : comme pour post-install — le flux d'installation est entièrement ignoré pour les applications enregistrées localement, donc la pré-installation ne s'exécute jamais sous `yarn twenty dev`. Utilisez `yarn twenty dev:function:exec --preInstall` pour la déclencher manuellement.
+
+
-
-
-
-Les deux hooks font partie du même flux d'installation et reçoivent le même `InstallPayload`. La différence tient au **moment** où ils s'exécutent par rapport à la migration des métadonnées de l'espace de travail, et cela change les données qu'ils peuvent manipuler en toute sécurité.
-
-La pré-installation est toujours **synchrone** (elle bloque l'installation et peut l'interrompre). Post-install est **asynchrone par défaut** — mis en file d'attente sur un worker avec des réessais automatiques — mais peut opter pour une exécution synchrone avec `shouldRunSynchronously: true`. Voir l'accordéon `definePostInstallLogicFunction` ci-dessus pour savoir quand utiliser chaque mode.
-
-**Utilisez `post-install` pour tout ce qui nécessite l'existence du nouveau schéma.** C'est le cas le plus courant :
-
-* Initialiser des données par défaut (création d'enregistrements initiaux, de vues par défaut, de contenu de démonstration) sur des objets et champs nouvellement ajoutés.
-* Enregistrer des webhooks auprès de services tiers maintenant que l'application dispose de ses identifiants.
-* Appeler votre propre API pour finaliser une configuration qui dépend des métadonnées synchronisées.
-* Logique idempotente « assurer l'existence de cet élément » qui doit réconcilier l'état à chaque mise à niveau — à combiner avec `shouldRunOnVersionUpgrade: true`.
-
-Exemple — initialiser un enregistrement `PostCard` par défaut après l'installation :
+S'exécute une fois que votre application a terminé son installation : métadonnées synchronisées, client SDK généré, nouveau schéma interrogeable. Exemple — initialiser un enregistrement par défaut lors des nouvelles installations :
```ts src/logic-functions/post-install.ts
import { definePostInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define';
-import { createClient } from './generated/client';
+import { CoreApiClient } from 'twenty-client-sdk/core';
const handler = async ({ previousVersion }: InstallPayload): Promise => {
if (previousVersion) return; // fresh installs only
- const client = createClient();
- await client.postCard.create({
- data: { title: 'Welcome to Postcard', content: 'Your first card!' },
+ const client = new CoreApiClient();
+ await client.mutation({
+ createPostCard: {
+ __args: { data: { name: 'Welcome to Postcard', content: 'Your first card!' } },
+ id: true,
+ },
});
};
@@ -133,22 +81,28 @@ export default definePostInstallLogicFunction({
description: 'Seeds a welcome post card after install.',
timeoutSeconds: 300,
shouldRunOnVersionUpgrade: false,
+ shouldRunSynchronously: false,
handler,
});
```
-**Utilisez `pre-install` lorsqu'une migration détruirait ou corromprait autrement des données existantes.** Comme la pré-installation s'exécute sur le schéma *précédent* et qu'un échec annule la mise à niveau, c'est l'endroit approprié pour tout ce qui est risqué :
+Le drapeau `shouldRunSynchronously` contrôle le modèle d'exécution :
-* **Sauvegarder des données sur le point d'être supprimées ou restructurées** — par exemple, vous supprimez un champ en v2 et devez copier ses valeurs dans un autre champ ou les exporter vers un stockage avant l'exécution de la migration.
-* **Archiver des enregistrements qu'une nouvelle contrainte invaliderait** — par exemple, un champ devient `NOT NULL` et vous devez d'abord supprimer ou corriger les lignes avec des valeurs nulles.
-* **Valider la compatibilité et refuser la mise à niveau si les données actuelles ne peuvent pas être migrées proprement** — lancez une exception depuis le gestionnaire et l'installation s'interrompt sans appliquer de modifications. C'est plus sûr que de découvrir l'incompatibilité en cours de migration.
-* **Renommer ou réassigner les clés des données** avant une modification du schéma qui ferait perdre l'association.
+* `false` *(par défaut)* — mis en file d'attente dans la file de messages (`retryLimit: 3`) et exécuté par un worker. La réponse d'installation est renvoyée dès que la tâche est mise en file d'attente. **À utiliser pour les travaux de longue durée** — initialisation de grands ensembles de données, API tierces lentes.
+* `true` — exécuté en ligne pendant le flux d'installation. La requête d'installation est bloquée jusqu'à ce que le gestionnaire ait terminé ; une erreur levée apparaît comme `POST_INSTALL_ERROR` pour l'appelant (aucune nouvelle tentative). **À utiliser pour les travaux rapides qui doivent être terminés avant la réponse.** La migration a déjà été appliquée à ce stade, donc un échec n'annule pas les modifications du schéma — il ne fait que remonter l'erreur.
-Exemple — archiver des enregistrements avant une migration destructive :
+
+
+
+S'exécute avant la migration des métadonnées, sur le schéma **précédent** — l'endroit idéal pour sauvegarder des données qu'une migration ferait perdre, ou pour refuser une mise à niveau risquée. Avant l'exécution, le serveur effectue une « synchronisation réduite » purement additive qui enregistre uniquement la fonction de pré-installation de la nouvelle version ; tout le reste — les objets, champs et données de la version précédente — reste intact lorsque votre gestionnaire s'exécute.
+
+La pré-installation est toujours **synchrone** et bloque l'installation. Si le gestionnaire lève une exception, l'installation est abandonnée avant toute modification du schéma — l'espace de travail reste sur la version précédente dans un état cohérent. C'est intentionnel : la pré-installation est votre dernière chance de refuser une mise à niveau risquée.
+
+Exemple — copier les valeurs d'un champ hérité avant que la migration ne le supprime :
```ts src/logic-functions/pre-install.ts
import { definePreInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define';
-import { createClient } from './generated/client';
+import { CoreApiClient } from 'twenty-client-sdk/core';
const handler = async ({ previousVersion, newVersion }: InstallPayload): Promise => {
// Only the 1.x → 2.x upgrade drops the legacy `notes` field.
@@ -156,24 +110,24 @@ const handler = async ({ previousVersion, newVersion }: InstallPayload): Promise
return;
}
- const client = createClient();
- const legacyRecords = await client.postCard.findMany({
- where: { notes: { isNotNull: true } },
+ const client = new CoreApiClient();
+ const { postCards } = await client.query({
+ postCards: {
+ __args: { filter: { notes: { isNot: null } } },
+ edges: { node: { id: true, notes: true } },
+ },
});
- if (legacyRecords.length === 0) return;
-
- // Copy legacy `notes` into the new `description` field before the migration
- // drops the `notes` column. If this fails, the upgrade is aborted and the
- // workspace stays on v1 with all data intact.
- await Promise.all(
- legacyRecords.map((record) =>
- client.postCard.update({
- where: { id: record.id },
- data: { description: record.notes },
- }),
- ),
- );
+ // Copy legacy `notes` into `description` before the migration drops the
+ // column. If this fails, the upgrade aborts and the workspace stays on v1.
+ for (const { node } of postCards.edges) {
+ await client.mutation({
+ updatePostCard: {
+ __args: { id: node.id, data: { description: node.notes } },
+ id: true,
+ },
+ });
+ }
};
export default definePreInstallLogicFunction({
@@ -186,21 +140,5 @@ export default definePreInstallLogicFunction({
});
```
-**Règle générale :**
-
-| Vous souhaitez... | Utiliser |
-| ------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------ |
-| Initialiser des données par défaut, configurer l'espace de travail, enregistrer des ressources externes | `post-install` |
-| Exécuter une initialisation longue ou des appels tiers qui ne doivent pas bloquer la réponse d'installation | `post-install` (par défaut — `shouldRunSynchronously: false`, avec des réessais du worker) |
-| Exécuter une configuration rapide dont l'appelant dépendra immédiatement après le retour de l'appel d'installation | `post-install` avec `shouldRunSynchronously: true` |
-| Lire ou sauvegarder des données que la migration à venir ferait perdre | `pre-install` |
-| Rejeter une mise à niveau qui corromprait des données existantes | `pre-install` (lancer une exception depuis le gestionnaire) |
-| Exécuter une réconciliation à chaque mise à niveau | `post-install` avec `shouldRunOnVersionUpgrade: true` |
-| Effectuer une configuration ponctuelle uniquement lors de la première installation | `post-install` avec `shouldRunOnVersionUpgrade: false` (par défaut) |
-
-
-En cas de doute, privilégiez **post-install**. Ne recourez à la pré-installation que lorsque la migration elle-même est destructive et que vous devez intercepter l'état précédent avant qu'il ne disparaisse.
-
-
diff --git a/packages/twenty-docs/l/fr/developers/extend/apps/data/objects.mdx b/packages/twenty-docs/l/fr/developers/extend/apps/data/objects.mdx
index d5943b68de..0d395d4226 100644
--- a/packages/twenty-docs/l/fr/developers/extend/apps/data/objects.mdx
+++ b/packages/twenty-docs/l/fr/developers/extend/apps/data/objects.mdx
@@ -86,6 +86,22 @@ export default defineObject({
**Les champs de base sont ajoutés automatiquement.** Lorsque vous définissez un objet personnalisé, Twenty crée pour vous des champs standard comme `id`, `name`, `createdAt`, `updatedAt`, `createdBy`, `updatedBy` et `deletedAt`. Vous n’avez pas besoin de les déclarer dans votre tableau `fields` — uniquement vos champs personnalisés. Vous pouvez remplacer un champ par défaut en en déclarant un avec le même nom, mais c’est rarement une bonne idée.
+## Types de champ
+
+L’ensemble complet des valeurs de `FieldType`, exportées depuis `twenty-sdk/define` :
+
+| Catégorie | Types |
+| ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
+| Texte | `TEXT`, `RICH_TEXT`, `ARRAY` (de chaînes), `RAW_JSON` |
+| Numérique | `NUMBER` (`universalSettings.dataType` : `'float'` / `'int'` / `'bigint'`), `NUMERIC` (précision arbitraire), `RATING`, `POSITION` |
+| Dates | `DATE`, `DATE_TIME` |
+| Choix | `BOOLEAN`, `SELECT`, `MULTI_SELECT` |
+| Composés | `FULL_NAME`, `ADDRESS`, `EMAILS`, `PHONES`, `LINKS`, `CURRENCY`, `ACTOR`, `FILES` |
+| Identifiants et relations | `UUID`, `RELATION`, `MORPH_RELATION` (voir [Relations](/l/fr/developers/extend/apps/data/relations)) |
+| Système | `TS_VECTOR` (vecteur de recherche en texte intégral, géré par le serveur) |
+
+Les types composés stockent plusieurs sous-champs (par exemple `FULL_NAME` = prénom + nom de famille ; `CURRENCY` = `amountMicros` + `currencyCode`). `SELECT` et `MULTI_SELECT` nécessitent un tableau `options` comme dans l’exemple ci-dessus.
+
## Valeurs par défaut
Les valeurs par défaut de type chaîne littérale doivent être entourées de guillemets simples **à l’intérieur** de la chaîne — `defaultValue: "'Draft'"`, et non `defaultValue: "Draft"`. C’est pourquoi le champ `status` ci-dessus utilise `` `'${PostCardStatus.DRAFT}'` ``.
diff --git a/packages/twenty-docs/l/fr/developers/extend/apps/getting-started/project-structure.mdx b/packages/twenty-docs/l/fr/developers/extend/apps/getting-started/project-structure.mdx
index f78763f4db..c45693fe7e 100644
--- a/packages/twenty-docs/l/fr/developers/extend/apps/getting-started/project-structure.mdx
+++ b/packages/twenty-docs/l/fr/developers/extend/apps/getting-started/project-structure.mdx
@@ -14,26 +14,39 @@ my-twenty-app/
default-role.ts # Permissions for logic functions
constants/
universal-identifiers.ts # Auto-generated UUIDs and metadata
+ front-components/
+ main-page.tsx # Welcome page component
+ navigation-menu-items/
+ main-page.navigation-menu-item.ts # Sidebar entry for the welcome page
+ page-layouts/
+ main-page.page-layout.ts # Standalone page hosting the component
__tests__/
- setup-test.ts
- app-install.integration-test.ts
- .github/workflows/ci.yml # GitHub Actions
- public/ # Static assets
- vitest.config.ts # Test runner config
+ application-config.test.ts # Unit test
+ global-setup.ts # Integration test setup (sync + uninstall)
+ schema.integration-test.ts # Integration test against a live server
+ .github/workflows/
+ ci.yml # Lint, typecheck, unit + integration tests
+ cd.yml # Deploy + install on push to main
+ public/
+ logo.svg # Static assets
+ vitest.config.ts # Integration test runner config
+ vitest.unit.config.ts # Unit test runner config
tsconfig.json, tsconfig.spec.json
.nvmrc, .yarnrc.yml, .oxlintrc.json
- README.md, LLMS.md
+ README.md, AGENTS.md, CLAUDE.md
```
## Fichiers clés
-| Fichier / Dossier | Objectif |
-| ---------------------------------------- | -------------------------------------------------------------------------------------------- |
-| `src/application-config.ts` | **Requis.** Le fichier de configuration principal de votre application. |
-| `src/default-role.ts` | Rôle par défaut qui contrôle ce à quoi vos fonctions de logique peuvent accéder. |
-| `src/constants/universal-identifiers.ts` | UUID générés automatiquement et métadonnées de l’application (nom d’affichage, description). |
-| `src/__tests__/` | Tests d’intégration (configuration + test d’exemple). |
-| `public/` | Ressources statiques (images, polices) servies avec votre application. |
+| Fichier / Dossier | Objectif |
+| -------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
+| `src/application-config.ts` | **Requis.** Le fichier de configuration principal de votre application. |
+| `src/default-role.ts` | Rôle par défaut qui contrôle ce à quoi vos fonctions de logique peuvent accéder. |
+| `src/constants/universal-identifiers.ts` | UUID générés automatiquement et métadonnées de l’application (nom d’affichage, description). |
+| `src/front-components/`, `src/navigation-menu-items/`, `src/page-layouts/` | Une page d’accueil de démarrage : un composant frontal rendu par une mise en page de page autonome, accessible depuis la barre latérale. |
+| `src/__tests__/` | Un test unitaire plus un test d’intégration (avec sa configuration globale) qui synchronise l’application avec un serveur réel. |
+| `public/` | Ressources statiques (images, polices) servies avec votre application. |
+| `AGENTS.md` / `CLAUDE.md` | Consignes pour les agents d’écriture de code IA qui travaillent sur l’application. |
**L’organisation des fichiers vous revient.** Les dossiers ci-dessus sont des conventions — le SDK détecte les entités via une analyse AST sur les appels à `export default defineEntity(...)` quel que soit l’emplacement du fichier.
@@ -47,15 +60,18 @@ Les deux packages du SDK Twenty doivent être placés dans `devDependencies`, et
{
"dependencies": {},
"devDependencies": {
- "twenty-client-sdk": "^2.13.0",
- "twenty-sdk": "^2.13.0"
+ "twenty-client-sdk": "2.20.0",
+ "twenty-sdk": "2.20.0",
+ "twenty-ui": "1.0.0-alpha.1"
}
}
```
+Le générateur de projet fige `twenty-sdk` et `twenty-client-sdk` sur sa propre version — gardez les deux synchronisés lors de la mise à niveau.
+
* **`twenty-sdk`** fournit le CLI `twenty` ainsi que les outils de build et de scaffolding. Il ne s’exécute qu’au moment du développement et du build et n’est jamais importé par le runtime de l’application que vous publiez.
* **`twenty-client-sdk`** *est* importé par le code de votre application (`CoreApiClient`, `MetadataApiClient`, `RestApiClient`), mais Twenty le fournit au moment de l’exécution : les fonctions de logique l’obtiennent à partir d’une couche SDK générée, et les composants front le résolvent à partir de modules servis par le serveur. La copie que vous avez installée est uniquement utilisée pour la vérification de type et le build au moment du déploiement, elle n’a donc jamais besoin d’être incluse dans le bundle déployé.
-Conserver l’un ou l’autre package dans `dependencies` l’intègre dans le bundle runtime de l’application installée, où il ne fait que l’alourdir inutilement. `twenty build` émet un avertissement lorsque l’un ou l’autre est encore répertorié dans `dependencies`.
+Conserver l’un ou l’autre package dans `dependencies` l’intègre dans le bundle runtime de l’application installée, où il ne fait que l’alourdir inutilement. `twenty dev:build` émet un avertissement lorsque l’un ou l’autre est encore répertorié dans `dependencies`.
Ajoutez les dépendances runtime propres à votre application (les bibliothèques que vos fonctions logiques importent réellement à l’exécution) dans `dependencies` comme d’habitude.
diff --git a/packages/twenty-docs/l/fr/developers/extend/apps/getting-started/quick-start.mdx b/packages/twenty-docs/l/fr/developers/extend/apps/getting-started/quick-start.mdx
index e5d7da3868..0534731978 100644
--- a/packages/twenty-docs/l/fr/developers/extend/apps/getting-started/quick-start.mdx
+++ b/packages/twenty-docs/l/fr/developers/extend/apps/getting-started/quick-start.mdx
@@ -6,17 +6,17 @@ description: Créez votre première application Twenty en quelques minutes.
## Prérequis
-* **Node.js 24+** — [Télécharger](https://nodejs.org/)
+* **Node.js 24.5+** — [Télécharger](https://nodejs.org/)
* **Yarn 4** — fourni avec Node.js via Corepack. Activez-le : `corepack enable`
* **Docker** — [Télécharger](https://www.docker.com/products/docker-desktop/). Nécessaire pour exécuter un serveur Twenty local. Ignorez si vous avez déjà Twenty en cours d’exécution ailleurs.
La création d’une application Twenty comporte trois phases. Le générateur les regroupe en une seule commande pour le parcours idéal, mais chaque phase est un concept distinct — en cas d’échec, savoir dans quelle phase vous vous trouvez indique ce qu’il faut corriger.
-| Phase | Ce que vous faites | Outil | Résultat |
-| -------------------------- | ------------------------------------------------------ | ----------------------------- | ----------------------------------------------------------- |
-| **1. Génération** | Générer le code source de l’application | `npx create-twenty-app` | Un projet TypeScript sur le disque |
-| **2. Exécuter un serveur** | Démarrer un serveur Twenty vers lequel se synchroniser | Docker + `yarn twenty server` | Une instance Twenty en cours d’exécution |
-| **3. Synchroniser** | Synchroniser en direct votre code avec le serveur | `yarn twenty dev` | Vos modifications apparaissent dans l’interface utilisateur |
+| Phase | Ce que vous faites | Outil | Résultat |
+| -------------------------- | ------------------------------------------------------ | ----------------------------------- | ----------------------------------------------------------- |
+| **1. Génération** | Générer le code source de l’application | `npx create-twenty-app` | Un projet TypeScript sur le disque |
+| **2. Exécuter un serveur** | Démarrer un serveur Twenty vers lequel se synchroniser | Docker + `yarn twenty docker:start` | Une instance Twenty en cours d’exécution |
+| **3. Synchroniser** | Synchroniser en direct votre code avec le serveur | `yarn twenty dev` | Vos modifications apparaissent dans l’interface utilisateur |
---
@@ -28,7 +28,7 @@ Créez une nouvelle application à partir du modèle :
npx create-twenty-app@latest my-twenty-app
```
-On vous demandera un nom et une description — appuyez sur **Entrée** pour utiliser les valeurs par défaut. Cela génère un projet TypeScript dans `my-twenty-app/` avec un fichier de démarrage `application-config.ts`, un rôle par défaut, un workflow CI et un test d’intégration.
+Le générateur est non interactif : le nom du répertoire devient le nom de l’application. Passez `--display-name` et `--description` pour personnaliser les métadonnées générées (vous pouvez également les modifier plus tard dans `src/constants/universal-identifiers.ts`). Cela génère un projet TypeScript dans `my-twenty-app/` avec un fichier de démarrage `application-config.ts`, un rôle par défaut, des workflows CI/CD et un test d’intégration.
**Après cette phase :** vous disposez du code source d’une application sur votre machine. Elle ne s’exécute pas encore — c’est la phase 2.
@@ -38,28 +38,14 @@ On vous demandera un nom et une description — appuyez sur **Entrée** pour uti
Votre application a besoin d’un serveur Twenty vers lequel se synchroniser. Le serveur est une instance Twenty complète — interface utilisateur, API GraphQL, PostgreSQL — exécutée localement dans Docker. Votre code local envoie ses définitions à ce serveur, qui les fait apparaître dans l’interface utilisateur.
-Le générateur propose d’en démarrer un pour vous :
+Le générateur de projet en crée un pour vous : avec Docker en cours d’exécution, il récupère l’image `twentycrm/twenty-app-dev`, la démarre sur le port `2020`, et authentifie le CLI auprès de l’espace de travail de démonstration prérempli (`tim@apple.dev`) — aucune connexion requise.
-> **Souhaitez-vous configurer une instance locale de Twenty ?**
-
-* **Oui (recommandé)** — récupère l’image Docker `twentycrm/twenty-app-dev` et la démarre sur le port `2020`. Assurez-vous d’abord que Docker est en cours d’exécution.
-* **Non** — choisissez cette option si vous avez déjà un serveur Twenty auquel vous souhaitez vous connecter. Vous pourrez le connecter plus tard avec `yarn twenty remote:add`.
-
-
-

-
-
-Une fois le serveur démarré, un navigateur s’ouvre pour la connexion. Utilisez le compte de démonstration prérempli :
-
-* **E-mail :** `tim@apple.dev`
-* **Mot de passe :** `tim@apple.dev`
+Pour vous connecter à un serveur Twenty existant à la place, passez `--url \`. Les serveurs distants s’authentifient avec OAuth : un navigateur s’ouvre pour que vous puissiez vous connecter et cliquer sur **Authorize**, ce qui donne au CLI l’accès à votre espace de travail. (Vous pouvez aussi choisir OAuth en local avec `--authentication-method oauth` — connectez-vous avec `tim@apple.dev` / `tim@apple.dev`.)
-Cliquez sur **Authorize** sur l’écran suivant — cela donne à la CLI l’accès à votre espace de travail.
-
@@ -117,27 +103,31 @@ Cliquez sur **View installed app** pour voir l’installation dans l’espace de
### Synchronisation ponctuelle pour la CI et les scripts
-Passez `--once` pour exécuter une seule opération de build + synchronisation puis quitter — même pipeline, pas de watcher :
+Utilisez `plan` et `apply` pour exécuter une fois le même pipeline, sans surveillance :
```bash filename="Terminal"
-yarn twenty dev --once
+yarn twenty plan # preview the metadata changes without applying them
+yarn twenty apply # show the plan, then apply it
```
-| Commande | Comportement | Quand l'utiliser : |
-| ---------------------------------- | --------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------ |
-| `yarn twenty dev` | Surveille et resynchronise à chaque modification. Reste en cours d’exécution jusqu’à ce que vous l’arrêtiez. | Développement local interactif. |
-| `yarn twenty dev --once` | Une seule opération de build + synchronisation, se termine avec le code `0` en cas de réussite et `1` en cas d’échec. | CI, hooks de pré-commit, agents IA et flux de travail scriptés. |
-| `yarn twenty dev --once --dry-run` | Construit et affiche les modifications de métadonnées **sans les appliquer**. | Inspection de ce qu’une synchronisation changerait avant de s’y engager. |
+| Commande | Comportement | Quand l'utiliser : |
+| ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------ |
+| `yarn twenty dev` | Surveille et resynchronise à chaque modification. Reste en cours d’exécution jusqu’à ce que vous l’arrêtiez. | Développement local interactif. |
+| `yarn twenty apply` | Une seule opération de build + synchronisation, se termine avec le code `0` en cas de réussite et `1` en cas d’échec. Demande une confirmation pour les modifications destructrices (passez `--force` pour l’ignorer). | CI, hooks de pré-commit, agents IA et flux de travail scriptés. |
+| `yarn twenty plan` | Construit et affiche les modifications de métadonnées **sans les appliquer**. | Inspection de ce qu’une synchronisation changerait avant de s’y engager. |
-Les deux modes nécessitent un serveur distant authentifié. Voir [synchronisation et récupération](/l/fr/developers/extend/apps/operations/sync-and-recovery#previewing-changes-dry-run) pour plus d’informations sur `--dry-run`.
+Tous les modes nécessitent un serveur distant authentifié. Voir [synchronisation et récupération](/l/fr/developers/extend/apps/operations/sync-and-recovery#previewing-changes-plan) pour plus d’informations sur `plan`.
+
+
+`yarn twenty dev --once` et `yarn twenty dev --once --dry-run` sont des alias obsolètes pour `yarn twenty apply` et `yarn twenty plan`.
+
### Options du mode de développement
| Option | Description |
| ------------------------------------- | -------------------------------------------------------------------------------------------------------------- |
-| `--once` | Construire et synchroniser une fois, puis quitter. |
-| `--dry-run` | Avec `--once`, prévisualisez les modifications de métadonnées sans les appliquer. N’écrit rien. |
-| `--debounceMs \` | Définir le délai de temporisation des modifications de fichiers en millisecondes (valeur par défaut : `2000`). |
+| `--force` | Applique les modifications destructrices (suppressions) sans confirmation. |
+| `--debounceMs \` | Définir le délai de temporisation des modifications de fichiers en millisecondes (valeur par défaut : `1000`). |
| `--verbose` / `--debug` | Afficher des journaux de build détaillés, les requêtes de synchronisation et les traces d’erreur. |
## Ce que vous pouvez créer
diff --git a/packages/twenty-docs/l/fr/developers/extend/apps/getting-started/scaffolding.mdx b/packages/twenty-docs/l/fr/developers/extend/apps/getting-started/scaffolding.mdx
index 3e89a52bfb..24b6422dbb 100644
--- a/packages/twenty-docs/l/fr/developers/extend/apps/getting-started/scaffolding.mdx
+++ b/packages/twenty-docs/l/fr/developers/extend/apps/getting-started/scaffolding.mdx
@@ -34,6 +34,10 @@ yarn twenty dev:add frontComponent
| Vue | `yarn twenty dev:add view` | `src/views/\.ts` |
| Élément de menu de navigation | `yarn twenty dev:add navigationMenuItem` | `src/navigation-menu-items/\.ts` |
| Mise en page | `yarn twenty dev:add pageLayout` | `src/page-layouts/\.ts` |
+| Onglet Mise en page | `yarn twenty dev:add pageLayoutTab` | `src/page-layout-tabs/\.ts` |
+| Élément du menu de commande | `yarn twenty dev:add commandMenuItem` | `src/command-menu-items/\.ts` |
+| Champ de vue | `yarn twenty dev:add viewField` | `src/view-fields/\.ts` |
+| Fournisseur de connexion | `yarn twenty dev:add connectionProvider` | `src/connection-providers/\.ts` |
## Ce que génère l'outil de génération
diff --git a/packages/twenty-docs/l/fr/developers/extend/apps/getting-started/troubleshooting.mdx b/packages/twenty-docs/l/fr/developers/extend/apps/getting-started/troubleshooting.mdx
index 42aed0b5c2..20ff381bdf 100644
--- a/packages/twenty-docs/l/fr/developers/extend/apps/getting-started/troubleshooting.mdx
+++ b/packages/twenty-docs/l/fr/developers/extend/apps/getting-started/troubleshooting.mdx
@@ -5,10 +5,10 @@ icon: wrench
---
* **Erreurs Docker** — Assurez-vous que Docker Desktop (ou le démon) est en cours d’exécution avant `yarn twenty docker:start`. Le message d’erreur indiquera la bonne commande de démarrage pour votre système d’exploitation.
-* **Mauvaise version de Node** — la version 24+ est requise. Vérifiez avec `node -v`.
+* **Mauvaise version de Node** — Version 24.5+ requise (`engines.node: ^24.5.0`). Vérifiez avec `node -v`.
* **Yarn 4 manquant** — Exécutez `corepack enable`.
* **Dépendances cassées** — `rm -rf node_modules && yarn install`.
* **Erreurs de `twenty-sdk` après la mise à niveau vers la v2.8.0** — il est passé de `dependencies` à `devDependencies` dans la v2.8.0. Voir [Structure du projet → Dépendances](/l/fr/developers/extend/apps/getting-started/project-structure#dependencies).
-* **`twenty build` avertit au sujet de `twenty-client-sdk` dans `dependencies`** — il est fourni au moment de l’exécution par Twenty, donc il devrait être déplacé vers `devDependencies` avec `twenty-sdk`. Voir [Structure du projet → Dépendances](/l/fr/developers/extend/apps/getting-started/project-structure#dependencies).
+* **`twenty dev:build` avertit au sujet de `twenty-client-sdk` dans `dependencies`** — il est fourni au moment de l'exécution par Twenty, donc il devrait être déplacé vers `devDependencies` avec `twenty-sdk`. Voir [Structure du projet → Dépendances](/l/fr/developers/extend/apps/getting-started/project-structure#dependencies).
Bloqué ? Demandez de l’aide sur le [Discord de Twenty](https://discord.com/channels/1130383047699738754/1130386664812982322).
diff --git a/packages/twenty-docs/l/fr/developers/extend/apps/layout/command-menu-items.mdx b/packages/twenty-docs/l/fr/developers/extend/apps/layout/command-menu-items.mdx
index 816ad4060e..6c176d5f1f 100644
--- a/packages/twenty-docs/l/fr/developers/extend/apps/layout/command-menu-items.mdx
+++ b/packages/twenty-docs/l/fr/developers/extend/apps/layout/command-menu-items.mdx
@@ -13,7 +13,6 @@ export default defineCommandMenuItem({
universalIdentifier: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
label: 'Open Dashboard',
shortLabel: 'Dashboard',
- icon: 'IconLayoutDashboard',
isPinned: true,
availabilityType: 'GLOBAL',
frontComponentUniversalIdentifier: '74c526eb-cb68-4cf7-b05c-0dd8c288d948',
@@ -22,51 +21,23 @@ export default defineCommandMenuItem({
## Champs de configuration
-| Champ | Obligatoire | Description |
-| --------------------------------------- | ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| `universalIdentifier` | Oui | ID unique et stable pour la commande |
-| `label` | Oui | Libellé complet affiché dans le menu de commande (Cmd+K) |
-| `frontComponentUniversalIdentifier` | Oui | L'`universalIdentifier` du composant frontal que cette commande ouvre |
-| `shortLabel` | Non | Libellé plus court affiché sur le bouton d'action rapide épinglé |
-| `icon` | Non | Nom de l'icône affiché à côté du libellé (p. ex. `'IconBolt'`, `'IconSend'`) |
-| `isPinned` | Non | Lorsque `true`, affiche la commande comme un bouton d'action rapide dans le coin supérieur droit de la page |
-| `availabilityType` | Non | Contrôle l'emplacement d'apparition de la commande : `'GLOBAL'` (toujours disponible), `'RECORD_SELECTION'` (uniquement lorsque des enregistrements sont sélectionnés) ou `'FALLBACK'` (affichée lorsqu'aucune autre commande ne correspond) |
-| `availabilityObjectUniversalIdentifier` | Non | Restreint la commande aux pages d’un type d’objet spécifique (p. ex., uniquement sur les enregistrements « Company ») |
-| `conditionalAvailabilityExpression` | Non | Une expression booléenne qui contrôle dynamiquement la visibilité (voir ci-dessous) |
+| Champ | Obligatoire | Description |
+| --------------------------------------- | ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `universalIdentifier` | Oui | ID unique et stable pour la commande |
+| `label` | Oui | Libellé complet affiché dans le menu de commande (Cmd+K) |
+| `frontComponentUniversalIdentifier` | Oui | L'`universalIdentifier` du composant frontal que cette commande ouvre |
+| `shortLabel` | Non | Libellé plus court affiché sur le bouton d'action rapide épinglé |
+| `icon` | Non | **Obsolète** — ignoré au profit de l’icône de l’application ; la build émet un avertissement si elle est définie |
+| `isPinned` | Non | Lorsque `true`, affiche la commande comme un bouton d'action rapide dans le coin supérieur droit de la page |
+| `availabilityType` | Non | Contrôle l’emplacement d’apparition de la commande : `'GLOBAL'` (toujours disponible), `'GLOBAL_OBJECT_CONTEXT'` (uniquement sur les pages avec un contexte d’objet — pages d’index et d’enregistrement), `'RECORD_SELECTION'` (uniquement lorsque des enregistrements sont sélectionnés) ou `'FALLBACK'` (affichée lorsqu’aucune autre commande ne correspond) |
+| `availabilityObjectUniversalIdentifier` | Non | Restreint la commande aux pages d’un type d’objet spécifique (p. ex., uniquement sur les enregistrements « Company ») |
+| `conditionalAvailabilityExpression` | Non | Une expression booléenne qui contrôle dynamiquement la visibilité (voir ci-dessous) |
## Commandes sans interface
Un élément de menu de commande associé à un [headless front component](/l/fr/developers/extend/apps/layout/front-components#headless-vs-non-headless) est la manière idiomatique de proposer une action en un clic — exécuter du code, naviguer, ou confirmer puis exécuter. La page Front Components couvre les [SDK Command components](/l/fr/developers/extend/apps/layout/front-components#sdk-command-components) (`Command`, `CommandLink`, `CommandModal`, `CommandOpenSidePanelPage`) qui gèrent le modèle action-et-démontage.
-Un flux typique :
-
-```tsx src/front-components/run-action.tsx
-import { defineFrontComponent } from 'twenty-sdk/define';
-import { Command } from 'twenty-sdk/command';
-import { CoreApiClient } from 'twenty-sdk/clients';
-
-const RunAction = () => {
- const execute = async () => {
- const client = new CoreApiClient();
- await client.mutation({
- createTask: {
- __args: { data: { title: 'Created by my app' } },
- id: true,
- },
- });
- };
-
- return ;
-};
-
-export default defineFrontComponent({
- universalIdentifier: 'e5f6a7b8-c9d0-1234-efab-345678901234',
- name: 'run-action',
- description: 'Creates a task from the command menu',
- component: RunAction,
- isHeadless: true,
-});
-```
+Un flux typique : un composant headless affiche `` (voir [l’exemple complet](/l/fr/developers/extend/apps/layout/front-components#sdk-command-components)), et l’élément de menu de commande y pointe :
```ts src/command-menu-items/run-action.command-menu-item.ts
import { defineCommandMenuItem } from 'twenty-sdk/define';
@@ -74,7 +45,6 @@ import { defineCommandMenuItem } from 'twenty-sdk/define';
export default defineCommandMenuItem({
universalIdentifier: 'f6a7b8c9-d0e1-2345-fabc-456789012345',
label: 'Run my action',
- icon: 'IconPlayerPlay',
frontComponentUniversalIdentifier: 'e5f6a7b8-c9d0-1234-efab-345678901234',
});
```
diff --git a/packages/twenty-docs/l/fr/developers/extend/apps/layout/front-components.mdx b/packages/twenty-docs/l/fr/developers/extend/apps/layout/front-components.mdx
index a842691001..19d6507f07 100644
--- a/packages/twenty-docs/l/fr/developers/extend/apps/layout/front-components.mdx
+++ b/packages/twenty-docs/l/fr/developers/extend/apps/layout/front-components.mdx
@@ -49,14 +49,13 @@ export default defineCommandMenuItem({
universalIdentifier: 'd4e5f6a7-b8c9-0123-defa-456789012345',
shortLabel: 'Hello',
label: 'Hello World',
- icon: 'IconBolt',
isPinned: true,
availabilityType: 'GLOBAL',
frontComponentUniversalIdentifier: '74c526eb-cb68-4cf7-b05c-0dd8c288d948',
});
```
-Après la synchronisation avec `yarn twenty dev` (ou en exécutant une seule fois `yarn twenty dev --once`), l'action rapide apparaît dans le coin supérieur droit de la page :
+Après la synchronisation avec `yarn twenty dev` (ou en exécutant une seule fois `yarn twenty apply`), l'action rapide apparaît dans le coin supérieur droit de la page :

@@ -88,11 +87,11 @@ Les composants frontaux existent en deux modes de rendu contrôlés par l’opti
```tsx src/front-components/sync-tracker.tsx
import { defineFrontComponent } from 'twenty-sdk/define';
-import { useRecordId, enqueueSnackbar } from 'twenty-sdk/front-component';
+import { useSelectedRecordIds, enqueueSnackbar } from 'twenty-sdk/front-component';
import { useEffect } from 'react';
const SyncTracker = () => {
- const recordId = useRecordId();
+ const [recordId] = useSelectedRecordIds();
useEffect(() => {
enqueueSnackbar({ message: `Tracking record ${recordId}`, variant: 'info' });
@@ -116,7 +115,7 @@ Comme le composant retourne `null`, Twenty n'affiche pas de conteneur pour celui
Le package `twenty-sdk` fournit quatre composants utilitaires Command conçus pour les composants frontaux headless. Chaque composant exécute une action au montage, gère les erreurs en affichant une notification snackbar et démonte automatiquement le composant frontal une fois terminé.
-Importez-les depuis `twenty-sdk/command` :
+Importez-les depuis `twenty-sdk/front-component` :
* **`Command`** — Exécute un callback asynchrone via la prop `execute`.
* **`CommandLink`** — Navigue vers un chemin d'application. Props : `to`, `params`, `queryParams`, `options`.
@@ -127,8 +126,8 @@ Voici un exemple complet d'un composant frontal headless utilisant `Command` pou
```tsx src/front-components/run-action.tsx
import { defineFrontComponent } from 'twenty-sdk/define';
-import { Command } from 'twenty-sdk/command';
-import { CoreApiClient } from 'twenty-sdk/clients';
+import { Command } from 'twenty-sdk/front-component';
+import { CoreApiClient } from 'twenty-client-sdk/core';
const RunAction = () => {
const execute = async () => {
@@ -160,7 +159,6 @@ import { defineCommandMenuItem } from 'twenty-sdk/define';
export default defineCommandMenuItem({
universalIdentifier: 'f6a7b8c9-d0e1-2345-fabc-456789012345',
label: 'Run my action',
- icon: 'IconPlayerPlay',
frontComponentUniversalIdentifier: 'e5f6a7b8-c9d0-1234-efab-345678901234',
});
```
@@ -169,7 +167,7 @@ Et un exemple utilisant `CommandModal` pour demander une confirmation avant l'ex
```tsx src/front-components/delete-draft.tsx
import { defineFrontComponent } from 'twenty-sdk/define';
-import { CommandModal } from 'twenty-sdk/command';
+import { CommandModal } from 'twenty-sdk/front-component';
const DeleteDraft = () => {
const execute = async () => {
@@ -202,7 +200,7 @@ Les composants front s’exécutent côté navigateur dans un Web Worker isolé
Une fonction logique déclarée avec `httpRouteTriggerSettings` est accessible via HTTP à son chemin de route. Twenty injecte dans le worker l’URL de base à partir de laquelle vos fonctions sont servies sous la forme de `TWENTY_FUNCTIONS_URL`, ainsi que le `TWENTY_APP_ACCESS_TOKEN` qui authentifie l’appel. Il n’existe pas encore de client SDK dédié pour invoquer vos propres fonctions, donc appelez-les avec un simple `fetch` :
-> **Sur Twenty Cloud, les fonctions logiques déclenchées par HTTP sont servies sur un domaine dédié par espace de travail** à l’adresse `https://\
.twenty.com\` — c’est exactement ce à quoi `TWENTY_FUNCTIONS_URL` correspond. Pour les appelants externes, copiez l’URL exacte à partir des paramètres **HTTP trigger** de la fonction ou de l’onglet **Settings** de l’application.
+> **Sur Twenty Cloud, les fonctions logiques déclenchées par HTTP sont servies sur un domaine dédié par espace de travail** à l’adresse `https://\.withtwenty.com\` — c’est exactement ce à quoi `TWENTY_FUNCTIONS_URL` correspond. Pour les appelants externes, copiez l’URL exacte à partir des paramètres **HTTP trigger** de la fonction ou de l’onglet **Settings** de l’application.
L’ancienne route de fonction `/s/` est **obsolète** et sera **désactivée le 2026-07-24**. Utilisez plutôt `TWENTY_FUNCTIONS_URL` (ci-dessus), et migrez toutes les URL `/s/` en dur avant cette date. La route `/s/` reste disponible pour l’auto-hébergement.
@@ -212,7 +210,7 @@ Un composant front sans interface (headless) peut effectuer l’appel au montage
```tsx src/front-components/sync-prs.tsx
import { defineFrontComponent } from 'twenty-sdk/define';
-import { Command } from 'twenty-sdk/command';
+import { Command } from 'twenty-sdk/front-component';
const SyncPrs = () => {
const execute = async () => {
@@ -316,13 +314,13 @@ Dans votre composant, utilisez les hooks du SDK pour accéder à l'utilisateur a
import { defineFrontComponent } from 'twenty-sdk/define';
import {
useUserId,
- useRecordId,
+ useSelectedRecordIds,
useFrontComponentId,
} from 'twenty-sdk/front-component';
const RecordInfo = () => {
const userId = useUserId();
- const recordId = useRecordId();
+ const [recordId] = useSelectedRecordIds();
const componentId = useFrontComponentId();
return (
@@ -405,12 +403,11 @@ Voici un exemple qui utilise l'API hôte pour afficher une snackbar et fermer le
```tsx src/front-components/archive-record.tsx
import { defineFrontComponent } from 'twenty-sdk/define';
-import { useRecordId } from 'twenty-sdk/front-component';
-import { enqueueSnackbar, closeSidePanel } from 'twenty-sdk/front-component';
-import { CoreApiClient } from 'twenty-sdk/clients';
+import { enqueueSnackbar, closeSidePanel, useSelectedRecordIds } from 'twenty-sdk/front-component';
+import { CoreApiClient } from 'twenty-client-sdk/core';
const ArchiveRecord = () => {
- const recordId = useRecordId();
+ const [recordId] = useSelectedRecordIds();
const handleArchive = async () => {
const client = new CoreApiClient();
@@ -451,10 +448,10 @@ export default defineFrontComponent({
Utilisez `useSelectedRecordIds()` pour gérer plusieurs enregistrements sélectionnés. C'est utile pour les opérations groupées :
```tsx src/front-components/bulk-export.tsx
-import { defineFrontComponent, numberOfSelectedRecords } from 'twenty-sdk/define';
+import { defineFrontComponent } from 'twenty-sdk/define';
import { useSelectedRecordIds } from 'twenty-sdk/front-component';
import { enqueueSnackbar, closeSidePanel } from 'twenty-sdk/front-component';
-import { CoreApiClient } from 'twenty-sdk/clients';
+import { CoreApiClient } from 'twenty-client-sdk/core';
const BulkExport = () => {
const selectedRecordIds = useSelectedRecordIds();
@@ -492,12 +489,19 @@ export default defineFrontComponent({
name: 'bulk-export',
description: 'Export selected records',
component: BulkExport,
- command: {
- universalIdentifier: 'd0e1f2a3-b4c5-6789-defa-012345678902',
- label: 'Bulk Export',
- availabilityType: 'RECORD_SELECTION',
- conditionalAvailabilityExpression: numberOfSelectedRecords > 0,
- },
+});
+```
+
+Affichez-la avec un [élément de menu de commande](/l/fr/developers/extend/apps/layout/command-menu-items) limité aux sélections d'enregistrements :
+
+```ts src/command-menu-items/bulk-export.command-menu-item.ts
+import { defineCommandMenuItem } from 'twenty-sdk/define';
+
+export default defineCommandMenuItem({
+ universalIdentifier: 'd0e1f2a3-b4c5-6789-defa-012345678902',
+ label: 'Bulk Export',
+ availabilityType: 'RECORD_SELECTION',
+ frontComponentUniversalIdentifier: 'd0e1f2a3-b4c5-6789-defa-012345678901',
});
```
diff --git a/packages/twenty-docs/l/fr/developers/extend/apps/layout/navigation-menu-items.mdx b/packages/twenty-docs/l/fr/developers/extend/apps/layout/navigation-menu-items.mdx
index 363b080a1a..a7724e9b71 100644
--- a/packages/twenty-docs/l/fr/developers/extend/apps/layout/navigation-menu-items.mdx
+++ b/packages/twenty-docs/l/fr/developers/extend/apps/layout/navigation-menu-items.mdx
@@ -35,6 +35,8 @@ export default defineNavigationMenuItem({
* `position` contrôle l’ordre dans la barre latérale.
+* L’énumération contient également `NavigationMenuItemType.RECORD`, utilisé en interne pour les favoris d’enregistrements créés par l’utilisateur — il n’est pas utilisable depuis un manifeste d’application (il n’existe aucun champ pour référencer un enregistrement).
+
* `icon` et `color` sont facultatifs et personnalisent l’apparence de l’entrée.
* `folderUniversalIdentifier` est également disponible sur n’importe quel élément pour l’imbriquer dans un parent de type `FOLDER`.
diff --git a/packages/twenty-docs/l/fr/developers/extend/apps/layout/views.mdx b/packages/twenty-docs/l/fr/developers/extend/apps/layout/views.mdx
index 5767d2abcb..37c99e1fed 100644
--- a/packages/twenty-docs/l/fr/developers/extend/apps/layout/views.mdx
+++ b/packages/twenty-docs/l/fr/developers/extend/apps/layout/views.mdx
@@ -33,17 +33,32 @@ export default defineView({
## Points clés
* `objectUniversalIdentifier` spécifie à quel objet cette vue s'applique. Il peut s’agir d’un objet personnalisé que vous avez défini ou d’un objet Twenty standard.
-* `key` détermine le type de vue — `ViewKey.INDEX` est la vue de liste principale pour l’objet.
+* "`key: ViewKey.INDEX`" marque la vue comme la vue de liste principale de l’objet (celle qu’un élément de navigation "`OBJECT`" ouvre).
* `fields` contrôle les colonnes affichées et leur ordre. Chaque champ référence un `fieldMetadataUniversalIdentifier`.
-* Vous pouvez également définir `filters`, `filterGroups`, `groups` et `fieldGroups` pour des configurations plus avancées.
+* Vous pouvez également déclarer `filters`, `filterGroups`, `sorts`, `groups` et `fieldGroups` pour des configurations plus avancées.
* `position` contrôle l’ordre lorsqu’il existe plusieurs vues pour le même objet.
+## Propriétés optionnelles
+
+| Propriété | Valeurs | Description |
+| ----------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `type` | `ViewType.TABLE` (par défaut), `ViewType.KANBAN`, `ViewType.CALENDAR` | Comment les enregistrements sont disposés. (`FIELDS_WIDGET` / `TABLE_WIDGET` existent également mais sont utilisés en interne par les widgets de mise en page de page.) |
+| `visibility` | `ViewVisibility.WORKSPACE` (par défaut), `ViewVisibility.UNLISTED` | Indique si la vue est listée pour l’ensemble de l’espace de travail ou masquée dans les sélecteurs. |
+| `openRecordIn` | `ViewOpenRecordIn.SIDE_PANEL` (par défaut), `ViewOpenRecordIn.RECORD_PAGE` | Endroit où un clic sur un enregistrement l’ouvre. |
+| `tris` | `{ fieldMetadataUniversalIdentifier, direction: ViewSortDirection.ASC \| DESC }[]` | Ordre de tri par défaut. |
+| `isCompact` | `boolean` | Affichage compact des lignes. |
+| `mainGroupByFieldMetadataUniversalIdentifier` + `shouldHideEmptyGroups` | — | Regrouper les enregistrements (par exemple, les colonnes kanban) par un champ. |
+| `kanbanAggregateOperation`, `kanbanAggregateOperationFieldMetadataUniversalIdentifier`, `kanbanColumnWidth` | `AggregateOperations.*` | Agrégats et dimensionnement des colonnes Kanban. |
+| `calendarLayout`, `calendarFieldMetadataUniversalIdentifier` | `ViewCalendarLayout.DAY` / `WEEK` / `MONTH` | Vues de calendrier : disposition et champ de date qui positionne les enregistrements. |
+
+Tous les enums ci-dessus sont exportés depuis `twenty-sdk/define`.
+
## Filtres
Une vue peut être livrée avec des filtres préappliqués. Chaque filtre possède trois coordonnées : le **champ** faisant l’objet du filtrage, l’**opérateur** (comment comparer) et la **valeur** (par rapport à quoi comparer). Les trois doivent être alignées : l’utilisation d’un opérateur qui ne s’applique pas à un type de champ sera rejetée au moment de la synchronisation.
```ts
-import { ViewFilterOperand } from 'twenty-shared/types';
+import { ViewFilterOperand } from 'twenty-sdk/define';
filters: [
{
diff --git a/packages/twenty-docs/l/fr/developers/extend/apps/logic/logic-functions.mdx b/packages/twenty-docs/l/fr/developers/extend/apps/logic/logic-functions.mdx
index 24ca9ce01b..e599e47d93 100644
--- a/packages/twenty-docs/l/fr/developers/extend/apps/logic/logic-functions.mdx
+++ b/packages/twenty-docs/l/fr/developers/extend/apps/logic/logic-functions.mdx
@@ -51,8 +51,12 @@ export default defineLogicFunction({
```
Types de déclencheurs disponibles :
-* **httpRoute** : Expose votre fonction sur un chemin et une méthode HTTP **sous l'endpoint `/s/`** :
-> p. ex. `path: '/post-card/create'` est appelable à `https://your-twenty-server.com/s/post-card/create`
+* **httpRoute** : Expose votre fonction sur un chemin HTTP et une méthode dans l'URL de **fonctions de base** de votre espace de travail — la valeur de 20 injects en tant que `TWENTY_FUNCTIONS_URL` (sur Twenty Cloud, un domaine dédié par espace de travail):
+> p. ex. `path: '/post-card/create'` est appelable à `https://your-workspace.withtwenty.com/post-card/create`
+
+
+L'ancienne route de préfixe `/s/` (`https://your-twenty-server.com/s/post-card/create`) est **obsolète sur Twenty Cloud** et sera désactivée le **2026-07-24**. Il reste disponible pour les instances auto-hébergées et locales qui ne configurent pas un domaine de fonctions isolées — utilisez `TWENTY_FUNCTIONS_URL` quand il est défini, et revenez à `\/s/\` autrement.
+
Pour appeler une fonction logique déclenchée par une route depuis un composant frontal (sans interface), consultez [Appeler une fonction logique](/l/fr/developers/extend/apps/layout/front-components#calling-a-logic-function).
diff --git a/packages/twenty-docs/l/fr/developers/extend/apps/logic/overview.mdx b/packages/twenty-docs/l/fr/developers/extend/apps/logic/overview.mdx
index 04b10d6f93..d5a12571b8 100644
--- a/packages/twenty-docs/l/fr/developers/extend/apps/logic/overview.mdx
+++ b/packages/twenty-docs/l/fr/developers/extend/apps/logic/overview.mdx
@@ -40,13 +40,13 @@ La **couche logique** d’une application Twenty est le code qui *s’exécute*
Une fonction logique choisit un ou plusieurs déclencheurs — chaque entrée ci-dessous est un champ distinct sur `defineLogicFunction()`:
-| Déclencheur | Moment d’exécution | Paramètre |
-| -------------------------------- | ---------------------------------------------------------------------------- | ------------------------------- |
-| **Route HTTP** | Une requête atteint votre point de terminaison `/s/\` | `httpRouteTriggerSettings` |
-| **Cron** | Une expression CRON correspond | `cronTriggerSettings` |
-| **Événement de base de données** | Un enregistrement de l’espace de travail est créé, mis à jour ou supprimé | `databaseEventTriggerSettings` |
-| **Outil IA** | Une fonctionnalité IA de Twenty décide d’appeler votre fonction | `toolTriggerSettings` |
-| **Action de flux de travail** | Une étape de flux de travail invoque votre fonction | `workflowActionTriggerSettings` |
+| Déclencheur | Moment d’exécution | Paramètre |
+| -------------------------------- | ------------------------------------------------------------------------- | ------------------------------- |
+| **Route HTTP** | Une requête atteint l'URL publique de votre fonction | `httpRouteTriggerSettings` |
+| **Cron** | Une expression CRON correspond | `cronTriggerSettings` |
+| **Événement de base de données** | Un enregistrement de l’espace de travail est créé, mis à jour ou supprimé | `databaseEventTriggerSettings` |
+| **Outil IA** | Une fonctionnalité IA de Twenty décide d’appeler votre fonction | `toolTriggerSettings` |
+| **Action de flux de travail** | Une étape de flux de travail invoque votre fonction | `workflowActionTriggerSettings` |
Les fonctions s’exécutent dans un environnement isolé dans des processus Node.js sandboxés et accèdent à l’espace de travail via un client API typé, limité au rôle déclaré sur [`defineApplication()`](/l/fr/developers/extend/apps/config/application).
diff --git a/packages/twenty-docs/l/fr/developers/extend/apps/operations/cli.mdx b/packages/twenty-docs/l/fr/developers/extend/apps/operations/cli.mdx
index f2ec5eca19..3c56113a05 100644
--- a/packages/twenty-docs/l/fr/developers/extend/apps/operations/cli.mdx
+++ b/packages/twenty-docs/l/fr/developers/extend/apps/operations/cli.mdx
@@ -4,7 +4,25 @@ description: Les commandes `yarn twenty` pour exécuter des fonctions, diffuser
icon: terminal
---
-Au-delà de `dev`, `dev:build`, `dev:add` et `dev:typecheck`, la CLI `yarn twenty` fournit des commandes pour exécuter des fonctions, consulter les journaux et gérer les installations d'applications.
+Le CLI `yarn twenty` est votre interface pour tout ce qui concerne les applications. Liste complète des commandes :
+
+| Commande | Ce que cela fait | Documenté dans |
+| ----------------------------------------------- | ----------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
+| `dev` | Surveille les fichiers sources et synchronise en direct les modifications | [Prise en main rapide](/l/fr/developers/extend/apps/getting-started/quick-start) |
+| `plan` | Prévisualiser les modifications de métadonnées sans les appliquer | [Synchronisation et récupération](/l/fr/developers/extend/apps/operations/sync-and-recovery#previewing-changes-plan) |
+| `appliquer` | Appliquer les modifications de métadonnées après avoir affiché le plan | [Synchronisation et récupération](/l/fr/developers/extend/apps/operations/sync-and-recovery) |
+| `dev:build` | Compiler l’application et générer le client d’API (`--tarball` pour empaqueter un `.tgz`) | [Publication](/l/fr/developers/extend/apps/operations/publishing) |
+| `dev:typecheck` | Exécuter la vérification des types TypeScript | [Tests](/l/fr/developers/extend/apps/operations/testing) |
+| `dev:add` | Générer la structure d’une nouvelle entité | [Génération de structure](/l/fr/developers/extend/apps/getting-started/scaffolding) |
+| `dev:generate-client` | Régénérer le client d’API typé | cette page |
+| `dev:function:exec` / `dev:function:logs` | Exécuter des fonctions et diffuser leurs journaux | cette page |
+| `dev:translations-extract` | Extraire les chaînes traduisibles dans les catalogues `locales/` | [Traductions](/l/fr/developers/extend/apps/translations/overview) |
+| `dev:catalog-sync` | Déclencher la synchronisation du catalogue de la place de marché | [Publication](/l/fr/developers/extend/apps/operations/publishing#how-marketplace-discovery-works) |
+| `app:publish` / `app:install` / `app:uninstall` | Cycle de vie de la mise en production | [Publication](/l/fr/developers/extend/apps/operations/publishing) et cette page |
+| `docker:*` | Gérer le conteneur du serveur Twenty local | [Serveur local](/l/fr/developers/extend/apps/getting-started/local-server) |
+| `remote:*` | Gérer les connexions serveur | cette page |
+
+Chaque commande accepte `-r, --remote \` pour cibler un serveur distant spécifique au lieu de celui par défaut.
## Exécuter des fonctions (`yarn twenty dev:function:exec`)
@@ -20,8 +38,9 @@ yarn twenty dev:function:exec -u e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf
# Pass a JSON payload
yarn twenty dev:function:exec -n create-new-post-card -p '{"name": "Hello"}'
-# Execute the post-install function
+# Execute the install hooks
yarn twenty dev:function:exec --postInstall
+yarn twenty dev:function:exec --preInstall
```
## Afficher les journaux des fonctions (`yarn twenty dev:function:logs`)
@@ -100,6 +119,12 @@ yarn twenty remote:list
# Set the active remote
yarn twenty remote:use
+
+# Check that the active remote's authentication is still valid
+yarn twenty remote:status
+
+# Remove a remote
+yarn twenty remote:remove
```
Vos identifiants sont stockés dans `~/.twenty/config.json`.
diff --git a/packages/twenty-docs/l/fr/developers/extend/apps/operations/publishing.mdx b/packages/twenty-docs/l/fr/developers/extend/apps/operations/publishing.mdx
index b16f6d8548..8b0b603777 100644
--- a/packages/twenty-docs/l/fr/developers/extend/apps/operations/publishing.mdx
+++ b/packages/twenty-docs/l/fr/developers/extend/apps/operations/publishing.mdx
@@ -229,7 +229,7 @@ yarn twenty dev:catalog-sync
# yarn twenty dev:catalog-sync --remote production
```
-Les métadonnées affichées dans la place de marché proviennent de votre configuration `defineApplication()` — des champs comme `displayName`, `description`, `author`, `category`, `logoUrl`, `screenshots`, `aboutDescription`, `websiteUrl` et `termsUrl`.
+Les métadonnées affichées dans la marketplace proviennent de votre configuration `defineApplication()` — voir [Métadonnées de la marketplace](#marketplace-metadata) ci-dessus.
Si votre application ne définit pas de `aboutDescription` dans `defineApplication()`, la place de marché utilisera automatiquement le `README.md` de votre package depuis npm comme contenu de la page À propos. Cela signifie que vous pouvez maintenir un seul README à la fois pour npm et pour la place de marché Twenty. Si vous souhaitez une description différente dans la place de marché, définissez explicitement `aboutDescription`.
diff --git a/packages/twenty-docs/l/fr/developers/extend/apps/operations/sync-and-recovery.mdx b/packages/twenty-docs/l/fr/developers/extend/apps/operations/sync-and-recovery.mdx
index c61e429d48..d0987ba86a 100644
--- a/packages/twenty-docs/l/fr/developers/extend/apps/operations/sync-and-recovery.mdx
+++ b/packages/twenty-docs/l/fr/developers/extend/apps/operations/sync-and-recovery.mdx
@@ -15,33 +15,44 @@ Pour l'itération locale au quotidien, vous voudrez presque toujours `yarn twent
| Vous souhaitez… | Commande | Notes |
| ------------------------------------------------------- | ----------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| Itérer localement avec la synchronisation en direct | `yarn twenty dev` | Surveille vos fichiers et synchronise à chaque modification. |
-| Synchroniser une fois puis quitter (CI, scripts, hooks) | `yarn twenty dev --once` | Effectue une compilation + synchronisation, puis quitte. |
-| Prévisualiser les changements **sans les appliquer** | `yarn twenty dev --once --dry-run` | Calcule et affiche le diff ; n'écrit rien. |
+| Synchroniser une fois puis quitter (CI, scripts, hooks) | `yarn twenty apply` | Effectue une compilation + synchronisation, puis quitte. Ajoutez `--force` pour ignorer la confirmation des changements destructifs. |
+| Prévisualiser les changements **sans les appliquer** | `yarn twenty plan` | Calcule et affiche le diff ; n'écrit rien. |
| Retirer l'application de l'espace de travail | `yarn twenty app:uninstall` | Ajoutez `--yes` pour ignorer la confirmation. |
| Envoyer une archive tarball vers un serveur | `yarn twenty app:publish --private` | Nécessite une version de `package.json` **strictement supérieure** — voir [Publication](/l/fr/developers/extend/apps/operations/publishing). |
| Publier sur la place de marché (npm) | `yarn twenty app:publish` | — |
| Installer / mettre à niveau une version déployée | `yarn twenty app:install` | Installe la version actuellement déployée. |
| Effacer le serveur local et repartir de zéro | `yarn twenty docker:reset` | Supprime **toutes** les données locales — en dernier recours. |
+
+`yarn twenty dev --once` et `yarn twenty dev --once --dry-run` fonctionnent toujours comme alias obsolètes de `yarn twenty apply` et `yarn twenty plan`.
+
+
### La synchronisation locale n'a pas besoin d'un incrément de version
La règle de `version` strictement croissante (`VERSION_ALREADY_EXISTS` lors du déploiement, `APP_ALREADY_INSTALLED` / `CANNOT_DOWNGRADE_APPLICATION` lors de l'installation) s'applique à **`app:publish` / `app:install`** — le chemin de mise en production. `yarn twenty dev` synchronise votre manifeste sur place et ne nécessite jamais de changement de version, vous n'avez donc pas besoin de toucher à `package.json` pour itérer. Si vous vous surprenez à incrémenter la version pour tester un changement local, c'est que vous utilisez le chemin de mise en production alors que vous voulez la boucle de développement.
## Lire la sortie de synchronisation
-Chaque synchronisation affiche les changements de métadonnées qu'elle a appliqués (ou appliquerait, avec `--dry-run`) :
+Chaque synchronisation affiche les changements de métadonnées qu'elle a appliqués (ou qu'elle appliquerait, avec `plan`), à la manière de Terraform — un bloc par entité avec ses attributs, puis une ligne récapitulative :
```text filename="Terminal"
-Metadata changes: 2 created, 1 updated, 1 deleted
- created objectMetadata rocket
- created fieldMetadata timelineActivities
- updated fieldMetadata launchedAt
- deleted pageLayout legacyTab
-✓ Synced
+ # objectMetadata "rocket" will be created
+ + icon = "IconRocket"
+ + labelSingular = "Rocket"
+ + ...
+
+ # fieldMetadata "launchedAt" will be updated
+ ~ isNullable = false -> true
+
+Plan: 2 to add, 1 to change, 1 to destroy.
+
+✓ Synced My App (4 files)
```
C'est votre premier diagnostic : il vous indique exactement quels objets, champs et mises en page ont changé, afin que vous puissiez confirmer qu'une synchronisation a fait ce que vous attendiez avant de vérifier l'interface utilisateur.
+Les changements destructifs (`to destroy`) sont listés avec ce qu'ils suppriment (par ex. `objectMetadata "auditNote" — drops the table and all its rows`) et nécessitent une confirmation interactive, ou `--force` dans les scripts.
+
Lorsqu'une synchronisation échoue sur une seule entité, l'erreur nomme l'entité en cause et son `universalIdentifier`, par exemple :
```text
@@ -50,39 +61,42 @@ Migration action 'create' for 'fieldMetadata' (universalIdentifier: 2020...4337)
Utilisez cet identifiant pour trouver l'entité dans votre manifeste (et, si nécessaire, dans l'espace de travail) plutôt que de deviner laquelle est en conflit.
-## Prévisualiser les changements (dry run)
+## Prévisualiser les changements (plan)
-`yarn twenty dev --once --dry-run` construit votre manifeste, demande au serveur le plan de migration et l'affiche — **sans rien appliquer**. C'est le moyen sûr de répondre « que changerait cette synchronisation ? » avant de s'y engager.
+`yarn twenty plan` construit votre manifeste, demande au serveur le plan de migration et l'affiche — **sans rien appliquer**. C'est le moyen sûr de répondre « que changerait cette synchronisation ? » avant de s'y engager.
```bash filename="Terminal"
-yarn twenty dev --once --dry-run
+yarn twenty plan
```
```text filename="Terminal"
Building manifest...
-Computing metadata diff (dry run, nothing will be applied)...
-Metadata changes: 1 created, 1 updated
- created fieldMetadata timelineActivities
- updated objectMetadata rocket
-✓ Dry run complete for My App — no changes were applied
+Computing metadata plan (read-only, nothing will be applied)...
+
+ # fieldMetadata "timelineActivities" will be created
+ + ...
+
+Plan: 1 to add, 1 to change, 0 to destroy.
+
+✓ Plan complete for My App — no changes were applied
```
-Un dry run :
+Un plan :
* **N'écrit rien** — aucune migration de métadonnées, aucune mise à jour de l'enregistrement d'application, aucun changement de rôle/onglet par défaut, et aucune génération de client d'API.
* Renvoie le **même diff** qu'une synchronisation réelle appliquerait, afin que vous puissiez examiner à l'avance les entités créées/mises à jour/supprimées.
* Est utile avant un changement risqué, lors de la révision d'un changement généré par une IA, ou dans un script qui doit échouer si un changement inattendu est sur le point d'être appliqué.
-Un dry run ne prévisualise que les changements de **métadonnées**, et il nécessite que l'application ait été synchronisée au moins une fois (pour que l'espace de travail la connaisse). Si vous l'exécutez sur une application qui n'a jamais été synchronisée, le serveur indique que l'application n'est pas installée — exécutez d'abord une fois `yarn twenty dev`.
+Un plan ne prévisualise que les changements de **métadonnées**, et il nécessite que l'application ait été synchronisée au moins une fois (pour que l'espace de travail la connaisse). Si vous l'exécutez sur une application qui n'a jamais été synchronisée, le serveur indique que l'application n'est pas installée — exécutez d'abord une fois `yarn twenty dev`.
## Échelle de récupération
Lorsque les métadonnées locales semblent incorrectes, augmentez le niveau de manière progressive dans cet ordre et arrêtez-vous dès que vous êtes débloqué. Chaque étape est plus perturbatrice que la précédente.
-1. **Resynchroniser.** Exécutez à nouveau `yarn twenty dev --once`. Les synchronisations sont idempotentes — réexécuter un manifeste propre est sûr et résout souvent un incident passager.
-2. **Prévisualiser le plan.** Exécutez `yarn twenty dev --once --dry-run` pour voir exactement ce que la prochaine synchronisation compte changer, sans l'appliquer.
+1. **Resynchroniser.** Exécutez à nouveau `yarn twenty apply`. Les synchronisations sont idempotentes — réexécuter un manifeste propre est sûr et résout souvent un incident passager.
+2. **Prévisualiser le plan.** Exécutez `yarn twenty plan` pour voir exactement ce que la prochaine synchronisation compte changer, sans l'appliquer.
3. **Lire l'erreur nommée.** Si une synchronisation échoue, relevez le type de métadonnées et l'`universalIdentifier` dans le message (voir ci-dessus) et localisez cette entité dans votre manifeste. Un conflit pointe généralement vers un identifiant dupliqué ou réutilisé.
4. **Désinstaller et réinstaller.** `yarn twenty app:uninstall`, puis synchronisez à nouveau (`yarn twenty dev`). Cette opération reconstruit les métadonnées de l'application à partir d'une base saine tout en gardant le reste de votre espace de travail intact.
5. **Réinitialisation complète (en dernier recours).** `yarn twenty docker:reset`, puis réinjectez des données et resynchronisez.
diff --git a/packages/twenty-docs/l/fr/developers/extend/apps/operations/testing.mdx b/packages/twenty-docs/l/fr/developers/extend/apps/operations/testing.mdx
index b44ab05f4d..b83dbf932f 100644
--- a/packages/twenty-docs/l/fr/developers/extend/apps/operations/testing.mdx
+++ b/packages/twenty-docs/l/fr/developers/extend/apps/operations/testing.mdx
@@ -78,6 +78,13 @@ Créez un `vitest.config.ts` à la racine de votre application :
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 ?? '';
+
+// 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({
@@ -88,66 +95,74 @@ export default defineConfig({
test: {
testTimeout: 120_000,
hookTimeout: 120_000,
+ fileParallelism: false,
include: ['src/**/*.integration-test.ts'],
- setupFiles: ['src/__tests__/setup-test.ts'],
+ globalSetup: ['src/__tests__/global-setup.ts'],
env: {
- TWENTY_API_URL: 'http://localhost:2020',
- TWENTY_API_KEY: 'your-api-key',
+ TWENTY_API_URL,
+ TWENTY_API_KEY,
},
},
});
```
-Créez un fichier de configuration qui vérifie que le serveur est joignable avant l'exécution des tests :
+Créez un fichier de configuration globale qui vérifie que le serveur est joignable, écrit une configuration de test pour le SDK (`~/.twenty/config.test.json`) et synchronise l’application avant l’exécution des tests :
-```ts src/__tests__/setup-test.ts
+```ts src/__tests__/global-setup.ts
import * as fs from 'fs';
import * as os from 'os';
import * as path from 'path';
-import { beforeAll } from 'vitest';
-const TWENTY_API_URL = process.env.TWENTY_API_URL ?? 'http://localhost:2020';
-const TEST_CONFIG_DIR = path.join(os.tmpdir(), '.twenty-sdk-test');
+import { appDevOnce, appUninstall } from 'twenty-sdk/cli';
+
+const APP_PATH = process.cwd();
+const CONFIG_DIR = path.join(os.homedir(), '.twenty');
+
+export async function setup() {
+ const apiUrl = process.env.TWENTY_API_URL!;
+ const apiKey = process.env.TWENTY_API_KEY!;
-beforeAll(async () => {
// Verify the server is running
- const response = await fetch(`${TWENTY_API_URL}/healthz`);
-
+ const response = await fetch(`${apiUrl}/healthz`);
if (!response.ok) {
- throw new Error(
- `Twenty server is not reachable at ${TWENTY_API_URL}. ` +
- 'Start the server before running integration tests.',
- );
+ throw new Error(`Twenty server is not reachable at ${apiUrl}.`);
}
- // Write a temporary config for the SDK
- fs.mkdirSync(TEST_CONFIG_DIR, { recursive: true });
-
+ // Write the SDK's test config (the CLI reads config.test.json when NODE_ENV=test)
+ fs.mkdirSync(CONFIG_DIR, { recursive: true });
fs.writeFileSync(
- path.join(TEST_CONFIG_DIR, 'config.json'),
+ path.join(CONFIG_DIR, 'config.test.json'),
JSON.stringify({
- remotes: {
- local: {
- apiUrl: process.env.TWENTY_API_URL,
- apiKey: process.env.TWENTY_API_KEY,
- },
- },
+ remotes: { local: { apiUrl, apiKey } },
defaultRemote: 'local',
}, null, 2),
);
-});
+
+ // Start from a clean slate, then sync the app
+ await appUninstall({ appPath: APP_PATH }).catch(() => {});
+
+ const result = await appDevOnce({ appPath: APP_PATH });
+ if (!result.success) {
+ throw new Error(`Dev sync failed: ${result.error?.message}`);
+ }
+}
+
+export async function teardown() {
+ await appUninstall({ appPath: APP_PATH });
+}
```
## APIs programmatiques du SDK
Le sous-chemin `twenty-sdk/cli` exporte des fonctions que vous pouvez appeler directement depuis le code de test :
-| Fonction | Description |
-| -------------- | -------------------------------------------------------------------- |
-| `appBuild` | Construire l'application et éventuellement créer une archive tarball |
-| `appDeploy` | Téléverser une archive tarball vers le serveur |
-| `appInstall` | Installer l'application sur l'espace de travail actif |
-| `appUninstall` | Désinstaller l'application de l'espace de travail actif |
+| Fonction | Description |
+| -------------- | ----------------------------------------------------------------------------------- |
+| `appBuild` | Construire l'application et éventuellement créer une archive tarball |
+| `appDeploy` | Téléverser une archive tarball vers le serveur |
+| `appDevOnce` | Construire et synchroniser l’application une fois (identique à `yarn twenty apply`) |
+| `appInstall` | Installer l'application sur l'espace de travail actif |
+| `appUninstall` | Désinstaller l'application de l'espace de travail actif |
Chaque fonction retourne un objet résultat avec `success: boolean` et soit `data` soit `error`.
@@ -238,64 +253,10 @@ Vous pouvez également exécuter une vérification des types sur votre applicati
yarn twenty dev:typecheck
```
-Cela exécute `tsc --noEmit` et signale toute erreur de type.
+Cela exécute `tsc --noEmit` sur le `tsconfig.json` de votre application et signale toute erreur de type. Les applications générées contiennent également un script `yarn typecheck` qui couvre aussi les fichiers de test (`tsconfig.spec.json`).
## CI avec GitHub Actions
-Le générateur crée un workflow GitHub Actions prêt à l’emploi dans `.github/workflows/ci.yml`. Il exécute automatiquement vos tests d’intégration à chaque push sur `main` et sur les pull requests.
+Le générateur crée un workflow prêt à l’emploi dans `.github/workflows/ci.yml`. À chaque push sur `main` et à chaque pull request, il lance un serveur Twenty éphémère dans le runner (via l’action `twentyhq/twenty/.github/actions/spawn-twenty-app-dev-test`), puis exécute `yarn lint`, `yarn typecheck`, `yarn test:unit` et `yarn test` avec `TWENTY_API_URL` / `TWENTY_API_KEY` pointant vers ce serveur. Aucun secret n’est requis, et vous pouvez fixer la version du serveur via la variable d’environnement `TWENTY_VERSION` en haut du workflow.
-Le workflow :
-
-1. Récupère votre code
-2. Lance un serveur Twenty temporaire en utilisant l’action `twentyhq/twenty/.github/actions/spawn-twenty-docker-image`
-3. Installe les dépendances avec `yarn install --immutable`
-4. Exécute `yarn test` avec `TWENTY_API_URL` et `TWENTY_API_KEY` injectés à partir des sorties de l’action
-
-```yaml .github/workflows/ci.yml
-name: CI
-
-on:
- push:
- branches:
- - main
- pull_request: {}
-
-env:
- TWENTY_VERSION: latest
-
-jobs:
- test:
- runs-on: ubuntu-latest
- steps:
- - name: Checkout
- uses: actions/checkout@v4
-
- - name: Spawn Twenty instance
- id: twenty
- uses: twentyhq/twenty/.github/actions/spawn-twenty-docker-image@main
- with:
- twenty-version: ${{ env.TWENTY_VERSION }}
- github-token: ${{ secrets.GITHUB_TOKEN }}
-
- - name: Enable Corepack
- run: corepack enable
-
- - name: Setup Node.js
- uses: actions/setup-node@v4
- with:
- node-version-file: '.nvmrc'
- cache: 'yarn'
-
- - name: Install dependencies
- run: yarn install --immutable
-
- - name: Run integration tests
- run: yarn test
- env:
- TWENTY_API_URL: ${{ steps.twenty.outputs.server-url }}
- TWENTY_API_KEY: ${{ steps.twenty.outputs.access-token }}
-```
-
-Vous n’avez pas besoin de configurer de secrets — l’action `spawn-twenty-docker-image` démarre un serveur Twenty éphémère directement dans le runner et fournit les détails de connexion. Le secret `GITHUB_TOKEN` est fourni automatiquement par GitHub.
-
-Pour épingler une version spécifique de Twenty au lieu de `latest`, modifiez la variable d’environnement `TWENTY_VERSION` en haut du workflow.
+Voir [Publication → CI/CD automatisé](/l/fr/developers/extend/apps/operations/publishing#automated-cicd-scaffolded-workflows) pour un guide complet des deux workflows générés (`ci.yml` et le pipeline de déploiement `cd.yml`).
diff --git a/packages/twenty-docs/l/fr/developers/extend/apps/tutorials/document-generator/building-the-ui.mdx b/packages/twenty-docs/l/fr/developers/extend/apps/tutorials/document-generator/building-the-ui.mdx
index f415ea40c7..8028a5a515 100644
--- a/packages/twenty-docs/l/fr/developers/extend/apps/tutorials/document-generator/building-the-ui.mdx
+++ b/packages/twenty-docs/l/fr/developers/extend/apps/tutorials/document-generator/building-the-ui.mdx
@@ -91,9 +91,11 @@ const GenerateDocumentForm = () => {
}, []);
const generate = async () => {
- const apiBaseUrl = process.env.TWENTY_API_URL;
+ // Prefer the injected functions URL; fall back to the legacy /s prefix (self-hosted/local)
+ const functionsBaseUrl =
+ process.env.TWENTY_FUNCTIONS_URL || `${process.env.TWENTY_API_URL}/s`;
const token = process.env.TWENTY_APP_ACCESS_TOKEN ?? process.env.TWENTY_API_KEY;
- const res = await fetch(`${apiBaseUrl}/s/documents/generate`, {
+ const res = await fetch(`${functionsBaseUrl}/documents/generate`, {
method: 'POST',
headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` },
body: JSON.stringify({ templateId, recordId }),
@@ -185,7 +187,9 @@ const DocumentViewer = () => {
const recordId = useFrontComponentExecutionContext((c) => c.recordId ?? null);
// ...load { content, file } for recordId, then derive the links:
const pdfUrl = document.file?.[0]?.url;
- const webUrl = `${process.env.TWENTY_API_URL ?? ''}/s/documents/view?id=${recordId}`;
+ const functionsBaseUrl =
+ process.env.TWENTY_FUNCTIONS_URL || `${process.env.TWENTY_API_URL ?? ''}/s`;
+ const webUrl = `${functionsBaseUrl}/documents/view?id=${recordId}`;
// Render the template body, plus quick links to the web page and the PDF.
// Links open in a new tab so they don't navigate the embedded component.
diff --git a/packages/twenty-docs/l/fr/developers/extend/apps/tutorials/document-generator/http-routes.mdx b/packages/twenty-docs/l/fr/developers/extend/apps/tutorials/document-generator/http-routes.mdx
index 0b3c04b69c..785b8cae46 100644
--- a/packages/twenty-docs/l/fr/developers/extend/apps/tutorials/document-generator/http-routes.mdx
+++ b/packages/twenty-docs/l/fr/developers/extend/apps/tutorials/document-generator/http-routes.mdx
@@ -9,8 +9,15 @@ Le même gestionnaire peut également répondre aux requêtes HTTP. Nous allons
* un point de terminaison **POST** que l'interface utilisateur appelle pour générer un document, et
* un point de terminaison public **GET** qui rend un document en tant que page web imprimable.
-Les deux utilisent `httpRouteTriggerSettings`. Les routes des applis sont servies dans `/s` sur votre
-Serveur Vingt (par exemple `http://localhost:2020/s/documents/generate`).
+Les deux utilisent `httpRouteTriggerSettings`. Sur le serveur de développement local, les routes des applications sont
+servies sous le préfixe `/s` (par exemple `http://localhost:2020/s/documents/generate`).
+
+
+Sur Twenty Cloud, les routes sont servies sur le domaine de fonctions dédiées à l'espace de travail
+— l'URL 20 injecte en tant que `TWENTY_FUNCTIONS_URL`, sans préfixe `/s`. Le préfixe `/s`
+est déprécié là-bas et ne reste que pour les instances auto-hébergées et locales.
+Voir [Appel à une fonction logique] (/developers/extend/apps/layout/front-components#calling-a-logic-function).
+
## Itinéraire POST — générer à la demande
diff --git a/packages/twenty-docs/l/fr/developers/extend/apps/tutorials/document-generator/publishing.mdx b/packages/twenty-docs/l/fr/developers/extend/apps/tutorials/document-generator/publishing.mdx
index 705c29fa1b..b4ab797a46 100644
--- a/packages/twenty-docs/l/fr/developers/extend/apps/tutorials/document-generator/publishing.mdx
+++ b/packages/twenty-docs/l/fr/developers/extend/apps/tutorials/document-generator/publishing.mdx
@@ -77,11 +77,11 @@ Exécuter les mêmes portes CI :
yarn lint # oxlint
yarn typecheck # tsgo
yarn test:unit # unit tests
-yarn twenty dev --once --dry-run # preview the metadata diff
+yarn twenty plan # preview the metadata diff
```
-La course à sec imprime exactement ce qui pourrait changer sur le serveur sans l'appliquer —
-une bonne vérification de l'état d'esprit. Voir
+Le plan affiche exactement ce qui changerait sur le serveur sans les appliquer —
+un bon dernier contrôle de cohérence. Voir
[Testing](/l/fr/developers/extend/apps/operations/testing) et
[Synchronisation et récupération](/l/fr/developers/extend/apps/operations/sync-and-recovery).
diff --git a/packages/twenty-docs/l/it/developers/extend/apps/config/install-hooks.mdx b/packages/twenty-docs/l/it/developers/extend/apps/config/install-hooks.mdx
index 551058934d..d16759efbd 100644
--- a/packages/twenty-docs/l/it/developers/extend/apps/config/install-hooks.mdx
+++ b/packages/twenty-docs/l/it/developers/extend/apps/config/install-hooks.mdx
@@ -4,9 +4,9 @@ description: Esegui logica prima o dopo l'installazione — popola i dati, esegu
icon: wrench
---
-Gli hook di installazione sono funzioni logiche speciali che vengono eseguite durante il ciclo di vita di installazione o aggiornamento. Condividono lo stesso runtime del gestore delle [logic functions](/l/it/developers/extend/apps/logic/logic-functions) normali e ricevono un `InstallPayload`, ma sono dichiarati con le proprie funzioni di definizione — `definePostInstallLogicFunction()` e `definePreInstallLogicFunction()` — e vivono al di fuori del normale modello di trigger (eventi HTTP, cron, database).
+Gli hook di installazione sono funzioni logiche speciali che vengono eseguite durante il ciclo di vita di installazione o aggiornamento. Condividono lo stesso runtime del gestore delle [logic functions](/l/it/developers/extend/apps/logic/logic-functions) normali e ricevono un `InstallPayload` (`{ previousVersion?: string; newVersion: string }` — `previousVersion` è `undefined` in una nuova installazione), ma sono dichiarati con le proprie funzioni di definizione e vivono al di fuori del normale modello di trigger (HTTP, eventi cron, eventi del database).
-Ogni app può definire **al massimo una funzione di pre-installazione** e **al massimo una funzione di post-installazione**. La build del manifesto genererà un errore se ne viene rilevata più di una per ciascun tipo.
+Ogni app può definire **al massimo una funzione di pre-installazione** e **al massimo una funzione di post-installazione**. La build del manifesto genera un errore se ne viene rilevata più di una per ciascun tipo.
```
┌─────────────────────────────────────────────────────────────┐
@@ -19,111 +19,59 @@ Ogni app può definire **al massimo una funzione di pre-installazione** e **al m
└─────────────────────────────────────────────────────────────┘
```
-
-
+## A colpo d'occhio
-Una funzione di post-installazione viene eseguita automaticamente una volta che la tua app ha terminato l'installazione in uno spazio di lavoro. Il server la esegue **dopo** che i metadati dell'app sono stati sincronizzati e il client SDK è stato generato, così lo spazio di lavoro è completamente pronto per l'uso e il nuovo schema è attivo. I casi d'uso tipici includono il popolamento di dati predefiniti, la creazione di record iniziali, la configurazione delle impostazioni dello spazio di lavoro o il provisioning di risorse su servizi di terze parti.
+| | `definePreInstallLogicFunction` | `definePostInstallLogicFunction` |
+| ----------------- | ------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
+| Esecuzioni | Prima della migrazione dei metadati — lo schema e i dati **precedenti** sono ancora intatti | Dopo la migrazione e la generazione dell'SDK — il **nuovo** schema è in vigore |
+| Esecuzione | Sempre sincrona; blocca l'installazione | Async per impostazione predefinita (in coda, 3 tentativi); modalità sync tramite opt-in con `shouldRunSynchronously: true` |
+| In caso di errore | L'installazione viene **annullata** prima di qualsiasi modifica allo schema | Async: ritentato fino a 3 volte. Sync: il chiamante riceve `POST_INSTALL_ERROR` (le modifiche allo schema **non** vengono annullate) |
+| Uso tipico | Eseguire il backup o correggere dati che una migrazione perderebbe; rifiutare un aggiornamento rischioso lanciando un'eccezione | Popolare dati predefiniti, configurare il workspace, registrare risorse esterne |
-```ts src/logic-functions/post-install.ts
-import { definePostInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define';
+**Regola generale:** usa post-install come impostazione predefinita. Ricorri al pre-install solo quando la migrazione stessa è distruttiva e devi intercettare lo stato precedente prima che vada perso.
-const handler = async (payload: InstallPayload): Promise => {
- console.log('Post install logic function executed successfully!', payload.previousVersion);
-};
+| Vuoi... | Usa |
+| ------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------- |
+| Popolare i dati, configurare il workspace, registrare risorse esterne | `post-install` |
+| Lavoro di lunga durata che non dovrebbe bloccare la risposta dell'installazione | `post-install` (modalità async predefinita, con retry del worker) |
+| Eseguire un setup rapido da cui il chiamante dipende immediatamente dopo il completamento dell'installazione | `post-install` con `shouldRunSynchronously: true` |
+| Leggere o eseguire il backup dei dati che la prossima migrazione perderebbe | `pre-install` |
+| Rifiutare un aggiornamento che corromperebbe i dati esistenti | `pre-install` (genera un'eccezione dall'handler) |
+| Riconciliazione a ogni aggiornamento | Uno qualsiasi dei due hook con `shouldRunOnVersionUpgrade: true` |
-export default definePostInstallLogicFunction({
- universalIdentifier: 'f7a2b9c1-3d4e-5678-abcd-ef9876543210',
- name: 'post-install',
- description: 'Runs after installation to set up the application.',
- timeoutSeconds: 300,
- shouldRunOnVersionUpgrade: false,
- shouldRunSynchronously: false,
- handler,
-});
-```
+## Comportamento condiviso da entrambi gli hook
-Puoi anche eseguire manualmente la funzione di post-installazione in qualsiasi momento utilizzando la CLI:
+* La config è una config di `defineLogicFunction` meno le impostazioni di trigger, più `shouldRunOnVersionUpgrade`.
+* **Quando viene eseguito**: solo sulle nuove installazioni, per impostazione predefinita. Imposta `shouldRunOnVersionUpgrade: true` per eseguirlo anche sugli upgrade. Usa `previousVersion` / `newVersion` per ramificare in base al percorso di upgrade.
+* **L'idempotenza è importante**: il post-install async può essere ritentato e qualsiasi hook viene rieseguito sugli upgrade quando `shouldRunOnVersionUpgrade` è attivo.
+* Il consueto ambiente delle logic-function (`APPLICATION_ID`, `APP_ACCESS_TOKEN`, `API_URL`) viene iniettato, così puoi chiamare le API di Twenty con il token della tua app.
+* L'hook viene collegato automaticamente al manifesto dell'applicazione in fase di build (`preInstallLogicFunction` / `postInstallLogicFunction`) — non c'è nulla da referenziare in [`defineApplication()`](/l/it/developers/extend/apps/config/application).
+* Il `timeoutSeconds` predefinito è 300 per consentire attività di setup più lunghe, come il seeding dei dati.
+* **Non eseguito in modalità dev**: `yarn twenty dev` salta il flusso di installazione e sincronizza direttamente i file, quindi gli hook non vengono mai eseguiti in quell'ambiente. Attivali invece manualmente:
```bash filename="Terminal"
yarn twenty dev:function:exec --postInstall
-```
-
-Punti chiave:
-* Le funzioni di post-installazione utilizzano `definePostInstallLogicFunction()` — una variante specializzata che omette le impostazioni dei trigger (`cronTriggerSettings`, `databaseEventTriggerSettings`, `httpRouteTriggerSettings`, `toolTriggerSettings`, `workflowActionTriggerSettings`).
-* L'handler riceve un `InstallPayload` con `{ previousVersion?: string; newVersion: string }` — `newVersion` è la versione in fase di installazione e `previousVersion` è la versione installata in precedenza (oppure `undefined` in caso di nuova installazione). Usa questi valori per distinguere le nuove installazioni dagli aggiornamenti e per eseguire logiche di migrazione specifiche per versione.
-* **Quando viene eseguito l'hook**: solo sulle nuove installazioni, per impostazione predefinita. Passa `shouldRunOnVersionUpgrade: true` se vuoi che venga eseguito anche quando l'app viene aggiornata da una versione precedente. Se omesso, il flag è `false` per impostazione predefinita e gli aggiornamenti saltano l'hook.
-* **Modello di esecuzione — asincrono per impostazione predefinita, sincrono su richiesta**: il flag `shouldRunSynchronously` controlla *come* viene eseguito il post-install.
- * `shouldRunSynchronously: false` *(default)* — l'hook viene **messo in coda nella coda dei messaggi** con `retryLimit: 3` ed eseguito in modo asincrono in un worker. La risposta di installazione viene restituita non appena il job è messo in coda, quindi un handler lento o in errore non blocca il chiamante. Il worker riproverà fino a tre volte. **Usalo per job di lunga durata** — popolamento di dataset di grandi dimensioni, chiamate a API di terze parti lente, provisioning di risorse esterne, qualsiasi cosa che possa superare una finestra di risposta HTTP ragionevole.
- * `shouldRunSynchronously: true` — l'hook viene eseguito **inline durante il flusso di installazione** (stesso executor del pre-install). La richiesta di installazione rimane bloccata finché l'handler non termina e, se genera un'eccezione, il chiamante dell'installazione riceve un `POST_INSTALL_ERROR`. Nessun tentativo automatico. **Usalo per attività rapide che devono completarsi prima della risposta** — ad esempio, emettere un errore di validazione all'utente, oppure un setup rapido di cui il client avrà bisogno immediatamente dopo il ritorno della chiamata di installazione. Tieni presente che la migrazione dei metadati è già stata applicata quando viene eseguito il post-install, quindi un errore in modalità sincrona **non** annulla le modifiche allo schema — si limita a far emergere l'errore.
-* Assicurati che il tuo handler sia idempotente. In modalità asincrona la coda può riprovare fino a tre volte; in entrambe le modalità l'hook può essere eseguito di nuovo durante gli aggiornamenti quando `shouldRunOnVersionUpgrade: true`.
-* Le variabili d'ambiente `APPLICATION_ID`, `APP_ACCESS_TOKEN` e `API_URL` sono disponibili all'interno dell'handler (come in qualsiasi altra funzione logica), quindi puoi chiamare le API di Twenty con un token di accesso applicativo con ambito sulla tua app.
-* È consentita una sola funzione di post-installazione per applicazione. La build del manifesto genererà un errore se ne viene rilevata più di una.
-* I campi `universalIdentifier`, `shouldRunOnVersionUpgrade` e `shouldRunSynchronously` della funzione vengono associati automaticamente al manifest dell'applicazione nel campo `postInstallLogicFunction` durante la build — non è necessario referenziarli in [`defineApplication()`](/l/it/developers/extend/apps/config/application).
-* Il timeout predefinito è impostato a 300 secondi (5 minuti) per consentire attività di configurazione più lunghe, come il popolamento dei dati.
-* **Non eseguito in modalità dev**: quando un'app è registrata in locale (tramite `yarn twenty dev`), il server salta completamente il flusso di installazione e sincronizza i file direttamente tramite il watcher della CLI — quindi il post-install non viene mai eseguito in modalità dev, indipendentemente da `shouldRunSynchronously`. Usa `yarn twenty dev:function:exec --postInstall` per attivarlo manualmente su un workspace in esecuzione.
-
-
-
-
-Una funzione di pre-installazione viene eseguita automaticamente durante l'installazione, **prima che venga applicata la migrazione dei metadati dello spazio di lavoro**. Condivide la stessa struttura di payload del post-install (`InstallPayload`), ma è posizionata prima nel flusso di installazione così da poter preparare lo stato da cui dipenderà la migrazione imminente — usi tipici includono il backup dei dati, la validazione della compatibilità con il nuovo schema o l'archiviazione di record che stanno per essere ristrutturati o eliminati.
-
-```ts src/logic-functions/pre-install.ts
-import { definePreInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define';
-
-const handler = async (payload: InstallPayload): Promise => {
- console.log('Pre install logic function executed successfully!', payload.previousVersion);
-};
-
-export default definePreInstallLogicFunction({
- universalIdentifier: 'a1b2c3d4-5678-90ab-cdef-1234567890ab',
- name: 'pre-install',
- description: 'Runs before installation to prepare the application.',
- timeoutSeconds: 300,
- shouldRunOnVersionUpgrade: true,
- handler,
-});
-```
-
-Puoi anche eseguire manualmente la funzione di pre-installazione in qualsiasi momento utilizzando la CLI:
-
-```bash filename="Terminal"
yarn twenty dev:function:exec --preInstall
```
-Punti chiave:
-* Le funzioni di pre-install usano `definePreInstallLogicFunction()` — stessa configurazione specialistica del post-install, solo agganciata a uno slot di ciclo di vita diverso.
-* Sia gli handler di pre- sia quelli di post-install ricevono lo stesso tipo `InstallPayload`: `{ previousVersion?: string; newVersion: string }`. Importalo una volta e riutilizzalo per entrambi gli hook.
-* **Quando viene eseguito l'hook**: posizionato appena prima della migrazione dei metadati del workspace (`synchronizeFromManifest`). Prima dell'esecuzione, il server esegue una "sincronizzazione ridotta" puramente additiva che registra nei metadati del workspace la funzione di pre-install della versione **nuova** — nient'altro viene toccato — e poi la esegue. Poiché questa sincronizzazione è solo additiva, gli oggetti, i campi e i dati della versione precedente restano intatti quando il tuo handler viene eseguito: puoi leggere ed eseguire in sicurezza il backup dello stato pre-migrazione.
-* **Modello di esecuzione**: il pre-install è eseguito **in modo sincrono** e **blocca l'installazione**. Se l'handler genera un'eccezione, l'installazione viene interrotta prima che vengano applicate modifiche allo schema — il workspace rimane sulla versione precedente in uno stato coerente. Questo è intenzionale: il pre-install è la tua ultima possibilità per rifiutare un aggiornamento rischioso.
-* Come per il post-install, è consentita una sola funzione di pre-installazione per applicazione. Viene collegata automaticamente al manifest dell'applicazione nel campo `preInstallLogicFunction` durante la build.
-* **Non eseguito in modalità dev**: come per il post-install — il flusso di installazione viene completamente saltato per le app registrate localmente, quindi il pre-install non viene mai eseguito con `yarn twenty dev`. Usa `yarn twenty dev:function:exec --preInstall` per attivarlo manualmente.
+
+
-
-
-
-Entrambi gli hook fanno parte dello stesso flusso di installazione e ricevono lo stesso `InstallPayload`. La differenza è **quando** vengono eseguiti rispetto alla migrazione dei metadati del workspace, e questo modifica quali dati possono gestire in sicurezza.
-
-Il pre-install è sempre **sincrono** (blocca l'installazione e può interromperla). Il post-install è **asincrono per impostazione predefinita** — messo in coda su un worker con retry automatici — ma può optare per l'esecuzione sincrona con `shouldRunSynchronously: true`. Vedi l'accordion `definePostInstallLogicFunction` sopra per quando usare ciascuna modalità.
-
-**Usa `post-install` per tutto ciò che richiede l'esistenza del nuovo schema.** Questo è il caso più comune:
-
-* Popolamento di dati predefiniti (creazione di record iniziali, viste predefinite, contenuti demo) su oggetti e campi appena aggiunti.
-* Registrazione di webhook con servizi di terze parti ora che l'app ha le proprie credenziali.
-* Chiamare la tua API per completare il setup che dipende dai metadati sincronizzati.
-* Logica idempotente di "ensure this exists" che dovrebbe riconciliare lo stato a ogni aggiornamento — da combinare con `shouldRunOnVersionUpgrade: true`.
-
-Esempio — eseguire il seeding di un record `PostCard` predefinito dopo l'installazione:
+Viene eseguito una volta che l'installazione della tua app è terminata: metadati sincronizzati, client SDK generato, nuovo schema interrogabile. Esempio — eseguire il seeding di un record predefinito nelle nuove installazioni:
```ts src/logic-functions/post-install.ts
import { definePostInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define';
-import { createClient } from './generated/client';
+import { CoreApiClient } from 'twenty-client-sdk/core';
const handler = async ({ previousVersion }: InstallPayload): Promise => {
if (previousVersion) return; // fresh installs only
- const client = createClient();
- await client.postCard.create({
- data: { title: 'Welcome to Postcard', content: 'Your first card!' },
+ const client = new CoreApiClient();
+ await client.mutation({
+ createPostCard: {
+ __args: { data: { name: 'Welcome to Postcard', content: 'Your first card!' } },
+ id: true,
+ },
});
};
@@ -133,22 +81,28 @@ export default definePostInstallLogicFunction({
description: 'Seeds a welcome post card after install.',
timeoutSeconds: 300,
shouldRunOnVersionUpgrade: false,
+ shouldRunSynchronously: false,
handler,
});
```
-**Usa `pre-install` quando una migrazione altrimenti distruggerebbe o corromperebbe i dati esistenti.** Poiché il pre-install viene eseguito contro lo schema *precedente* e un suo fallimento annulla l'aggiornamento, è il posto giusto per qualsiasi operazione rischiosa:
+Il flag `shouldRunSynchronously` controlla il modello di esecuzione:
-* **Eseguire il backup dei dati che stanno per essere eliminati o ristrutturati** — ad esempio, stai rimuovendo un campo nella v2 e devi copiarne i valori in un altro campo o esportarli su uno storage prima che venga eseguita la migrazione.
-* **Archiviare i record che un nuovo vincolo renderebbe non validi** — ad esempio, un campo sta diventando `NOT NULL` e devi prima eliminare o correggere le righe con valori nulli.
-* **Validare la compatibilità e rifiutare l'aggiornamento se i dati attuali non possono essere migrati correttamente** — genera un'eccezione dall'handler e l'installazione si interrompe senza applicare modifiche. Questo è più sicuro che scoprire l'incompatibilità a migrazione in corso.
-* **Rinominare o rigenerare le chiavi dei dati** prima di una modifica dello schema che farebbe perdere l'associazione.
+* `false` *(predefinito)* — messo in coda nella message queue (`retryLimit: 3`) ed eseguito da un worker. La risposta dell'installazione ritorna non appena il job viene messo in coda. **Da usare per lavoro di lunga durata** — seeding di grandi dataset, API di terze parti lente.
+* `true` — eseguito inline durante il flusso di installazione. La richiesta di installazione rimane bloccata finché l'handler non termina; un errore lanciato viene esposto al chiamante come `POST_INSTALL_ERROR` (nessun retry). **Da usare per lavoro rapido che deve completarsi prima della risposta.** La migrazione è già stata applicata a questo punto, quindi un errore non annulla le modifiche allo schema — si limita a esporre l'errore.
-Esempio — archiviare i record prima di una migrazione distruttiva:
+
+
+
+Viene eseguito prima della migrazione dei metadati, contro lo schema **precedente** — il posto giusto per eseguire il backup di dati che una migrazione perderebbe o per rifiutare un upgrade rischioso. Prima dell'esecuzione, il server esegue una "sincronizzazione ridotta" puramente additiva che registra solo la funzione di pre-install della versione nuova; tutto il resto — oggetti, campi e dati della versione precedente — rimane intatto quando il tuo handler viene eseguito.
+
+Il pre-install è sempre **sincrono** e blocca l'installazione. Se l'handler genera un'eccezione, l'installazione viene interrotta prima che venga applicata qualsiasi modifica allo schema — il workspace rimane sulla versione precedente in uno stato coerente. Questo è intenzionale: il pre-install è la tua ultima possibilità per rifiutare un aggiornamento rischioso.
+
+Esempio — copiare i valori di un campo legacy prima che la migrazione lo elimini:
```ts src/logic-functions/pre-install.ts
import { definePreInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define';
-import { createClient } from './generated/client';
+import { CoreApiClient } from 'twenty-client-sdk/core';
const handler = async ({ previousVersion, newVersion }: InstallPayload): Promise => {
// Only the 1.x → 2.x upgrade drops the legacy `notes` field.
@@ -156,24 +110,24 @@ const handler = async ({ previousVersion, newVersion }: InstallPayload): Promise
return;
}
- const client = createClient();
- const legacyRecords = await client.postCard.findMany({
- where: { notes: { isNotNull: true } },
+ const client = new CoreApiClient();
+ const { postCards } = await client.query({
+ postCards: {
+ __args: { filter: { notes: { isNot: null } } },
+ edges: { node: { id: true, notes: true } },
+ },
});
- if (legacyRecords.length === 0) return;
-
- // Copy legacy `notes` into the new `description` field before the migration
- // drops the `notes` column. If this fails, the upgrade is aborted and the
- // workspace stays on v1 with all data intact.
- await Promise.all(
- legacyRecords.map((record) =>
- client.postCard.update({
- where: { id: record.id },
- data: { description: record.notes },
- }),
- ),
- );
+ // Copy legacy `notes` into `description` before the migration drops the
+ // column. If this fails, the upgrade aborts and the workspace stays on v1.
+ for (const { node } of postCards.edges) {
+ await client.mutation({
+ updatePostCard: {
+ __args: { id: node.id, data: { description: node.notes } },
+ id: true,
+ },
+ });
+ }
};
export default definePreInstallLogicFunction({
@@ -186,21 +140,5 @@ export default definePreInstallLogicFunction({
});
```
-**Regola generale:**
-
-| Vuoi... | Usa |
-| ---------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ |
-| Popolare dati predefiniti, configurare il workspace, registrare risorse esterne | `post-install` |
-| Eseguire seeding di lunga durata o chiamate a terze parti che non dovrebbero bloccare la risposta dell'installazione | `post-install` (predefinito — `shouldRunSynchronously: false`, con retry del worker) |
-| Eseguire un setup rapido di cui il chiamante farà affidamento immediatamente dopo il ritorno della chiamata di installazione | `post-install` con `shouldRunSynchronously: true` |
-| Leggere o eseguire il backup dei dati che la prossima migrazione perderebbe | `pre-install` |
-| Rifiutare un aggiornamento che corromperebbe i dati esistenti | `pre-install` (genera un'eccezione dall'handler) |
-| Eseguire la riconciliazione a ogni aggiornamento | `post-install` con `shouldRunOnVersionUpgrade: true` |
-| Eseguire un setup una tantum solo alla prima installazione | `post-install` con `shouldRunOnVersionUpgrade: false` (predefinito) |
-
-
-In caso di dubbio, usa **post-install**. Ricorri al pre-install solo quando la migrazione stessa è distruttiva e devi intercettare lo stato precedente prima che vada perso.
-
-
diff --git a/packages/twenty-docs/l/it/developers/extend/apps/data/objects.mdx b/packages/twenty-docs/l/it/developers/extend/apps/data/objects.mdx
index 61e8b0f634..7030e3458e 100644
--- a/packages/twenty-docs/l/it/developers/extend/apps/data/objects.mdx
+++ b/packages/twenty-docs/l/it/developers/extend/apps/data/objects.mdx
@@ -86,6 +86,22 @@ export default defineObject({
**I campi base vengono aggiunti automaticamente.** Quando definisci un oggetto personalizzato, Twenty crea per te campi standard come `id`, `name`, `createdAt`, `updatedAt`, `createdBy`, `updatedBy` e `deletedAt`. Non è necessario dichiararli nel tuo array `fields` — solo i tuoi campi personalizzati. Puoi sovrascrivere un campo predefinito dichiarandone uno con lo stesso nome, ma è raramente una buona idea.
+## Tipi di campo
+
+L’insieme completo dei valori di `FieldType`, esportati da `twenty-sdk/define`:
+
+| Categoria | Tipi |
+| -------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
+| Testo | `TEXT`, `RICH_TEXT`, `ARRAY` (di stringhe), `RAW_JSON` |
+| Numerico | `NUMBER` (`universalSettings.dataType`: `'float'` / `'int'` / `'bigint'`), `NUMERIC` (precisione arbitraria), `RATING`, `POSITION` |
+| Date | `DATE`, `DATE_TIME` |
+| Scelta | `BOOLEAN`, `SELECT`, `MULTI_SELECT` |
+| Composito | `FULL_NAME`, `ADDRESS`, `EMAILS`, `PHONES`, `LINKS`, `CURRENCY`, `ACTOR`, `FILES` |
+| Identificatori e relazioni | `UUID`, `RELATION`, `MORPH_RELATION` (vedi [Relazioni](/l/it/developers/extend/apps/data/relations)) |
+| Sistema | `TS_VECTOR` (vettore per la ricerca full-text, gestito dal server) |
+
+I tipi compositi memorizzano più sotto-campi (ad es. `FULL_NAME` = nome + cognome; `CURRENCY` = `amountMicros` + `currencyCode`). `SELECT` e `MULTI_SELECT` richiedono un array `options` come nell’esempio sopra.
+
## Valori predefiniti
I valori predefiniti letterali devono essere racchiusi tra apici singoli **all'interno** della stringa — `defaultValue: "'Draft'"`, non `defaultValue: "Draft"`. Ecco perché il campo `status` sopra utilizza `` `'${PostCardStatus.DRAFT}'` ``.
diff --git a/packages/twenty-docs/l/it/developers/extend/apps/getting-started/project-structure.mdx b/packages/twenty-docs/l/it/developers/extend/apps/getting-started/project-structure.mdx
index e92a1af1a5..0ea3128c76 100644
--- a/packages/twenty-docs/l/it/developers/extend/apps/getting-started/project-structure.mdx
+++ b/packages/twenty-docs/l/it/developers/extend/apps/getting-started/project-structure.mdx
@@ -14,26 +14,39 @@ my-twenty-app/
default-role.ts # Permissions for logic functions
constants/
universal-identifiers.ts # Auto-generated UUIDs and metadata
+ front-components/
+ main-page.tsx # Welcome page component
+ navigation-menu-items/
+ main-page.navigation-menu-item.ts # Sidebar entry for the welcome page
+ page-layouts/
+ main-page.page-layout.ts # Standalone page hosting the component
__tests__/
- setup-test.ts
- app-install.integration-test.ts
- .github/workflows/ci.yml # GitHub Actions
- public/ # Static assets
- vitest.config.ts # Test runner config
+ application-config.test.ts # Unit test
+ global-setup.ts # Integration test setup (sync + uninstall)
+ schema.integration-test.ts # Integration test against a live server
+ .github/workflows/
+ ci.yml # Lint, typecheck, unit + integration tests
+ cd.yml # Deploy + install on push to main
+ public/
+ logo.svg # Static assets
+ vitest.config.ts # Integration test runner config
+ vitest.unit.config.ts # Unit test runner config
tsconfig.json, tsconfig.spec.json
.nvmrc, .yarnrc.yml, .oxlintrc.json
- README.md, LLMS.md
+ README.md, AGENTS.md, CLAUDE.md
```
## File principali
-| File / Cartella | Scopo |
-| ---------------------------------------- | -------------------------------------------------------------------------------- |
-| `src/application-config.ts` | **Obbligatorio.** Il file di configurazione principale della tua app. |
-| `src/default-role.ts` | Ruolo predefinito che controlla a cosa possono accedere le tue funzioni logiche. |
-| `src/constants/universal-identifiers.ts` | UUID generati automaticamente e metadati (nome visualizzato, descrizione). |
-| `src/__tests__/` | Test di integrazione (setup + test di esempio). |
-| `public/` | Asset statici (immagini, font) serviti insieme alla tua app. |
+| File / Cartella | Scopo |
+| -------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
+| `src/application-config.ts` | **Obbligatorio.** Il file di configurazione principale della tua app. |
+| `src/default-role.ts` | Ruolo predefinito che controlla a cosa possono accedere le tue funzioni logiche. |
+| `src/constants/universal-identifiers.ts` | UUID generati automaticamente e metadati (nome visualizzato, descrizione). |
+| `src/front-components/`, `src/navigation-menu-items/`, `src/page-layouts/` | Una pagina di benvenuto iniziale: un front component eseguito da un page layout autonomo, raggiungibile dalla sidebar. |
+| `src/__tests__/` | Un test unitario più un test di integrazione (con il relativo setup globale) che sincronizza l'app con un server reale. |
+| `public/` | Asset statici (immagini, font) serviti insieme alla tua app. |
+| `AGENTS.md` / `CLAUDE.md` | Linee guida per gli agenti di codice AI che lavorano sull'app. |
**L'organizzazione dei file dipende da te.** Le cartelle sopra sono convenzioni — l'SDK rileva le entità tramite analisi AST sulle chiamate a `export default defineEntity(...)` indipendentemente da dove si trova il file.
@@ -47,15 +60,18 @@ Entrambi i pacchetti Twenty SDK devono essere inseriti sotto `devDependencies`,
{
"dependencies": {},
"devDependencies": {
- "twenty-client-sdk": "^2.13.0",
- "twenty-sdk": "^2.13.0"
+ "twenty-client-sdk": "2.20.0",
+ "twenty-sdk": "2.20.0",
+ "twenty-ui": "1.0.0-alpha.1"
}
}
```
+Lo scaffolder blocca `twenty-sdk` e `twenty-client-sdk` alla propria versione — mantieni i due allineati durante l'aggiornamento.
+
* **`twenty-sdk`** fornisce la CLI `twenty` e gli strumenti di build/scaffolding. Viene eseguito solo in fase di sviluppo e di build e non viene mai importato dal runtime dell'app pubblicata.
* **`twenty-client-sdk`** *viene* importato dal codice della tua app (`CoreApiClient`, `MetadataApiClient`, `RestApiClient`), ma Twenty lo fornisce a runtime: le funzioni di logica lo ricevono da un layer SDK generato e i componenti di front-end lo risolvono da moduli forniti dal server. La copia installata viene utilizzata solo per il type checking e per la build al momento del deploy, quindi non è mai necessario includerla nel bundle distribuito.
-Mantenere uno qualsiasi dei pacchetti sotto `dependencies` lo inserisce nel bundle di runtime dell'app installata, dove rappresenta solo zavorra. `twenty build` emette un avviso quando uno dei due è ancora elencato sotto `dependencies`.
+Mantenere uno qualsiasi dei pacchetti sotto `dependencies` lo inserisce nel bundle di runtime dell'app installata, dove rappresenta solo zavorra. `twenty dev:build` emette un avviso quando uno dei due è ancora elencato sotto `dependencies`.
Aggiungi come di consueto le dipendenze di runtime proprie della tua app (librerie che le tue funzioni di logica importano effettivamente a runtime) sotto `dependencies`.
diff --git a/packages/twenty-docs/l/it/developers/extend/apps/getting-started/quick-start.mdx b/packages/twenty-docs/l/it/developers/extend/apps/getting-started/quick-start.mdx
index 7b0697aaa2..2134cc1e71 100644
--- a/packages/twenty-docs/l/it/developers/extend/apps/getting-started/quick-start.mdx
+++ b/packages/twenty-docs/l/it/developers/extend/apps/getting-started/quick-start.mdx
@@ -6,17 +6,17 @@ description: Crea la tua prima app Twenty in pochi minuti.
## Prerequisiti
-* **Node.js 24+** — [Scarica](https://nodejs.org/)
+* **Node.js 24.5+** — [Scarica](https://nodejs.org/)
* **Yarn 4** — incluso con Node.js tramite Corepack. Abilitalo: `corepack enable`
* **Docker** — [Scarica](https://www.docker.com/products/docker-desktop/). Necessario per eseguire un server Twenty locale. Salta se hai già Twenty in esecuzione altrove.
La creazione di un'app Twenty ha tre fasi. Lo strumento di scaffolding le combina in un unico comando per il percorso ottimale, ma ogni fase è un concetto distinto — quando qualcosa fallisce, sapere in quale fase ti trovi indica cosa correggere.
-| Fase | Cosa fai | Strumento | Risultato |
-| ----------------------- | ------------------------------------------------------ | ----------------------------- | ---------------------------------- |
-| **1. Crea struttura** | Genera il codice sorgente dell'app | `npx create-twenty-app` | Un progetto TypeScript sul disco |
-| **2. Esegui un server** | Avvia un server Twenty con cui sincronizzare | Docker + `yarn twenty server` | Un'istanza Twenty in esecuzione |
-| **3. Sincronizza** | Sincronizza in tempo reale il tuo codice con il server | `yarn twenty dev` | Le tue modifiche compaiono nell'UI |
+| Fase | Cosa fai | Strumento | Risultato |
+| ----------------------- | ------------------------------------------------------ | ----------------------------------- | ---------------------------------- |
+| **1. Crea struttura** | Genera il codice sorgente dell'app | `npx create-twenty-app` | Un progetto TypeScript sul disco |
+| **2. Esegui un server** | Avvia un server Twenty con cui sincronizzare | Docker + `yarn twenty docker:start` | Un'istanza Twenty in esecuzione |
+| **3. Sincronizza** | Sincronizza in tempo reale il tuo codice con il server | `yarn twenty dev` | Le tue modifiche compaiono nell'UI |
---
@@ -28,7 +28,7 @@ Crea una nuova app dal modello:
npx create-twenty-app@latest my-twenty-app
```
-Ti verrà chiesto un nome e una descrizione — premi **Invio** per usare i valori predefiniti. Questo genera un progetto TypeScript in `my-twenty-app/` con un `application-config.ts` iniziale, un ruolo predefinito, un workflow CI e un test di integrazione.
+Lo scaffolder è non interattivo: il nome della directory diventa il nome dell'app. Passa `--display-name` e `--description` per personalizzare i metadati generati (puoi anche modificarli in seguito in `src/constants/universal-identifiers.ts`). Questo genera un progetto TypeScript in `my-twenty-app/` con un `application-config.ts` iniziale, un ruolo predefinito, workflow CI/CD e un test di integrazione.
**Dopo questa fase:** hai il codice sorgente dell'app sulla tua macchina. Non è ancora in esecuzione — questa è la Fase 2.
@@ -38,28 +38,14 @@ Ti verrà chiesto un nome e una descrizione — premi **Invio** per usare i valo
La tua app ha bisogno di un server Twenty con cui sincronizzarsi. Il server è un'istanza Twenty completa — UI, API GraphQL, PostgreSQL — in esecuzione in locale su Docker. Il tuo codice locale carica le sue definizioni su quel server, che le rende visibili nell'UI.
-Lo strumento di scaffolding ti propone di avviarne uno per te:
+Lo scaffolder avvia un'istanza per te: con Docker in esecuzione, scarica l'immagine `twentycrm/twenty-app-dev`, la avvia sulla porta `2020` e autentica la CLI sullo spazio di lavoro demo prepopolato (`tim@apple.dev`) — non è necessario effettuare l'accesso.
-> **Vuoi configurare un'istanza locale di Twenty?**
-
-* **Sì (consigliato)** — scarica l'immagine Docker `twentycrm/twenty-app-dev` e la avvia sulla porta `2020`. Assicurati prima che Docker sia in esecuzione.
-* **No** — scegli questa opzione se hai già un server Twenty a cui vuoi connetterti. Puoi collegarlo in seguito con `yarn twenty remote:add`.
-
-
-

-
-
-Quando il server è attivo, si apre il browser per l'accesso. Usa l'account demo preconfigurato:
-
-* **Email:** `tim@apple.dev`
-* **Password:** `tim@apple.dev`
+Per connetterti invece a un server Twenty esistente, passa `--url \`. I server remoti eseguono l'autenticazione con OAuth: si apre un browser così puoi effettuare l'accesso e fare clic su **Authorize**, concedendo alla CLI l'accesso al tuo spazio di lavoro. (Puoi anche scegliere di utilizzare OAuth in locale con `--authentication-method oauth` — accedi con `tim@apple.dev` / `tim@apple.dev`.)
-Fai clic su **Authorize** nella schermata successiva — questo concede alla CLI l'accesso al tuo spazio di lavoro.
-
@@ -117,28 +103,32 @@ Fai clic su **View installed app** per vedere l'installazione nello spazio di la
### Sincronizzazione una tantum per CI e script
-Passa `--once` per eseguire una singola build + sincronizzazione ed uscire — stessa pipeline, nessun watcher:
+Usa `plan` e `apply` per eseguire la stessa pipeline una volta, senza watcher:
```bash filename="Terminal"
-yarn twenty dev --once
+yarn twenty plan # preview the metadata changes without applying them
+yarn twenty apply # show the plan, then apply it
```
-| Comando | Comportamento | Quando usarlo |
-| ---------------------------------- | ---------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
-| `yarn twenty dev` | Monitora e risincronizza a ogni modifica. Rimane in esecuzione finché non lo interrompi. | Sviluppo locale interattivo. |
-| `yarn twenty dev --once` | Singola build + sincronizzazione, termina con codice `0` in caso di successo, `1` in caso di errore. | CI, hook pre-commit, agenti IA, flussi di lavoro scriptati. |
-| `yarn twenty dev --once --dry-run` | Crea e stampa le modifiche ai metadati **senza applicarle**. | Ispezionare quali modifiche verrebbero apportate da una sincronizzazione prima di confermarla. |
+| Comando | Comportamento | Quando usarlo |
+| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
+| `yarn twenty dev` | Monitora e risincronizza a ogni modifica. Rimane in esecuzione finché non lo interrompi. | Sviluppo locale interattivo. |
+| `yarn twenty apply` | Singola build + sincronizzazione, termina con codice `0` in caso di successo, `1` in caso di errore. Richiede conferma per le modifiche distruttive (passa `--force` per saltarla). | CI, hook pre-commit, agenti IA, flussi di lavoro scriptati. |
+| `yarn twenty plan` | Crea e stampa le modifiche ai metadati **senza applicarle**. | Ispezionare quali modifiche verrebbero apportate da una sincronizzazione prima di confermarla. |
-Entrambe le modalità richiedono un remoto autenticato. Vedi [Sincronizzazione e ripristino](/l/it/developers/extend/apps/operations/sync-and-recovery#previewing-changes-dry-run) per maggiori informazioni su `--dry-run`.
+Tutte le modalità richiedono un remoto autenticato. Vedi [Sincronizzazione e ripristino](/l/it/developers/extend/apps/operations/sync-and-recovery#previewing-changes-plan) per maggiori informazioni su `plan`.
+
+
+`yarn twenty dev --once` e `yarn twenty dev --once --dry-run` sono alias deprecati di `yarn twenty apply` e `yarn twenty plan`.
+
### Opzioni della modalità di sviluppo
-| Opzione | Descrizione |
-| ------------------------------------- | -------------------------------------------------------------------------------------------------- |
-| `--once` | Esegui una build e una sincronizzazione una sola volta, quindi esci. |
-| `--dry-run` | Con `--once`, visualizza in anteprima le modifiche ai metadati senza applicarle. Non scrive nulla. |
-| `--debounceMs \` | Imposta il ritardo di debounce delle modifiche ai file in millisecondi (predefinito: `2000`). |
-| `--verbose` / `--debug` | Mostra log di build dettagliati, richieste di sincronizzazione e tracce di errore. |
+| Opzione | Descrizione |
+| ------------------------------------- | --------------------------------------------------------------------------------------------- |
+| `--force` | Applica modifiche distruttive (eliminazioni) senza conferma. |
+| `--debounceMs \` | Imposta il ritardo di debounce delle modifiche ai file in millisecondi (predefinito: `1000`). |
+| `--verbose` / `--debug` | Mostra log di build dettagliati, richieste di sincronizzazione e tracce di errore. |
## Cosa puoi creare
diff --git a/packages/twenty-docs/l/it/developers/extend/apps/getting-started/scaffolding.mdx b/packages/twenty-docs/l/it/developers/extend/apps/getting-started/scaffolding.mdx
index 7ba9719e94..8374feb0ea 100644
--- a/packages/twenty-docs/l/it/developers/extend/apps/getting-started/scaffolding.mdx
+++ b/packages/twenty-docs/l/it/developers/extend/apps/getting-started/scaffolding.mdx
@@ -34,6 +34,10 @@ yarn twenty dev:add frontComponent
| Vista | `yarn twenty dev:add view` | `src/views/\.ts` |
| Voce del menu di navigazione | `yarn twenty dev:add navigationMenuItem` | `src/navigation-menu-items/\.ts` |
| Layout di pagina | `yarn twenty dev:add pageLayout` | `src/page-layouts/\.ts` |
+| Scheda layout di pagina | `yarn twenty dev:add pageLayoutTab` | `src/page-layout-tabs/\.ts` |
+| Voce del menu comandi | `yarn twenty dev:add commandMenuItem` | `src/command-menu-items/\.ts` |
+| Campo vista | `yarn twenty dev:add viewField` | `src/view-fields/\.ts` |
+| Provider di connessione | `yarn twenty dev:add connectionProvider` | `src/connection-providers/\.ts` |
## Cosa genera lo scaffolder
diff --git a/packages/twenty-docs/l/it/developers/extend/apps/getting-started/troubleshooting.mdx b/packages/twenty-docs/l/it/developers/extend/apps/getting-started/troubleshooting.mdx
index b8f92aff66..cb6948fcf0 100644
--- a/packages/twenty-docs/l/it/developers/extend/apps/getting-started/troubleshooting.mdx
+++ b/packages/twenty-docs/l/it/developers/extend/apps/getting-started/troubleshooting.mdx
@@ -5,10 +5,10 @@ icon: wrench
---
* **Errori di Docker** — Assicurati che Docker Desktop (o il demone) sia in esecuzione prima di `yarn twenty docker:start`. Il messaggio di errore mostrerà il comando di avvio corretto per il tuo sistema operativo.
-* **Versione di Node errata** — È necessaria la versione 24 o superiore. Verifica con `node -v`.
+* **Versione di Node errata** — Serve la 24.5+ (`engines.node: ^24.5.0`). Verifica con `node -v`.
* **Manca Yarn 4** — Esegui `corepack enable`.
* **Dipendenze danneggiate** — `rm -rf node_modules && yarn install`.
* **Errori di `twenty-sdk` dopo l'aggiornamento alla v2.8.0** — è stato spostato da `dependencies` a `devDependencies` nella v2.8.0. Vedi [Struttura del progetto → Dipendenze](/l/it/developers/extend/apps/getting-started/project-structure#dependencies).
-* **`twenty build` mostra un avviso su `twenty-client-sdk` sotto `dependencies`** — viene fornito in fase di esecuzione da Twenty, quindi dovrebbe essere spostato in `devDependencies` insieme a `twenty-sdk`. Vedi [Struttura del progetto → Dipendenze](/l/it/developers/extend/apps/getting-started/project-structure#dependencies).
+* **`twenty dev:build` mostra un avviso su `twenty-client-sdk` sotto `dependencies`** — viene fornito in fase di esecuzione da Twenty, quindi dovrebbe essere spostato in `devDependencies` insieme a `twenty-sdk`. Vedi [Struttura del progetto → Dipendenze](/l/it/developers/extend/apps/getting-started/project-structure#dependencies).
Bloccato? Chiedi aiuto su [Discord di Twenty](https://discord.com/channels/1130383047699738754/1130386664812982322).
diff --git a/packages/twenty-docs/l/it/developers/extend/apps/layout/command-menu-items.mdx b/packages/twenty-docs/l/it/developers/extend/apps/layout/command-menu-items.mdx
index fbb8abbd16..9d98beec4e 100644
--- a/packages/twenty-docs/l/it/developers/extend/apps/layout/command-menu-items.mdx
+++ b/packages/twenty-docs/l/it/developers/extend/apps/layout/command-menu-items.mdx
@@ -13,7 +13,6 @@ export default defineCommandMenuItem({
universalIdentifier: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
label: 'Open Dashboard',
shortLabel: 'Dashboard',
- icon: 'IconLayoutDashboard',
isPinned: true,
availabilityType: 'GLOBAL',
frontComponentUniversalIdentifier: '74c526eb-cb68-4cf7-b05c-0dd8c288d948',
@@ -22,51 +21,23 @@ export default defineCommandMenuItem({
## Campi di configurazione
-| Campo | Obbligatorio | Descrizione |
-| --------------------------------------- | ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| `universalIdentifier` | Sì | ID univoco stabile per il comando |
-| `label` | Sì | Etichetta completa mostrata nel menu comandi (Cmd+K) |
-| `frontComponentUniversalIdentifier` | Sì | L'`universalIdentifier` del componente front-end che questo comando apre |
-| `shortLabel` | No | Etichetta breve visualizzata sul pulsante di azione rapida fissato |
-| `icon` | No | Nome dell'icona visualizzato accanto all'etichetta (ad es. `'IconBolt'`, `'IconSend'`) |
-| `isPinned` | No | Quando `true`, mostra il comando come pulsante di azione rapida nell'angolo in alto a destra della pagina |
-| `availabilityType` | No | Controlla dove compare il comando: `'GLOBAL'` (sempre disponibile), `'RECORD_SELECTION'` (solo quando sono selezionati dei record) o `'FALLBACK'` (mostrato quando nessun altro comando corrisponde) |
-| `availabilityObjectUniversalIdentifier` | No | Limita il comando alle pagine di uno specifico tipo di oggetto (ad es. solo sui record Company) |
-| `conditionalAvailabilityExpression` | No | Un'espressione booleana che controlla dinamicamente la visibilità (vedi sotto) |
+| Campo | Obbligatorio | Descrizione |
+| --------------------------------------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `universalIdentifier` | Sì | ID univoco stabile per il comando |
+| `label` | Sì | Etichetta completa mostrata nel menu comandi (Cmd+K) |
+| `frontComponentUniversalIdentifier` | Sì | L'`universalIdentifier` del componente front-end che questo comando apre |
+| `shortLabel` | No | Etichetta breve visualizzata sul pulsante di azione rapida fissato |
+| `icon` | No | **Deprecato** — ignorato a favore dell'icona dell'applicazione; la build emette un avviso se impostato |
+| `isPinned` | No | Quando `true`, mostra il comando come pulsante di azione rapida nell'angolo in alto a destra della pagina |
+| `availabilityType` | No | Controlla dove compare il comando: `'GLOBAL'` (sempre disponibile), `'GLOBAL_OBJECT_CONTEXT'` (solo sulle pagine con un contesto oggetto — pagine indice e di record), `'RECORD_SELECTION'` (solo quando sono selezionati dei record) o `'FALLBACK'` (mostrato quando nessun altro comando corrisponde) |
+| `availabilityObjectUniversalIdentifier` | No | Limita il comando alle pagine di uno specifico tipo di oggetto (ad es. solo sui record Company) |
+| `conditionalAvailabilityExpression` | No | Un'espressione booleana che controlla dinamicamente la visibilità (vedi sotto) |
## Comandi headless
Un elemento del menu comandi abbinato a un [headless front component](/l/it/developers/extend/apps/layout/front-components#headless-vs-non-headless) è il modo idiomatico per distribuire un'azione con un clic: eseguire codice, navigare oppure confermare ed eseguire. La pagina Front Components tratta i [SDK Command components](/l/it/developers/extend/apps/layout/front-components#sdk-command-components) (`Command`, `CommandLink`, `CommandModal`, `CommandOpenSidePanelPage`) che gestiscono il pattern di action-and-unmount.
-Un flusso tipico:
-
-```tsx src/front-components/run-action.tsx
-import { defineFrontComponent } from 'twenty-sdk/define';
-import { Command } from 'twenty-sdk/command';
-import { CoreApiClient } from 'twenty-sdk/clients';
-
-const RunAction = () => {
- const execute = async () => {
- const client = new CoreApiClient();
- await client.mutation({
- createTask: {
- __args: { data: { title: 'Created by my app' } },
- id: true,
- },
- });
- };
-
- return ;
-};
-
-export default defineFrontComponent({
- universalIdentifier: 'e5f6a7b8-c9d0-1234-efab-345678901234',
- name: 'run-action',
- description: 'Creates a task from the command menu',
- component: RunAction,
- isHeadless: true,
-});
-```
+Un flusso tipico: un componente headless renderizza `` (vedi l'[esempio completo](/l/it/developers/extend/apps/layout/front-components#sdk-command-components)), e la voce di menu del comando lo punta:
```ts src/command-menu-items/run-action.command-menu-item.ts
import { defineCommandMenuItem } from 'twenty-sdk/define';
@@ -74,7 +45,6 @@ import { defineCommandMenuItem } from 'twenty-sdk/define';
export default defineCommandMenuItem({
universalIdentifier: 'f6a7b8c9-d0e1-2345-fabc-456789012345',
label: 'Run my action',
- icon: 'IconPlayerPlay',
frontComponentUniversalIdentifier: 'e5f6a7b8-c9d0-1234-efab-345678901234',
});
```
diff --git a/packages/twenty-docs/l/it/developers/extend/apps/layout/front-components.mdx b/packages/twenty-docs/l/it/developers/extend/apps/layout/front-components.mdx
index bea1006ba9..8e0a9d9853 100644
--- a/packages/twenty-docs/l/it/developers/extend/apps/layout/front-components.mdx
+++ b/packages/twenty-docs/l/it/developers/extend/apps/layout/front-components.mdx
@@ -49,14 +49,13 @@ export default defineCommandMenuItem({
universalIdentifier: 'd4e5f6a7-b8c9-0123-defa-456789012345',
shortLabel: 'Hello',
label: 'Hello World',
- icon: 'IconBolt',
isPinned: true,
availabilityType: 'GLOBAL',
frontComponentUniversalIdentifier: '74c526eb-cb68-4cf7-b05c-0dd8c288d948',
});
```
-Dopo la sincronizzazione con `yarn twenty dev` (o eseguendo una volta sola `yarn twenty dev --once`), l'azione rapida appare nell'angolo in alto a destra della pagina:
+Dopo la sincronizzazione con `yarn twenty dev` (o eseguendo una volta sola `yarn twenty apply`), l'azione rapida appare nell'angolo in alto a destra della pagina:

@@ -88,11 +87,11 @@ I componenti front-end prevedono due modalità di rendering controllate dall'opz
```tsx src/front-components/sync-tracker.tsx
import { defineFrontComponent } from 'twenty-sdk/define';
-import { useRecordId, enqueueSnackbar } from 'twenty-sdk/front-component';
+import { useSelectedRecordIds, enqueueSnackbar } from 'twenty-sdk/front-component';
import { useEffect } from 'react';
const SyncTracker = () => {
- const recordId = useRecordId();
+ const [recordId] = useSelectedRecordIds();
useEffect(() => {
enqueueSnackbar({ message: `Tracking record ${recordId}`, variant: 'info' });
@@ -116,7 +115,7 @@ Poiché il componente restituisce `null`, Twenty evita di renderizzare un conten
Il pacchetto `twenty-sdk` fornisce quattro componenti di supporto Command progettati per i componenti front-end headless. Ogni componente esegue un'azione al montaggio, gestisce gli errori mostrando una notifica snackbar e smonta automaticamente il componente front-end al termine.
-Importali da `twenty-sdk/command`:
+Importali da `twenty-sdk/front-component`:
* **`Command`** — Esegue una callback asincrona tramite la prop `execute`.
* **`CommandLink`** — Naviga verso un percorso dell'app. Props: `to`, `params`, `queryParams`, `options`.
@@ -127,8 +126,8 @@ Ecco un esempio completo di componente front-end headless che usa `Command` per
```tsx src/front-components/run-action.tsx
import { defineFrontComponent } from 'twenty-sdk/define';
-import { Command } from 'twenty-sdk/command';
-import { CoreApiClient } from 'twenty-sdk/clients';
+import { Command } from 'twenty-sdk/front-component';
+import { CoreApiClient } from 'twenty-client-sdk/core';
const RunAction = () => {
const execute = async () => {
@@ -160,7 +159,6 @@ import { defineCommandMenuItem } from 'twenty-sdk/define';
export default defineCommandMenuItem({
universalIdentifier: 'f6a7b8c9-d0e1-2345-fabc-456789012345',
label: 'Run my action',
- icon: 'IconPlayerPlay',
frontComponentUniversalIdentifier: 'e5f6a7b8-c9d0-1234-efab-345678901234',
});
```
@@ -169,7 +167,7 @@ E un esempio che usa `CommandModal` per chiedere conferma prima di eseguire:
```tsx src/front-components/delete-draft.tsx
import { defineFrontComponent } from 'twenty-sdk/define';
-import { CommandModal } from 'twenty-sdk/command';
+import { CommandModal } from 'twenty-sdk/front-component';
const DeleteDraft = () => {
const execute = async () => {
@@ -202,7 +200,7 @@ I componenti front vengono eseguiti lato browser in un Web Worker in sandbox, me
Una funzione logica dichiarata con `httpRouteTriggerSettings` è raggiungibile tramite HTTP al relativo percorso della route. Twenty inietta nel worker l'URL di base da cui vengono servite le tue funzioni come `TWENTY_FUNCTIONS_URL`, insieme al `TWENTY_APP_ACCESS_TOKEN` che autentica la chiamata. Non esiste ancora un client SDK dedicato per invocare le proprie funzioni, quindi chiamale con un semplice `fetch`:
-> **Su Twenty Cloud, le funzioni logiche attivate tramite HTTP sono servite su un dominio dedicato per ogni workspace** in `https://\
.twenty.com\` — questo è esattamente ciò in cui viene risolto `TWENTY_FUNCTIONS_URL`. Per i chiamanti esterni, copia l’URL esatto dalle impostazioni del **trigger HTTP** della funzione o dalla scheda **Settings** dell’applicazione.
+> **Su Twenty Cloud, le funzioni logiche attivate tramite HTTP sono servite su un dominio dedicato per ogni workspace** in `https://\.withtwenty.com\` — questo è esattamente ciò in cui viene risolto `TWENTY_FUNCTIONS_URL`. Per i chiamanti esterni, copia l’URL esatto dalle impostazioni del **trigger HTTP** della funzione o dalla scheda **Settings** dell’applicazione.
La route legacy della funzione `/s/` è **deprecata** e sarà **disattivata il 2026-07-24**. Usa invece `TWENTY_FUNCTIONS_URL` (sopra) e migra tutti gli URL `/s/` hard-coded prima di quella data. La route `/s/` rimane disponibile per il self-hosting.
@@ -212,7 +210,7 @@ Un front component headless può eseguire la chiamata al mount tramite il compon
```tsx src/front-components/sync-prs.tsx
import { defineFrontComponent } from 'twenty-sdk/define';
-import { Command } from 'twenty-sdk/command';
+import { Command } from 'twenty-sdk/front-component';
const SyncPrs = () => {
const execute = async () => {
@@ -316,13 +314,13 @@ All'interno del tuo componente, usa gli hook dell'SDK per accedere all'utente co
import { defineFrontComponent } from 'twenty-sdk/define';
import {
useUserId,
- useRecordId,
+ useSelectedRecordIds,
useFrontComponentId,
} from 'twenty-sdk/front-component';
const RecordInfo = () => {
const userId = useUserId();
- const recordId = useRecordId();
+ const [recordId] = useSelectedRecordIds();
const componentId = useFrontComponentId();
return (
@@ -405,12 +403,11 @@ Ecco un esempio che usa l'API host per mostrare una snackbar e chiudere il panne
```tsx src/front-components/archive-record.tsx
import { defineFrontComponent } from 'twenty-sdk/define';
-import { useRecordId } from 'twenty-sdk/front-component';
-import { enqueueSnackbar, closeSidePanel } from 'twenty-sdk/front-component';
-import { CoreApiClient } from 'twenty-sdk/clients';
+import { enqueueSnackbar, closeSidePanel, useSelectedRecordIds } from 'twenty-sdk/front-component';
+import { CoreApiClient } from 'twenty-client-sdk/core';
const ArchiveRecord = () => {
- const recordId = useRecordId();
+ const [recordId] = useSelectedRecordIds();
const handleArchive = async () => {
const client = new CoreApiClient();
@@ -451,10 +448,10 @@ export default defineFrontComponent({
Usa `useSelectedRecordIds()` per gestire più record selezionati. Questo è utile per operazioni in blocco:
```tsx src/front-components/bulk-export.tsx
-import { defineFrontComponent, numberOfSelectedRecords } from 'twenty-sdk/define';
+import { defineFrontComponent } from 'twenty-sdk/define';
import { useSelectedRecordIds } from 'twenty-sdk/front-component';
import { enqueueSnackbar, closeSidePanel } from 'twenty-sdk/front-component';
-import { CoreApiClient } from 'twenty-sdk/clients';
+import { CoreApiClient } from 'twenty-client-sdk/core';
const BulkExport = () => {
const selectedRecordIds = useSelectedRecordIds();
@@ -492,12 +489,19 @@ export default defineFrontComponent({
name: 'bulk-export',
description: 'Export selected records',
component: BulkExport,
- command: {
- universalIdentifier: 'd0e1f2a3-b4c5-6789-defa-012345678902',
- label: 'Bulk Export',
- availabilityType: 'RECORD_SELECTION',
- conditionalAvailabilityExpression: numberOfSelectedRecords > 0,
- },
+});
+```
+
+Mostralo con una [voce del menu dei comandi](/l/it/developers/extend/apps/layout/command-menu-items) limitata alle selezioni di record:
+
+```ts src/command-menu-items/bulk-export.command-menu-item.ts
+import { defineCommandMenuItem } from 'twenty-sdk/define';
+
+export default defineCommandMenuItem({
+ universalIdentifier: 'd0e1f2a3-b4c5-6789-defa-012345678902',
+ label: 'Bulk Export',
+ availabilityType: 'RECORD_SELECTION',
+ frontComponentUniversalIdentifier: 'd0e1f2a3-b4c5-6789-defa-012345678901',
});
```
diff --git a/packages/twenty-docs/l/it/developers/extend/apps/layout/navigation-menu-items.mdx b/packages/twenty-docs/l/it/developers/extend/apps/layout/navigation-menu-items.mdx
index 1d5217f8d3..8caa64f084 100644
--- a/packages/twenty-docs/l/it/developers/extend/apps/layout/navigation-menu-items.mdx
+++ b/packages/twenty-docs/l/it/developers/extend/apps/layout/navigation-menu-items.mdx
@@ -35,6 +35,8 @@ export default defineNavigationMenuItem({
* `position` controlla l’ordinamento nella barra laterale.
+* L'enum contiene anche `NavigationMenuItemType.RECORD`, utilizzato internamente per i record preferiti creati dall'utente — non è utilizzabile da un app manifest (non esiste alcun campo per fare riferimento a un record).
+
* `icon` e `color` sono opzionali e personalizzano l’aspetto della voce.
* `folderUniversalIdentifier` è inoltre disponibile su qualsiasi elemento per annidarlo all’interno di un genitore di tipo `FOLDER`.
diff --git a/packages/twenty-docs/l/it/developers/extend/apps/layout/views.mdx b/packages/twenty-docs/l/it/developers/extend/apps/layout/views.mdx
index 7186686190..29f34ab717 100644
--- a/packages/twenty-docs/l/it/developers/extend/apps/layout/views.mdx
+++ b/packages/twenty-docs/l/it/developers/extend/apps/layout/views.mdx
@@ -33,17 +33,32 @@ export default defineView({
## Punti chiave
* `objectUniversalIdentifier` specifica a quale oggetto si applica questa vista. Può essere un oggetto personalizzato che hai definito o un oggetto Twenty standard.
-* `key` determina il tipo di vista — `ViewKey.INDEX` è la vista elenco principale per l'oggetto.
+* `key: ViewKey.INDEX` contrassegna la vista come vista elenco principale dell'oggetto (quella che un elemento di navigazione `OBJECT` apre).
* `fields` controlla quali colonne compaiono e in quale ordine. Ogni campo fa riferimento a un `fieldMetadataUniversalIdentifier`.
-* Puoi anche definire `filters`, `filterGroups`, `groups` e `fieldGroups` per configurazioni più avanzate.
+* Puoi anche definire `filters`, `filterGroups`, `sorts`, `groups` e `fieldGroups` per configurazioni più avanzate.
* `position` controlla l'ordinamento quando esistono più viste per lo stesso oggetto.
+## Proprietà opzionali
+
+| Proprietà | Valori | Descrizione |
+| ----------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
+| `type` | `ViewType.TABLE` (predefinito), `ViewType.KANBAN`, `ViewType.CALENDAR` | Come sono disposti i record. (`FIELDS_WIDGET` / `TABLE_WIDGET` esistono anche ma sono usati internamente dai widget di layout di pagina.) |
+| `visibility` | `ViewVisibility.WORKSPACE` (predefinito), `ViewVisibility.UNLISTED` | Se la vista è elencata per l'intero workspace o nascosta dai selettori. |
+| `openRecordIn` | `ViewOpenRecordIn.SIDE_PANEL` (predefinito), `ViewOpenRecordIn.RECORD_PAGE` | Dove l'apertura di un record con un clic lo visualizza. |
+| `sorts` | `{ fieldMetadataUniversalIdentifier, direction: ViewSortDirection.ASC \| DESC }[]` | Ordine di ordinamento predefinito. |
+| `isCompact` | `boolean` | Visualizzazione compatta delle righe. |
+| `mainGroupByFieldMetadataUniversalIdentifier` + `shouldHideEmptyGroups` | — | Raggruppa i record (ad es. colonne kanban) per un campo. |
+| `kanbanAggregateOperation`, `kanbanAggregateOperationFieldMetadataUniversalIdentifier`, `kanbanColumnWidth` | `AggregateOperations.*` | Aggregazioni e dimensionamento delle colonne kanban. |
+| `calendarLayout`, `calendarFieldMetadataUniversalIdentifier` | `ViewCalendarLayout.DAY` / `WEEK` / `MONTH` | Viste calendario: layout e campo data che posiziona i record. |
+
+Tutti gli enum sopra sono esportati da `twenty-sdk/define`.
+
## Filtri
Una vista può essere fornita con filtri preapplicati. Ogni filtro ha tre coordinate: il **campo** che viene filtrato, l'**operando** (come confrontare) e il **valore** (con cosa confrontare). Tutti e tre devono allinearsi: l'uso di un operando che non si applica a un tipo di campo verrà rifiutato al momento della sincronizzazione.
```ts
-import { ViewFilterOperand } from 'twenty-shared/types';
+import { ViewFilterOperand } from 'twenty-sdk/define';
filters: [
{
diff --git a/packages/twenty-docs/l/it/developers/extend/apps/logic/logic-functions.mdx b/packages/twenty-docs/l/it/developers/extend/apps/logic/logic-functions.mdx
index 2a3978d992..c2a2b214e3 100644
--- a/packages/twenty-docs/l/it/developers/extend/apps/logic/logic-functions.mdx
+++ b/packages/twenty-docs/l/it/developers/extend/apps/logic/logic-functions.mdx
@@ -51,8 +51,12 @@ export default defineLogicFunction({
```
Tipi di trigger disponibili:
-* **httpRoute**: Espone la tua funzione su un percorso e metodo HTTP **sotto l'endpoint `/s/`**:
-> ad es. `path: '/post-card/create'` è invocabile su `https://your-twenty-server.com/s/post-card/create`
+* **httpRoute**: Espone la tua funzione su un percorso HTTP e un metodo al **URL di base delle funzioni del tuo workspace** — il valore Twenty inietta come `TWENTY_FUNCTIONS_URL` (su Twenty Cloud, un dominio dedicato per workspace
+> ad es. `path: '/post-card/create'` è invocabile su `https://your-workspace.withtwenty.com/post-card/create`
+
+
+Il prefisso tradizionale `/s/` (`https://your-twenty-server.com/s/post-card/create`) è **deprecato su Twenty Cloud** e sarà disattivato il **2026-07-24**. Rimane disponibile per le istanze locali e self-hosted che non configurano un dominio di funzioni isolate — usa `TWENTY_FUNCTIONS_URL` quando è impostato, e torna a `\/s/\` altrimenti.
+
Per richiamare, da un componente front-end (headless), una funzione logica attivata da una rotta, vedi [Chiamare una funzione logica](/l/it/developers/extend/apps/layout/front-components#calling-a-logic-function).
diff --git a/packages/twenty-docs/l/it/developers/extend/apps/logic/overview.mdx b/packages/twenty-docs/l/it/developers/extend/apps/logic/overview.mdx
index 2e98239c92..0c7083af25 100644
--- a/packages/twenty-docs/l/it/developers/extend/apps/logic/overview.mdx
+++ b/packages/twenty-docs/l/it/developers/extend/apps/logic/overview.mdx
@@ -42,7 +42,7 @@ Una funzione logica sceglie uno o più trigger — ogni voce qui sotto è un cam
| Scatenante | Quando viene eseguito | Impostazione |
| ----------------------- | --------------------------------------------------------------------- | ------------------------------- |
-| **Route HTTP** | Una richiesta raggiunge il tuo endpoint `/s/\` | `httpRouteTriggerSettings` |
+| **Route HTTP** | Una richiesta colpisce l'URL pubblico della tua funzione | `httpRouteTriggerSettings` |
| **Cron** | Viene soddisfatta un'espressione CRON | `cronTriggerSettings` |
| **Evento database** | Un record dello spazio di lavoro viene creato, aggiornato o eliminato | `databaseEventTriggerSettings` |
| **Strumento AI** | Una funzionalità AI di Twenty decide di chiamare la tua funzione | `toolTriggerSettings` |
diff --git a/packages/twenty-docs/l/it/developers/extend/apps/operations/cli.mdx b/packages/twenty-docs/l/it/developers/extend/apps/operations/cli.mdx
index 80f4c74647..e9d93acfd6 100644
--- a/packages/twenty-docs/l/it/developers/extend/apps/operations/cli.mdx
+++ b/packages/twenty-docs/l/it/developers/extend/apps/operations/cli.mdx
@@ -4,7 +4,25 @@ description: Comandi di `yarn twenty` per eseguire funzioni, eseguire lo streami
icon: terminal
---
-Oltre a `dev`, `dev:build`, `dev:add` e `dev:typecheck`, la CLI `yarn twenty` fornisce comandi per eseguire funzioni, visualizzare i log e gestire le installazioni delle app.
+La CLI `yarn twenty` è la tua interfaccia per tutto ciò che riguarda le app. Elenco completo dei comandi:
+
+| Comando | Cosa fa | Documentato in |
+| ----------------------------------------------- | ----------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- |
+| `dev` | Monitora i file sorgente e sincronizza in tempo reale le modifiche | [Guida rapida](/l/it/developers/extend/apps/getting-started/quick-start) |
+| `piano` | Visualizza in anteprima le modifiche ai metadati senza applicarle | [Sincronizzazione e ripristino](/l/it/developers/extend/apps/operations/sync-and-recovery#previewing-changes-plan) |
+| `applica` | Applica le modifiche ai metadati dopo aver mostrato il piano | [Sincronizzazione e ripristino](/l/it/developers/extend/apps/operations/sync-and-recovery) |
+| `dev:build` | Compila l'app e genera il client API (`--tarball` per creare un `.tgz`) | [Pubblicazione](/l/it/developers/extend/apps/operations/publishing) |
+| `dev:typecheck` | Esegui il controllo dei tipi TypeScript | [Test](/l/it/developers/extend/apps/operations/testing) |
+| Crea l'impalcatura per una nuova entità | Crea l'impalcatura per una nuova entità | [Scaffolding](/l/it/developers/extend/apps/getting-started/scaffolding) |
+| `dev:generate-client` | Rigenera il client API tipizzato | questa pagina |
+| `dev:function:exec` / `dev:function:logs` | Esegui le funzioni e trasmetti in streaming i relativi log | questa pagina |
+| `dev:translations-extract` | Estrai le stringhe traducibili nei cataloghi in `locales/` | [Traduzioni](/l/it/developers/extend/apps/translations/overview) |
+| `dev:catalog-sync` | Attiva la sincronizzazione del catalogo del marketplace | [Pubblicazione](/l/it/developers/extend/apps/operations/publishing#how-marketplace-discovery-works) |
+| `app:publish` / `app:install` / `app:uninstall` | Ciclo di vita delle release | [Pubblicazione](/l/it/developers/extend/apps/operations/publishing) e questa pagina |
+| `docker:*` | Gestisci il container del server Twenty locale | [Server locale](/l/it/developers/extend/apps/getting-started/local-server) |
+| `remote:*` | Gestisci le connessioni al server | questa pagina |
+
+Ogni comando accetta `-r, --remote \` per indirizzarsi a un remote specifico invece di quello predefinito.
## Esecuzione delle funzioni (`yarn twenty dev:function:exec`)
@@ -20,8 +38,9 @@ yarn twenty dev:function:exec -u e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf
# Pass a JSON payload
yarn twenty dev:function:exec -n create-new-post-card -p '{"name": "Hello"}'
-# Execute the post-install function
+# Execute the install hooks
yarn twenty dev:function:exec --postInstall
+yarn twenty dev:function:exec --preInstall
```
## Visualizzazione dei log delle funzioni (`yarn twenty dev:function:logs`)
@@ -100,6 +119,12 @@ yarn twenty remote:list
# Set the active remote
yarn twenty remote:use
+
+# Check that the active remote's authentication is still valid
+yarn twenty remote:status
+
+# Remove a remote
+yarn twenty remote:remove
```
Le tue credenziali sono archiviate in `~/.twenty/config.json`.
diff --git a/packages/twenty-docs/l/it/developers/extend/apps/operations/publishing.mdx b/packages/twenty-docs/l/it/developers/extend/apps/operations/publishing.mdx
index a7242da04a..c09d9bf218 100644
--- a/packages/twenty-docs/l/it/developers/extend/apps/operations/publishing.mdx
+++ b/packages/twenty-docs/l/it/developers/extend/apps/operations/publishing.mdx
@@ -229,7 +229,7 @@ yarn twenty dev:catalog-sync
# yarn twenty dev:catalog-sync --remote production
```
-I metadati visualizzati nel marketplace provengono dalla configurazione `defineApplication()` — campi come `displayName`, `description`, `author`, `category`, `logoUrl`, `screenshots`, `aboutDescription`, `websiteUrl` e `termsUrl`.
+I metadati mostrati nel marketplace provengono dalla configurazione di `defineApplication()` — vedi [Metadati del marketplace](#marketplace-metadata) sopra.
Se la tua app non definisce un `aboutDescription` in `defineApplication()`, il marketplace userà automaticamente il `README.md` del tuo pacchetto su npm come contenuto della pagina Informazioni. Questo significa che puoi mantenere un unico README sia per npm sia per il marketplace di Twenty. Se desideri una descrizione diversa nel marketplace, imposta esplicitamente `aboutDescription`.
diff --git a/packages/twenty-docs/l/it/developers/extend/apps/operations/sync-and-recovery.mdx b/packages/twenty-docs/l/it/developers/extend/apps/operations/sync-and-recovery.mdx
index 5079d0e28e..f75f2418e3 100644
--- a/packages/twenty-docs/l/it/developers/extend/apps/operations/sync-and-recovery.mdx
+++ b/packages/twenty-docs/l/it/developers/extend/apps/operations/sync-and-recovery.mdx
@@ -15,33 +15,44 @@ Per l’iterazione locale quotidiana vuoi quasi sempre `yarn twenty dev`. Il dep
| Vuoi… | Comando | Note |
| ----------------------------------------------------------- | ----------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
| Iterare in locale con sincronizzazione in tempo reale | `yarn twenty dev` | Monitora i file e sincronizza a ogni modifica. |
-| Sincronizzare una volta e uscire (CI, script, hook) | `yarn twenty dev --once` | Esegue una build + sincronizzazione, poi termina. |
-| Visualizzare in anteprima le modifiche **senza applicarle** | `yarn twenty dev --once --dry-run` | Calcola e stampa il diff; non scrive nulla. |
+| Sincronizzare una volta e uscire (CI, script, hook) | `yarn twenty apply` | Esegue una build + sincronizzazione, poi termina. Aggiungi `--force` per saltare la conferma delle modifiche distruttive. |
+| Visualizzare in anteprima le modifiche **senza applicarle** | `yarn twenty plan` | Calcola e stampa il diff; non scrive nulla. |
| Rimuovere l'app dallo spazio di lavoro | `yarn twenty app:uninstall` | Aggiungi `--yes` per saltare il prompt. |
| Inviare un tarball a un server | `yarn twenty app:publish --private` | Richiede una versione di `package.json` **strettamente superiore** — vedi [Publishing](/l/it/developers/extend/apps/operations/publishing). |
| Pubblicare nel marketplace (npm) | `yarn twenty app:publish` | — |
| Installare / aggiornare una versione distribuita | `yarn twenty app:install` | Installa la versione attualmente distribuita. |
| Pulire il server locale e ripartire da zero | `yarn twenty docker:reset` | Elimina **tutti** i dati locali — ultima risorsa. |
+
+`yarn twenty dev --once` e `yarn twenty dev --once --dry-run` funzionano ancora come alias deprecati per `yarn twenty apply` e `yarn twenty plan`.
+
+
### La sincronizzazione locale non richiede un incremento di versione
La regola della `version` strettamente crescente (`VERSION_ALREADY_EXISTS` in fase di deploy, `APP_ALREADY_INSTALLED` / `CANNOT_DOWNGRADE_APPLICATION` in fase di installazione) si applica a **`app:publish` / `app:install`** — il percorso di release. `yarn twenty dev` sincronizza il tuo manifest in-place e non richiede mai una modifica di versione, quindi non devi toccare `package.json` per iterare. Se ti ritrovi ad aumentare la versione per testare una modifica locale, stai usando il percorso di release quando invece vuoi il ciclo di sviluppo.
## Lettura dell'output di sincronizzazione
-Ogni sincronizzazione stampa le modifiche ai metadati che ha applicato (o che applicherebbe, con `--dry-run`):
+Ogni sincronizzazione stampa le modifiche ai metadati che ha applicato (o che applicherebbe, con `plan`), in stile Terraform — un blocco per entità con i relativi attributi, quindi una riga di riepilogo:
```text filename="Terminal"
-Metadata changes: 2 created, 1 updated, 1 deleted
- created objectMetadata rocket
- created fieldMetadata timelineActivities
- updated fieldMetadata launchedAt
- deleted pageLayout legacyTab
-✓ Synced
+ # objectMetadata "rocket" will be created
+ + icon = "IconRocket"
+ + labelSingular = "Rocket"
+ + ...
+
+ # fieldMetadata "launchedAt" will be updated
+ ~ isNullable = false -> true
+
+Plan: 2 to add, 1 to change, 1 to destroy.
+
+✓ Synced My App (4 files)
```
Questo è il tuo primo strumento diagnostico: ti dice esattamente quali oggetti, campi e layout sono cambiati, così puoi confermare che una sincronizzazione ha fatto ciò che ti aspettavi prima di controllare l'interfaccia utente (UI).
+Le modifiche distruttive (`to destroy`) sono elencate con ciò che eliminano (ad es. `objectMetadata "auditNote" — drops the table and all its rows`) e richiedono una conferma interattiva, oppure `--force` negli script.
+
Quando una sincronizzazione fallisce su una singola entità, l'errore indica l'entità in questione e il suo `universalIdentifier`, per esempio:
```text
@@ -50,39 +61,42 @@ Migration action 'create' for 'fieldMetadata' (universalIdentifier: 2020...4337)
Usa quell'identificatore per trovare l'entità nel tuo manifest (e, se necessario, nello spazio di lavoro) invece di indovinare quale sia in conflitto.
-## Anteprima delle modifiche (dry run)
+## Anteprima delle modifiche (plan)
-`yarn twenty dev --once --dry-run` crea il tuo manifest, chiede al server il piano di migrazione e lo stampa — **senza applicare nulla**. È il modo sicuro per rispondere a "cosa cambierebbe questa sincronizzazione?" prima di impegnarti ad applicarla.
+`yarn twenty plan` crea il tuo manifest, chiede al server il piano di migrazione e lo stampa — **senza applicare nulla**. È il modo sicuro per rispondere a "cosa cambierebbe questa sincronizzazione?" prima di impegnarti ad applicarla.
```bash filename="Terminal"
-yarn twenty dev --once --dry-run
+yarn twenty plan
```
```text filename="Terminal"
Building manifest...
-Computing metadata diff (dry run, nothing will be applied)...
-Metadata changes: 1 created, 1 updated
- created fieldMetadata timelineActivities
- updated objectMetadata rocket
-✓ Dry run complete for My App — no changes were applied
+Computing metadata plan (read-only, nothing will be applied)...
+
+ # fieldMetadata "timelineActivities" will be created
+ + ...
+
+Plan: 1 to add, 1 to change, 0 to destroy.
+
+✓ Plan complete for My App — no changes were applied
```
-Un dry run:
+Un piano:
* **Non scrive nulla** — nessuna migrazione dei metadati, nessun aggiornamento del record dell'applicazione, nessuna modifica ai ruoli/schede predefiniti e nessuna generazione del client API.
* Restituisce lo **stesso diff** che una sincronizzazione reale applicherebbe, così puoi esaminare in anticipo le entità create/aggiornate/eliminate.
* È utile prima di una modifica rischiosa, quando si rivede una modifica generata da un'IA o in uno script che deve fallire se sta per essere applicata una modifica imprevista.
-Un dry run mostra in anteprima solo le modifiche ai **metadati** e richiede che l'app sia stata sincronizzata almeno una volta (così lo spazio di lavoro la conosce). Se lo esegui su un'app che non è mai stata sincronizzata, il server segnala che l'app non è installata — esegui prima `yarn twenty dev` una volta.
+Un piano mostra in anteprima solo le modifiche ai **metadati** e richiede che l'app sia stata sincronizzata almeno una volta (così lo spazio di lavoro la conosce). Se lo esegui su un'app che non è mai stata sincronizzata, il server segnala che l'app non è installata — esegui prima `yarn twenty dev` una volta.
## Scala di ripristino
Quando i metadati locali sembrano errati, procedi in quest'ordine e fermati non appena ti sblocchi. Ogni passaggio è più invasivo del precedente.
-1. **Nuova sincronizzazione.** Esegui di nuovo `yarn twenty dev --once`. Le sincronizzazioni sono idempotenti — rieseguire un manifest pulito è sicuro e spesso risolve un problema temporaneo.
-2. **Visualizza in anteprima il piano.** Esegui `yarn twenty dev --once --dry-run` per vedere esattamente cosa intende cambiare la prossima sincronizzazione, senza applicarlo.
+1. **Nuova sincronizzazione.** Esegui di nuovo `yarn twenty apply`. Le sincronizzazioni sono idempotenti — rieseguire un manifest pulito è sicuro e spesso risolve un problema temporaneo.
+2. **Visualizza in anteprima il piano.** Esegui `yarn twenty plan` per vedere esattamente cosa intende cambiare la prossima sincronizzazione, senza applicarlo.
3. **Leggi l'errore nominale.** Se una sincronizzazione fallisce, annota il tipo di metadato e lo `universalIdentifier` nel messaggio (vedi sopra) e individua quell'entità nel tuo manifest. Un conflitto di solito indica un identificatore duplicato o riutilizzato.
4. **Disinstalla e reinstalla.** `yarn twenty app:uninstall`, poi sincronizza di nuovo (`yarn twenty dev`). Questo ricostruisce i metadati dell'app partendo da zero, lasciando intatto il resto del tuo spazio di lavoro.
5. **Ripristino completo (ultima risorsa).** `yarn twenty docker:reset`, poi esegui di nuovo il seeding e la sincronizzazione.
diff --git a/packages/twenty-docs/l/it/developers/extend/apps/operations/testing.mdx b/packages/twenty-docs/l/it/developers/extend/apps/operations/testing.mdx
index 1a356646fa..95783016ad 100644
--- a/packages/twenty-docs/l/it/developers/extend/apps/operations/testing.mdx
+++ b/packages/twenty-docs/l/it/developers/extend/apps/operations/testing.mdx
@@ -78,6 +78,13 @@ Crea un `vitest.config.ts` alla radice della tua app:
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 ?? '';
+
+// 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({
@@ -88,66 +95,74 @@ export default defineConfig({
test: {
testTimeout: 120_000,
hookTimeout: 120_000,
+ fileParallelism: false,
include: ['src/**/*.integration-test.ts'],
- setupFiles: ['src/__tests__/setup-test.ts'],
+ globalSetup: ['src/__tests__/global-setup.ts'],
env: {
- TWENTY_API_URL: 'http://localhost:2020',
- TWENTY_API_KEY: 'your-api-key',
+ TWENTY_API_URL,
+ TWENTY_API_KEY,
},
},
});
```
-Crea un file di setup che verifichi che il server sia raggiungibile prima dell'esecuzione dei test:
+Crea un file di configurazione globale che verifichi che il server sia raggiungibile, scriva una configurazione di test per l'SDK (`~/.twenty/config.test.json`) e sincronizzi l'app prima dell'esecuzione dei test:
-```ts src/__tests__/setup-test.ts
+```ts src/__tests__/global-setup.ts
import * as fs from 'fs';
import * as os from 'os';
import * as path from 'path';
-import { beforeAll } from 'vitest';
-const TWENTY_API_URL = process.env.TWENTY_API_URL ?? 'http://localhost:2020';
-const TEST_CONFIG_DIR = path.join(os.tmpdir(), '.twenty-sdk-test');
+import { appDevOnce, appUninstall } from 'twenty-sdk/cli';
+
+const APP_PATH = process.cwd();
+const CONFIG_DIR = path.join(os.homedir(), '.twenty');
+
+export async function setup() {
+ const apiUrl = process.env.TWENTY_API_URL!;
+ const apiKey = process.env.TWENTY_API_KEY!;
-beforeAll(async () => {
// Verify the server is running
- const response = await fetch(`${TWENTY_API_URL}/healthz`);
-
+ const response = await fetch(`${apiUrl}/healthz`);
if (!response.ok) {
- throw new Error(
- `Twenty server is not reachable at ${TWENTY_API_URL}. ` +
- 'Start the server before running integration tests.',
- );
+ throw new Error(`Twenty server is not reachable at ${apiUrl}.`);
}
- // Write a temporary config for the SDK
- fs.mkdirSync(TEST_CONFIG_DIR, { recursive: true });
-
+ // Write the SDK's test config (the CLI reads config.test.json when NODE_ENV=test)
+ fs.mkdirSync(CONFIG_DIR, { recursive: true });
fs.writeFileSync(
- path.join(TEST_CONFIG_DIR, 'config.json'),
+ path.join(CONFIG_DIR, 'config.test.json'),
JSON.stringify({
- remotes: {
- local: {
- apiUrl: process.env.TWENTY_API_URL,
- apiKey: process.env.TWENTY_API_KEY,
- },
- },
+ remotes: { local: { apiUrl, apiKey } },
defaultRemote: 'local',
}, null, 2),
);
-});
+
+ // Start from a clean slate, then sync the app
+ await appUninstall({ appPath: APP_PATH }).catch(() => {});
+
+ const result = await appDevOnce({ appPath: APP_PATH });
+ if (!result.success) {
+ throw new Error(`Dev sync failed: ${result.error?.message}`);
+ }
+}
+
+export async function teardown() {
+ await appUninstall({ appPath: APP_PATH });
+}
```
## API programmatiche dell'SDK
Il sottopercorso `twenty-sdk/cli` esporta funzioni che puoi chiamare direttamente dal codice di test:
-| Funzione | Descrizione |
-| -------------- | ----------------------------------------------- |
-| `appBuild` | Compila l'app e, opzionalmente, crea un tarball |
-| `appDeploy` | Carica un tarball sul server |
-| `appInstall` | Installa l'app nello spazio di lavoro attivo |
-| `appUninstall` | Disinstalla l'app dallo spazio di lavoro attivo |
+| Funzione | Descrizione |
+| -------------- | ---------------------------------------------------------------- |
+| `appBuild` | Compila l'app e, opzionalmente, crea un tarball |
+| `appDeploy` | Carica un tarball sul server |
+| `appDevOnce` | Compila e sincronizza l'app una volta (come `yarn twenty apply`) |
+| `appInstall` | Installa l'app nello spazio di lavoro attivo |
+| `appUninstall` | Disinstalla l'app dallo spazio di lavoro attivo |
Ogni funzione restituisce un oggetto risultato con `success: boolean` e `data` oppure `error`.
@@ -238,64 +253,10 @@ Puoi anche eseguire il controllo dei tipi sulla tua app senza eseguire i test:
yarn twenty dev:typecheck
```
-Questo esegue `tsc --noEmit` e riporta eventuali errori di tipo.
+Questo esegue `tsc --noEmit` contro il `tsconfig.json` della tua app e riporta eventuali errori di tipo. Le app generate dallo scaffolder includono anche uno script `yarn typecheck` che copre anche i file di test (`tsconfig.spec.json`).
## CI con GitHub Actions
-Lo strumento di scaffolding genera un workflow GitHub Actions pronto all'uso in `.github/workflows/ci.yml`. Esegue automaticamente i test di integrazione a ogni push su `main` e sulle pull request.
+Lo strumento di scaffolding genera un workflow pronto all'uso in `.github/workflows/ci.yml`. A ogni push su `main` e a ogni pull request, avvia un server Twenty effimero nel runner (tramite l'azione `twentyhq/twenty/.github/actions/spawn-twenty-app-dev-test`), quindi esegue `yarn lint`, `yarn typecheck`, `yarn test:unit` e `yarn test` con `TWENTY_API_URL` / `TWENTY_API_KEY` che puntano a quel server. Non sono necessari secret e puoi fissare la versione del server tramite la variabile di ambiente `TWENTY_VERSION` in cima al workflow.
-Il workflow:
-
-1. Esegue il checkout del tuo codice
-2. Avvia un server Twenty temporaneo utilizzando l'azione `twentyhq/twenty/.github/actions/spawn-twenty-docker-image`
-3. Installa le dipendenze con `yarn install --immutable`
-4. Esegue `yarn test` con `TWENTY_API_URL` e `TWENTY_API_KEY` iniettati dagli output dell'azione
-
-```yaml .github/workflows/ci.yml
-name: CI
-
-on:
- push:
- branches:
- - main
- pull_request: {}
-
-env:
- TWENTY_VERSION: latest
-
-jobs:
- test:
- runs-on: ubuntu-latest
- steps:
- - name: Checkout
- uses: actions/checkout@v4
-
- - name: Spawn Twenty instance
- id: twenty
- uses: twentyhq/twenty/.github/actions/spawn-twenty-docker-image@main
- with:
- twenty-version: ${{ env.TWENTY_VERSION }}
- github-token: ${{ secrets.GITHUB_TOKEN }}
-
- - name: Enable Corepack
- run: corepack enable
-
- - name: Setup Node.js
- uses: actions/setup-node@v4
- with:
- node-version-file: '.nvmrc'
- cache: 'yarn'
-
- - name: Install dependencies
- run: yarn install --immutable
-
- - name: Run integration tests
- run: yarn test
- env:
- TWENTY_API_URL: ${{ steps.twenty.outputs.server-url }}
- TWENTY_API_KEY: ${{ steps.twenty.outputs.access-token }}
-```
-
-Non è necessario configurare alcun secret — l'azione `spawn-twenty-docker-image` avvia un server Twenty effimero direttamente nel runner e fornisce i dettagli di connessione. Il secret `GITHUB_TOKEN` è fornito automaticamente da GitHub.
-
-Per fissare una versione specifica di Twenty invece di `latest`, modifica la variabile d'ambiente `TWENTY_VERSION` all'inizio del workflow.
+Vedi [Pubblicazione → CI/CD automatizzato](/l/it/developers/extend/apps/operations/publishing#automated-cicd-scaffolded-workflows) per una spiegazione completa di entrambi i workflow generati dallo scaffolder (`ci.yml` e la pipeline di deploy `cd.yml`).
diff --git a/packages/twenty-docs/l/it/developers/extend/apps/tutorials/document-generator/building-the-ui.mdx b/packages/twenty-docs/l/it/developers/extend/apps/tutorials/document-generator/building-the-ui.mdx
index a7fe580a8a..c3e9956705 100644
--- a/packages/twenty-docs/l/it/developers/extend/apps/tutorials/document-generator/building-the-ui.mdx
+++ b/packages/twenty-docs/l/it/developers/extend/apps/tutorials/document-generator/building-the-ui.mdx
@@ -91,9 +91,11 @@ const GenerateDocumentForm = () => {
}, []);
const generate = async () => {
- const apiBaseUrl = process.env.TWENTY_API_URL;
+ // Prefer the injected functions URL; fall back to the legacy /s prefix (self-hosted/local)
+ const functionsBaseUrl =
+ process.env.TWENTY_FUNCTIONS_URL || `${process.env.TWENTY_API_URL}/s`;
const token = process.env.TWENTY_APP_ACCESS_TOKEN ?? process.env.TWENTY_API_KEY;
- const res = await fetch(`${apiBaseUrl}/s/documents/generate`, {
+ const res = await fetch(`${functionsBaseUrl}/documents/generate`, {
method: 'POST',
headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` },
body: JSON.stringify({ templateId, recordId }),
@@ -186,7 +188,9 @@ const DocumentViewer = () => {
const recordId = useFrontComponentExecutionContext((c) => c.recordId ?? null);
// ...load { content, file } for recordId, then derive the links:
const pdfUrl = document.file?.[0]?.url;
- const webUrl = `${process.env.TWENTY_API_URL ?? ''}/s/documents/view?id=${recordId}`;
+ const functionsBaseUrl =
+ process.env.TWENTY_FUNCTIONS_URL || `${process.env.TWENTY_API_URL ?? ''}/s`;
+ const webUrl = `${functionsBaseUrl}/documents/view?id=${recordId}`;
// Render the template body, plus quick links to the web page and the PDF.
// Links open in a new tab so they don't navigate the embedded component.
diff --git a/packages/twenty-docs/l/it/developers/extend/apps/tutorials/document-generator/http-routes.mdx b/packages/twenty-docs/l/it/developers/extend/apps/tutorials/document-generator/http-routes.mdx
index 9b86bb2add..53e3adddae 100644
--- a/packages/twenty-docs/l/it/developers/extend/apps/tutorials/document-generator/http-routes.mdx
+++ b/packages/twenty-docs/l/it/developers/extend/apps/tutorials/document-generator/http-routes.mdx
@@ -9,8 +9,15 @@ Lo stesso gestore può anche rispondere alle richieste HTTP. Aggiungeremo due pe
* un endpoint **POST** per generare un documento, e
* un endpoint pubblico **GET** che rende un documento come una pagina web stampabile.
-Entrambi usano `httpRouteTriggerSettings`. Gli itinerari delle app sono serviti sotto `/s` sul tuo server
-Twenty (es. `http://localhost:2020/s/documents/generate`).
+Entrambi usano `httpRouteTriggerSettings`. Sul server dev locale, gli itinerari delle app sono
+serviti sotto il prefisso `/s` (ad es. `http://localhost:2020/s/documents/generate`).
+
+
+Su Twenty Cloud, i percorsi sono serviti sul dominio delle funzioni dedicate
+dello spazio di lavoro — l'URL Twenty inietta come `TWENTY_FUNCTIONS_URL`, senza prefisso `/s`. Il prefisso `/s`
+è deprecato e rimane solo per le istanze autosostenute e locali.
+Vedi [Chiamare una funzione logica](/l/it/developers/extend/apps/layout/front-components#calling-a-logic-function).
+
## Percorso POST — generare su richiesta
diff --git a/packages/twenty-docs/l/it/developers/extend/apps/tutorials/document-generator/publishing.mdx b/packages/twenty-docs/l/it/developers/extend/apps/tutorials/document-generator/publishing.mdx
index 47ade6923d..cab31df02a 100644
--- a/packages/twenty-docs/l/it/developers/extend/apps/tutorials/document-generator/publishing.mdx
+++ b/packages/twenty-docs/l/it/developers/extend/apps/tutorials/document-generator/publishing.mdx
@@ -76,11 +76,11 @@ Eseguire lo stesso cancelli CI fa:
yarn lint # oxlint
yarn typecheck # tsgo
yarn test:unit # unit tests
-yarn twenty dev --once --dry-run # preview the metadata diff
+yarn twenty plan # preview the metadata diff
```
-L'esecuzione a secco stampa esattamente quello che cambierebbe sul server senza applicarlo —
-un buon controllo finale di sanità. Vedi
+Il piano mostra esattamente cosa verrebbe modificato sul server senza applicare effettivamente le modifiche —
+un buon controllo finale di coerenza. Vedi
[Testing](/l/it/developers/extend/apps/operations/testing) e
[Sincronizzazione & recupero](/l/it/developers/extend/apps/operations/sync-and-recovery).
diff --git a/packages/twenty-docs/l/ja/developers/extend/apps/config/install-hooks.mdx b/packages/twenty-docs/l/ja/developers/extend/apps/config/install-hooks.mdx
index 6ca535e1c0..6ec2daf95e 100644
--- a/packages/twenty-docs/l/ja/developers/extend/apps/config/install-hooks.mdx
+++ b/packages/twenty-docs/l/ja/developers/extend/apps/config/install-hooks.mdx
@@ -4,7 +4,7 @@ description: インストールの前後にロジックを実行して、シー
icon: wrench
---
-インストールフックは、インストールまたはアップグレードのライフサイクル中に実行される特別なロジック関数です。 これらは通常の[ロジック関数](/l/ja/developers/extend/apps/logic/logic-functions)と同じハンドラーランタイムを共有し、`InstallPayload` を受け取りますが、`definePostInstallLogicFunction()` と `definePreInstallLogicFunction()` という独自の define 関数で宣言され、通常のトリガーモデル (HTTP、cron、データベースイベント) の外側で動作します。
+インストールフックは、インストールまたはアップグレードのライフサイクル中に実行される特別なロジック関数です。 これらは通常の[ロジック関数](/l/ja/developers/extend/apps/logic/logic-functions)と同じハンドラーランタイムを共有し、`InstallPayload`(`{ previousVersion?: string; newVersion: string }` — 新規インストールでは `previousVersion` は `undefined`)を受け取りますが、独自の define 関数で宣言され、通常のトリガーモデル (HTTP、cron、データベースイベント) の外側で動作します。
各アプリは、**プレインストール関数は最大 1 つ**、**ポストインストール関数も最大 1 つ**まで定義できます。 どちらかが複数検出された場合、マニフェストのビルドはエラーになります。
@@ -19,111 +19,59 @@ icon: wrench
└─────────────────────────────────────────────────────────────┘
```
-
-
+## ひと目でわかる概要
-ポストインストール関数は、アプリのワークスペースへのインストールが完了した後に自動的に実行されます。 サーバーは、アプリのメタデータが同期され、SDK クライアントが生成された**後に**これを実行します。そのため、ワークスペースは完全に利用できる状態となり、新しいスキーマが適用されています。 代表的なユースケースには、デフォルトデータの投入、初期レコードの作成、ワークスペース設定の構成、またはサードパーティのサービスでのリソースのプロビジョニングが含まれます。
+| | `definePreInstallLogicFunction` | `definePostInstallLogicFunction` |
+| ------ | -------------------------------------------------------- | --------------------------------------------------------------------------------- |
+| 実行回数 | メタデータマイグレーションの前 — **以前の**スキーマとデータはまだそのまま残っている | マイグレーションと SDK 生成の後 — **新しい**スキーマが適用されている |
+| 実行 | 常に同期的であり、インストールをブロックする | デフォルトでは非同期(キュー投入され、最大 3 回再試行);`shouldRunSynchronously: true` の指定で同期実行に切り替え可能 |
+| 失敗時 | スキーマ変更の前にインストールが**中止**される | 非同期: 最大 3 回まで再試行される。 同期: 呼び出し元は `POST_INSTALL_ERROR` を受け取る(スキーマ変更はロールバック**されない**) |
+| 典型的な用途 | マイグレーションで失われるデータのバックアップや修復を行う;スローすることでリスクの高いアップグレードを拒否する | デフォルトデータのシーディング、ワークスペースの構成、外部リソースの登録 |
-```ts src/logic-functions/post-install.ts
-import { definePostInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define';
+**経験則:** 既定では post-install を使用する。 マイグレーション自体が破壊的で、消える前の状態を先に扱う必要がある場合にのみ、pre-install を使ってください。
-const handler = async (payload: InstallPayload): Promise => {
- console.log('Post install logic function executed successfully!', payload.previousVersion);
-};
+| やりたいこと… | 使用 |
+| ---------------------------------------- | -------------------------------------------------- |
+| データのシーディング、ワークスペースの構成、外部リソースの登録 | `post-install` |
+| インストール応答をブロックすべきでない長時間処理を実行する | `post-install`(既定の非同期モード。ワーカーによる再試行あり) |
+| インストールが返った直後に呼び出し元がすぐに依存する高速なセットアップを実行する | `post-install`(`shouldRunSynchronously: true` を指定) |
+| 次のマイグレーションで失われるデータを読み取る、またはバックアップする | `pre-install` |
+| 既存データを破損させる恐れのあるアップグレードを拒否する | `pre-install`(ハンドラーからスロー) |
+| すべてのアップグレードで調整処理を実行する | `shouldRunOnVersionUpgrade: true` を指定したいずれかのフック |
-export default definePostInstallLogicFunction({
- universalIdentifier: 'f7a2b9c1-3d4e-5678-abcd-ef9876543210',
- name: 'post-install',
- description: 'Runs after installation to set up the application.',
- timeoutSeconds: 300,
- shouldRunOnVersionUpgrade: false,
- shouldRunSynchronously: false,
- handler,
-});
-```
+## 両方のフックに共通する動作
-CLI を使用して、いつでもポストインストール関数を手動で実行することもできます:
+* 設定は、トリガー設定を除いた `defineLogicFunction` の設定に `shouldRunOnVersionUpgrade` を加えたものです。
+* **実行タイミング**: 既定では新規インストール時のみ。 アップグレード時にも実行するには、`shouldRunOnVersionUpgrade: true` を設定します。 `previousVersion` / `newVersion` を使って、アップグレードパスに応じて分岐させます。
+* **べき等性が重要です**: 非同期 post-install は再試行される可能性があり、さらに `shouldRunOnVersionUpgrade` が有効な場合はいずれのフックもアップグレード時に再実行されます。
+* 通常のロジック関数の環境(`APPLICATION_ID`、`APP_ACCESS_TOKEN`、`API_URL`)が注入されるため、アプリのトークンを使って Twenty API を呼び出せます。
+* フックはビルド時に自動的にアプリケーションマニフェストにアタッチされます(`preInstallLogicFunction` / `postInstallLogicFunction`)。[`defineApplication()`](/l/ja/developers/extend/apps/config/application) 内で参照する必要はありません。
+* デフォルトの `timeoutSeconds` は 300 に設定されており、データシーディングのような長めのセットアップ作業を許容します。
+* **開発モードでは実行されません**: `yarn twenty dev` はインストールフローをスキップしてファイルを直接同期するため、フックはそこで一切実行されません。 代わりに手動でトリガーしてください:
```bash filename="Terminal"
yarn twenty dev:function:exec --postInstall
-```
-
-主なポイント:
-* ポストインストール関数は `definePostInstallLogicFunction()` を使用します — トリガー設定(`cronTriggerSettings`, `databaseEventTriggerSettings`, `httpRouteTriggerSettings`, `toolTriggerSettings`, `workflowActionTriggerSettings`)を省いた専用のバリアントです。
-* ハンドラーは `InstallPayload`(`{ previousVersion?: string; newVersion: string }`)を受け取ります。`newVersion` は現在インストール中のバージョン、`previousVersion` は以前にインストールされていたバージョン(新規インストール時は `undefined`)です。 これらの値を使用して新規インストールとアップグレードを区別し、バージョン固有のマイグレーションロジックを実行します。
-* **フックが実行されるタイミング**: 既定では新規インストール時のみ。 アプリを以前のバージョンからアップグレードする際にも実行したい場合は、`shouldRunOnVersionUpgrade: true` を指定してください。 省略した場合、このフラグは既定で `false` となり、アップグレード時にはフックはスキップされます。
-* **実行モデル — 既定は非同期、同期はオプトイン**: `shouldRunSynchronously` フラグは、ポストインストールが実行される*方法*を制御します。
- * `shouldRunSynchronously: false` *(既定)* — フックは `retryLimit: 3` で**メッセージキューに投入**され、ワーカー内で非同期に実行されます。 ジョブがキューに投入されるとすぐにインストールのレスポンスが返るため、処理が遅い、または失敗するハンドラーでも呼び出し元をブロックしません。 ワーカーは最大 3 回まで再試行します。 **長時間実行のジョブに使用** — 大規模データセットのシーディング、低速なサードパーティ API の呼び出し、外部リソースのプロビジョニングなど、妥当な HTTP 応答時間枠を超える可能性のある処理。
- * `shouldRunSynchronously: true` — フックは**インストールフロー内でインライン実行**されます(プレインストールと同じエグゼキューター)。 ハンドラーが完了するまでインストールリクエストはブロックされ、スローした場合はインストールの呼び出し元が `POST_INSTALL_ERROR` を受け取ります。 自動再試行はありません。 **応答前に完了必須の高速な処理に使用** — 例: ユーザーへのバリデーションエラーの表示、インストール呼び出し直後にクライアントが依存するクイックセットアップ。 ポストインストールが実行される時点ではメタデータのマイグレーションはすでに適用済みである点に注意してください。そのため、同期モードで失敗してもスキーマ変更は**ロールバックされません** — エラーが表出するだけです。
-* ハンドラーが冪等であることを必ず確認してください。 非同期モードではキューが最大 3 回まで再試行する場合があります。いずれのモードでも、`shouldRunOnVersionUpgrade: true` の場合はアップグレード時にフックが再度実行されることがあります。
-* ハンドラー内では(他のロジック関数と同様に)環境変数 `APPLICATION_ID`、`APP_ACCESS_TOKEN`、`API_URL` が利用できます。そのため、アプリにスコープされたアプリケーションアクセストークンで Twenty API を呼び出せます。
-* アプリケーションごとにポストインストール関数は 1 つのみ許可されます。 複数検出された場合、マニフェストのビルドはエラーになります。
-* ビルド時に、関数の `universalIdentifier`、`shouldRunOnVersionUpgrade`、`shouldRunSynchronously` はアプリケーションマニフェストの `postInstallLogicFunction` フィールドに自動的に付与されます。[`defineApplication()`](/l/ja/developers/extend/apps/config/application) でそれらを参照する必要はありません。
-* デフォルトのタイムアウトは 300 秒(5 分)に設定されており、データシーディングのような長めのセットアップ作業を許容します。
-* **dev モードでは実行されません**: アプリがローカル登録(`yarn twenty dev`)された場合、サーバーはインストールフローを完全にスキップし、CLI ウォッチャー経由でファイルを直接同期します。したがって、`shouldRunSynchronously` に関わらず、dev モードではポストインストールは実行されません。 稼働中のワークスペースに対して手動でトリガーするには、`yarn twenty dev:function:exec --postInstall` を使用します。
-
-
-
-
-プレインストール関数は、インストール中に自動的に実行されるロジック関数で、**ワークスペースのメタデータマイグレーションが適用される前**に実行されます。 ポストインストール(`InstallPayload`)と同じペイロード型を共有しますが、インストールフローの早い段階に位置するため、これから行われるマイグレーションが依存する状態を準備できます。典型的な用途には、データのバックアップ、新しいスキーマとの互換性の検証、再構成または削除予定のレコードのアーカイブなどがあります。
-
-```ts src/logic-functions/pre-install.ts
-import { definePreInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define';
-
-const handler = async (payload: InstallPayload): Promise => {
- console.log('Pre install logic function executed successfully!', payload.previousVersion);
-};
-
-export default definePreInstallLogicFunction({
- universalIdentifier: 'a1b2c3d4-5678-90ab-cdef-1234567890ab',
- name: 'pre-install',
- description: 'Runs before installation to prepare the application.',
- timeoutSeconds: 300,
- shouldRunOnVersionUpgrade: true,
- handler,
-});
-```
-
-CLI を使用して、いつでもプレインストール関数を手動で実行することもできます:
-
-```bash filename="Terminal"
yarn twenty dev:function:exec --preInstall
```
-主なポイント:
-* プレインストール関数は `definePreInstallLogicFunction()` を使用します — ポストインストールと同じ特化設定ですが、異なるライフサイクルスロットに割り当てられます。
-* プレインストールとポストインストールの両ハンドラーは同じ `InstallPayload` 型(`{ previousVersion?: string; newVersion: string }`)を受け取ります。 一度インポートして、両方のフックで再利用してください。
-* **フックが実行されるタイミング**: ワークスペースのメタデータマイグレーション(`synchronizeFromManifest`)の直前に配置されます。 実行前に、サーバーは純粋に追加のみの「簡易同期」を実行し、ワークスペースのメタデータに**新しい**バージョンのプレインストール関数を登録します — それ以外には一切手を触れません — その後に実行されます。 この同期は追加のみのため、ハンドラーが実行される時点でも前バージョンのオブジェクト、フィールド、データはそのまま残っています。マイグレーション前の状態を安全に読み取り、バックアップできます。
-* **実行モデル**: プレインストールは**同期的**に実行され、**インストールをブロック**します。 ハンドラーがスローした場合、スキーマ変更が適用される前にインストールは中止され、ワークスペースは一貫した状態のまま前のバージョンに留まります。 これは意図的な設計です。プレインストールは、リスクの高いアップグレードを拒否できる最後の機会です。
-* ポストインストールと同様に、アプリケーションごとにプレインストール関数は 1 つのみ許可されます。 ビルド時に、アプリケーションマニフェストの `preInstallLogicFunction` に自動的に追加されます。
-* **dev モードでは実行されません**: ポストインストールと同様に、ローカル登録されたアプリではインストールフローが完全にスキップされるため、`yarn twenty dev` 下ではプレインストールは実行されません。 手動でトリガーするには、`yarn twenty dev:function:exec --preInstall` を使用します。
+
+
-
-
-
-両方のフックは同じインストールフローの一部で、同じ `InstallPayload` を受け取ります。 違いは、ワークスペースのメタデータマイグレーションとの相対的な実行タイミング(**いつ**実行されるか)であり、それによって安全に扱えるデータが変わります。
-
-プレインストールは常に**同期的**です(インストールをブロックでき、中止することも可能)。 ポストインストールは**既定で非同期**(ワーカーにエンキューされ自動再試行あり)ですが、`shouldRunSynchronously: true` で同期実行にオプトインできます。 各モードの使い分けは、上の `definePostInstallLogicFunction` のアコーディオンを参照してください。
-
-**新しいスキーマの存在を前提とする処理には `post-install` を使用してください。** これは一般的なケースです:
-
-* 新規に追加されたオブジェクトやフィールドに対するデフォルトデータのシーディング(初期レコード、デフォルトビュー、デモコンテンツの作成)。
-* アプリにクレデンシャルが付与された後に、サードパーティサービスにウェブフックを登録すること。
-* 同期済みメタデータに依存するセットアップを完了させるために自前の API を呼び出すこと。
-* あらゆるアップグレード時に状態を調整すべき、冪等な「存在を保証する」ロジック — `shouldRunOnVersionUpgrade: true` と組み合わせます。
-
-例 — インストール後に既定の `PostCard` レコードをシードする:
+アプリのインストールが完了した後に実行されます: メタデータは同期され、SDK クライアントが生成され、新しいスキーマはクエリ可能な状態になります。 例 — 新規インストール時に既定のレコードをシードする:
```ts src/logic-functions/post-install.ts
import { definePostInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define';
-import { createClient } from './generated/client';
+import { CoreApiClient } from 'twenty-client-sdk/core';
const handler = async ({ previousVersion }: InstallPayload): Promise => {
if (previousVersion) return; // fresh installs only
- const client = createClient();
- await client.postCard.create({
- data: { title: 'Welcome to Postcard', content: 'Your first card!' },
+ const client = new CoreApiClient();
+ await client.mutation({
+ createPostCard: {
+ __args: { data: { name: 'Welcome to Postcard', content: 'Your first card!' } },
+ id: true,
+ },
});
};
@@ -133,22 +81,28 @@ export default definePostInstallLogicFunction({
description: 'Seeds a welcome post card after install.',
timeoutSeconds: 300,
shouldRunOnVersionUpgrade: false,
+ shouldRunSynchronously: false,
handler,
});
```
-**`pre-install` を、マイグレーションが既存データを破壊または破損しかねない場合に使用してください。** プレインストールは*以前の*スキーマに対して実行され、失敗するとアップグレードをロールバックするため、リスクのある処理に最適です:
+`shouldRunSynchronously` フラグは実行モデルを制御します:
-* **削除または再構成される予定のデータのバックアップ** — 例: v2 でフィールドを削除するため、マイグレーション実行前にその値を別のフィールドへコピーする、またはストレージへエクスポートする必要がある場合。
-* **新しい制約により無効化されるレコードのアーカイブ** — 例: フィールドが `NOT NULL` になるため、先に null 値の行を削除または修正する必要がある場合。
-* 互換性を**検証し、現在のデータをクリーンに移行できない場合はアップグレードを拒否** — ハンドラーからスローすれば、変更が適用されないままインストールが中止されます。 これは、マイグレーションの途中で非互換性に気付くよりも安全です。
-* 関連付けが失われるスキーマ変更に先立って、**データの名称変更やキーの再割り当て**を行う。
+* `false` *(既定)* — メッセージキューに投入され(`retryLimit: 3`)、ワーカーによって実行される。 ジョブがキューに投入されるとすぐにインストールのレスポンスが返ります。 **長時間実行される処理に使用** — 大規模データセットのシーディング、低速なサードパーティ API など。
+* `true` — インストールフロー中にインラインで実行される。 ハンドラーが終了するまでインストールリクエストはブロックされます。スローされたエラーは `POST_INSTALL_ERROR` として呼び出し元に伝播します(再試行なし)。 **高速かつ、レスポンス前に完了している必要がある処理に使用します。** この時点ではマイグレーションはすでに適用済みであるため、失敗してもスキーマ変更はロールバックされず、エラーが表面化するだけです。
-例 — 破壊的なマイグレーションの前にレコードをアーカイブする:
+
+
+
+メタデータマイグレーションの前、**以前の**スキーマに対して実行されます — マイグレーションで失われるデータをバックアップしたり、リスクの高いアップグレードを拒否したりするのに適した場所です。 実行前に、サーバーは純粋に追加のみの「簡易同期」を実行し、新しいバージョンのプレインストール関数だけを登録します。あなたのハンドラーが実行される際には、それ以外 — 以前のバージョンのオブジェクト、フィールド、データ — には一切手を触れません。
+
+プレインストールは常に**同期的**であり、インストールをブロックします。 ハンドラーがスローした場合、スキーマ変更が行われる前にインストールは中止され、ワークスペースは一貫した状態のまま前のバージョンに留まります。 これは意図的な設計です。プレインストールは、リスクの高いアップグレードを拒否できる最後の機会です。
+
+例 — マイグレーションでレガシーフィールドが削除される前に、その値をコピーする:
```ts src/logic-functions/pre-install.ts
import { definePreInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define';
-import { createClient } from './generated/client';
+import { CoreApiClient } from 'twenty-client-sdk/core';
const handler = async ({ previousVersion, newVersion }: InstallPayload): Promise => {
// Only the 1.x → 2.x upgrade drops the legacy `notes` field.
@@ -156,24 +110,24 @@ const handler = async ({ previousVersion, newVersion }: InstallPayload): Promise
return;
}
- const client = createClient();
- const legacyRecords = await client.postCard.findMany({
- where: { notes: { isNotNull: true } },
+ const client = new CoreApiClient();
+ const { postCards } = await client.query({
+ postCards: {
+ __args: { filter: { notes: { isNot: null } } },
+ edges: { node: { id: true, notes: true } },
+ },
});
- if (legacyRecords.length === 0) return;
-
- // Copy legacy `notes` into the new `description` field before the migration
- // drops the `notes` column. If this fails, the upgrade is aborted and the
- // workspace stays on v1 with all data intact.
- await Promise.all(
- legacyRecords.map((record) =>
- client.postCard.update({
- where: { id: record.id },
- data: { description: record.notes },
- }),
- ),
- );
+ // Copy legacy `notes` into `description` before the migration drops the
+ // column. If this fails, the upgrade aborts and the workspace stays on v1.
+ for (const { node } of postCards.edges) {
+ await client.mutation({
+ updatePostCard: {
+ __args: { id: node.id, data: { description: node.notes } },
+ id: true,
+ },
+ });
+ }
};
export default definePreInstallLogicFunction({
@@ -186,21 +140,5 @@ export default definePreInstallLogicFunction({
});
```
-**経験則:**
-
-| やりたいこと… | 使用 |
-| ----------------------------------------------- | --------------------------------------------------------------- |
-| デフォルトデータのシーディング、ワークスペースの構成、外部リソースの登録 | `post-install` |
-| インストールの応答をブロックすべきでない長時間のシーディングやサードパーティ呼び出しを実行する | `post-install`(既定 — `shouldRunSynchronously: false`、ワーカーの再試行あり) |
-| インストール呼び出しが返った直後に呼び出し元が依存する高速なセットアップを実行する | `post-install`(`shouldRunSynchronously: true` を指定) |
-| 次のマイグレーションで失われるデータを読み取る、またはバックアップする | `pre-install` |
-| 既存データを破損させる恐れのあるアップグレードを拒否する | `pre-install`(ハンドラーからスロー) |
-| すべてのアップグレードで調整処理を実行する | `post-install`(`shouldRunOnVersionUpgrade: true` を指定) |
-| 初回インストール時のみの一度限りのセットアップを行う | `post-install`(`shouldRunOnVersionUpgrade: false` を指定、既定) |
-
-
-迷ったら、既定は**post-install**にしましょう。 マイグレーション自体が破壊的で、消える前の状態を先に扱う必要がある場合にのみ、pre-install を使ってください。
-
-
diff --git a/packages/twenty-docs/l/ja/developers/extend/apps/data/objects.mdx b/packages/twenty-docs/l/ja/developers/extend/apps/data/objects.mdx
index f569b4ffc2..5b3f977a67 100644
--- a/packages/twenty-docs/l/ja/developers/extend/apps/data/objects.mdx
+++ b/packages/twenty-docs/l/ja/developers/extend/apps/data/objects.mdx
@@ -86,6 +86,22 @@ export default defineObject({
**ベースフィールドは自動的に追加されます。** カスタムオブジェクトを定義すると、Twenty は `id`、`name`、`createdAt`、`updatedAt`、`createdBy`、`updatedBy`、`deletedAt` などの標準フィールドを自動的に作成します。 これらを `fields` 配列で宣言する必要はありません。カスタムフィールドのみを追加してください。 同じ名前でフィールドを宣言することでデフォルトフィールドを上書きすることもできますが、これはほとんどの場合お勧めできません。
+## フィールドタイプ
+
+`twenty-sdk/define` からエクスポートされる、`FieldType` 値の完全な一覧:
+
+| カテゴリ | タイプ |
+| ---------- | ------------------------------------------------------------------------------------------------------------ |
+| テキスト | `TEXT`、`RICH_TEXT`、`ARRAY`(文字列の配列)、`RAW_JSON` |
+| 数値 | `NUMBER`(`universalSettings.dataType`: `'float'` / `'int'` / `'bigint'`)、`NUMERIC`(任意精度)、`RATING`、`POSITION` |
+| 日付 | `DATE`, `DATE_TIME` |
+| 選択 | `BOOLEAN`、`SELECT`、`MULTI_SELECT` |
+| 複合 | `FULL_NAME`、`ADDRESS`、`EMAILS`、`PHONES`、`LINKS`、`CURRENCY`、`ACTOR`、`FILES` |
+| 識別子とリレーション | `UUID`、`RELATION`、`MORPH_RELATION`([Relations](/l/ja/developers/extend/apps/data/relations) を参照) |
+| システム | `TS_VECTOR`(サーバーによって管理される全文検索ベクター) |
+
+複合タイプは複数のサブフィールドを保持します(例: `FULL_NAME` = 名 + 姓、`CURRENCY` = `amountMicros` + `currencyCode`)。 `SELECT` と `MULTI_SELECT` は、上記の例のように `options` 配列を必要とします。
+
## デフォルト値
文字列リテラルのデフォルト値は、文字列**の内部で**シングルクォートで囲む必要があります。つまり、`defaultValue: "'Draft'"` のように書き、`defaultValue: "Draft"` のようには書きません。 そのため上記の `status` フィールドでは、`` `'${PostCardStatus.DRAFT}'` `` を使用しています。
diff --git a/packages/twenty-docs/l/ja/developers/extend/apps/getting-started/project-structure.mdx b/packages/twenty-docs/l/ja/developers/extend/apps/getting-started/project-structure.mdx
index 1d0977d50c..d4340887c7 100644
--- a/packages/twenty-docs/l/ja/developers/extend/apps/getting-started/project-structure.mdx
+++ b/packages/twenty-docs/l/ja/developers/extend/apps/getting-started/project-structure.mdx
@@ -14,26 +14,39 @@ my-twenty-app/
default-role.ts # Permissions for logic functions
constants/
universal-identifiers.ts # Auto-generated UUIDs and metadata
+ front-components/
+ main-page.tsx # Welcome page component
+ navigation-menu-items/
+ main-page.navigation-menu-item.ts # Sidebar entry for the welcome page
+ page-layouts/
+ main-page.page-layout.ts # Standalone page hosting the component
__tests__/
- setup-test.ts
- app-install.integration-test.ts
- .github/workflows/ci.yml # GitHub Actions
- public/ # Static assets
- vitest.config.ts # Test runner config
+ application-config.test.ts # Unit test
+ global-setup.ts # Integration test setup (sync + uninstall)
+ schema.integration-test.ts # Integration test against a live server
+ .github/workflows/
+ ci.yml # Lint, typecheck, unit + integration tests
+ cd.yml # Deploy + install on push to main
+ public/
+ logo.svg # Static assets
+ vitest.config.ts # Integration test runner config
+ vitest.unit.config.ts # Unit test runner config
tsconfig.json, tsconfig.spec.json
.nvmrc, .yarnrc.yml, .oxlintrc.json
- README.md, LLMS.md
+ README.md, AGENTS.md, CLAUDE.md
```
## 主要ファイル
-| ファイル / フォルダー | 目的 |
-| ---------------------------------------- | --------------------------------- |
-| `src/application-config.ts` | **必須。** アプリのメイン設定ファイルです。 |
-| `src/default-role.ts` | ロジック関数がアクセスできる範囲を制御するデフォルトのロールです。 |
-| `src/constants/universal-identifiers.ts` | 自動生成される UUID とアプリのメタデータ(表示名、説明)。 |
-| `src/__tests__/` | 統合テスト(セットアップ + サンプルテスト)。 |
-| `public/` | アプリとともに提供される静的アセット(画像、フォント)。 |
+| ファイル / フォルダー | 目的 |
+| -------------------------------------------------------------------------- | ------------------------------------------------------------------------- |
+| `src/application-config.ts` | **必須。** アプリのメイン設定ファイルです。 |
+| `src/default-role.ts` | ロジック関数がアクセスできる範囲を制御するデフォルトのロールです。 |
+| `src/constants/universal-identifiers.ts` | 自動生成される UUID とアプリのメタデータ(表示名、説明)。 |
+| `src/front-components/`, `src/navigation-menu-items/`, `src/page-layouts/` | スターター用のウェルカムページ:スタンドアロンのページレイアウトによってレンダリングされ、サイドバーからアクセスできるフロントコンポーネントです。 |
+| `src/__tests__/` | ユニットテストと統合テスト(グローバルセットアップ付き)があり、実際のサーバーに対してアプリを同期します。 |
+| `public/` | アプリとともに提供される静的アセット(画像、フォント)。 |
+| `AGENTS.md` / `CLAUDE.md` | アプリ上で作業する AI コーディングエージェント向けのガイダンス。 |
**ファイル構成は自由です。** 上記のフォルダーはあくまで慣習であり、SDK はファイルがどこにあっても、`export default defineEntity(...)` 呼び出しに対する AST 解析によってエンティティを検出します。
@@ -47,15 +60,18 @@ Twenty の両方の SDK パッケージは、`dependencies` ではなく `devDep
{
"dependencies": {},
"devDependencies": {
- "twenty-client-sdk": "^2.13.0",
- "twenty-sdk": "^2.13.0"
+ "twenty-client-sdk": "2.20.0",
+ "twenty-sdk": "2.20.0",
+ "twenty-ui": "1.0.0-alpha.1"
}
}
```
+スキャフォルダーは `twenty-sdk` と `twenty-client-sdk` を自身のバージョンに固定します — アップグレードする際はこの 2 つを同期させてください。
+
* **`twenty-sdk`** は、`twenty` CLI とビルド/スキャフォールディング用のツールを提供します。 これは開発時とビルド時にのみ実行され、公開済みアプリのランタイムによってインポートされることは決してありません。
* **`twenty-client-sdk`** はアプリのコード(`CoreApiClient`、`MetadataApiClient`、`RestApiClient`)によってインポートされますが、Twenty がランタイムで提供します — ロジック関数は生成された SDK レイヤーからそれを取得し、フロントエンドコンポーネントはサーバー提供のモジュールから解決します。 インストール済みのコピーは型チェックとデプロイ時のビルドにのみ使用されるため、デプロイされたバンドルに同梱される必要はありません。
-どちらかのパッケージを `dependencies` の下に置いたままだと、インストールされたアプリのランタイムバンドルに取り込まれてしまい、不要な重荷になります。 いずれかが `dependencies` の下に残っていると、`twenty build` は警告を出力します。
+どちらかのパッケージを `dependencies` の下に置いたままだと、インストールされたアプリのランタイムバンドルに取り込まれてしまい、不要な重荷になります。 いずれかがまだ `dependencies` の下にリストされている場合、`twenty dev:build` は警告を出力します。
アプリ独自のランタイム依存関係(ロジック関数が実際にランタイムでインポートするライブラリ)は、通常どおり `dependencies` の下に追加してください。
diff --git a/packages/twenty-docs/l/ja/developers/extend/apps/getting-started/quick-start.mdx b/packages/twenty-docs/l/ja/developers/extend/apps/getting-started/quick-start.mdx
index f754a2acc6..f306145b4f 100644
--- a/packages/twenty-docs/l/ja/developers/extend/apps/getting-started/quick-start.mdx
+++ b/packages/twenty-docs/l/ja/developers/extend/apps/getting-started/quick-start.mdx
@@ -6,17 +6,17 @@ description: 数分で最初の Twenty アプリを作成しましょう。
## 前提条件
-* **Node.js 24+** — [こちらからダウンロード](https://nodejs.org/)
+* **Node.js 24.5+** — [こちらからダウンロード](https://nodejs.org/)
* **Yarn 4** — Corepack 経由で Node.js に同梱されています。 有効化: `corepack enable`
* **Docker** — [こちらからダウンロード](https://www.docker.com/products/docker-desktop/)。 ローカルの Twenty サーバーを実行するために必要です。 すでに別の場所で Twenty が稼働している場合はスキップしてください。
Twenty アプリの構築は 3 つのフェーズで構成されます。 スキャフォルダーはそれらをハッピーパスの 1 つのコマンドにまとめますが、各フェーズは別個の概念です — 何かが失敗したとき、いまどのフェーズにいるかが分かると、直すべき箇所が特定できます。
-| フェーズ | やること | ツール | 結果 |
-| --------------- | ----------------------- | ----------------------------- | ------------------------ |
-| **1. スキャフォールド** | アプリのソースコードを生成する | `npx create-twenty-app` | ディスク上の TypeScript プロジェクト |
-| **2. サーバーを起動** | 同期先となる Twenty サーバーを起動する | Docker + `yarn twenty server` | 稼働中の Twenty インスタンス |
-| **3. 同期** | コードをサーバーにライブ同期する | `yarn twenty dev` | 変更が UI に反映されます |
+| フェーズ | やること | ツール | 結果 |
+| --------------- | ----------------------- | ----------------------------------- | ------------------------ |
+| **1. スキャフォールド** | アプリのソースコードを生成する | `npx create-twenty-app` | ディスク上の TypeScript プロジェクト |
+| **2. サーバーを起動** | 同期先となる Twenty サーバーを起動する | Docker + `yarn twenty docker:start` | 稼働中の Twenty インスタンス |
+| **3. 同期** | コードをサーバーにライブ同期する | `yarn twenty dev` | 変更が UI に反映されます |
---
@@ -28,7 +28,7 @@ Twenty アプリの構築は 3 つのフェーズで構成されます。 スキ
npx create-twenty-app@latest my-twenty-app
```
-名前と説明の入力を求められます — 既定値でよければ **Enter** を押します。 これにより、`my-twenty-app/` にスターターの `application-config.ts`、デフォルトロール、CI ワークフロー、統合テストを含む TypeScript プロジェクトが生成されます。
+スキャフォルダーは非対話型であり、ディレクトリ名がアプリ名になります。 生成されるメタデータをカスタマイズするには、`--display-name` と `--description` を指定します(後から `src/constants/universal-identifiers.ts` 内で編集することもできます)。 これにより、`my-twenty-app/` にスターターの `application-config.ts`、デフォルトロール、CI/CD ワークフロー、および統合テストを含む TypeScript プロジェクトが生成されます。
**このフェーズ後:** マシン上にアプリのソースコードがあります。 まだ実行はされていません — それはフェーズ 2 です。
@@ -38,28 +38,14 @@ npx create-twenty-app@latest my-twenty-app
アプリは同期先としての Twenty サーバーを必要とします。 サーバーは、UI、GraphQL API、PostgreSQL を備えた完全な Twenty インスタンスで、Docker 上でローカルに実行されます。 ローカルのコードは定義をそのサーバーにアップロードし、UI に反映されます。
-スキャフォルダーが起動を提案します:
+スキャフォルダーが環境を自動的に起動します。Docker が動作している状態で、`twentycrm/twenty-app-dev` イメージを取得し、ポート `2020` で起動して、事前にデモデータが投入されたワークスペース(`tim@apple.dev`)に対して CLI を認証します — サインインは不要です。
-> **ローカルの Twenty インスタンスをセットアップしますか?**
-
-* **Yes(推奨)** — `twentycrm/twenty-app-dev` Docker イメージを取得し、ポート `2020` で起動します。 まず Docker が起動していることを確認してください。
-* **No** — すでに接続したい Twenty サーバーがある場合に選択します。 後で `yarn twenty remote:add` で接続を設定できます。
-
-
-

-
-
-サーバーが起動すると、サインイン用にブラウザーが開きます。 あらかじめ用意されたデモアカウントを使用します:
-
-* **メールアドレス:** `tim@apple.dev`
-* **パスワード:** `tim@apple.dev`
+既存の Twenty サーバーに接続する場合は、代わりに `--url \` を指定してください。 リモートサーバーは OAuth で認証されます。ブラウザーが開き、サインインして **Authorize** をクリックすると、CLI にワークスペースへのアクセス権が付与されます。 (ローカルでも `--authentication-method oauth` を指定して OAuth を利用できます。その場合は `tim@apple.dev` / `tim@apple.dev` でサインインします。)
-次の画面で **Authorize** をクリックします — これにより、CLI にワークスペースへのアクセスが許可されます。
-
@@ -117,28 +103,32 @@ yarn twenty dev
### CI やスクリプト向けの一回限りの同期
-単一のビルド+同期を実行して終了するには `--once` を指定します — パイプラインは同じでウォッチャーはありません:
+ウォッチャーなしで同じパイプラインを 1 回だけ実行するには、`plan` と `apply` を使用します。
```bash filename="Terminal"
-yarn twenty dev --once
+yarn twenty plan # preview the metadata changes without applying them
+yarn twenty apply # show the plan, then apply it
```
-| コマンド | 動作 | 使用する場面 |
-| ---------------------------------- | ------------------------------------------- | -------------------------------------------- |
-| `yarn twenty dev` | ソースファイルを監視し、変更のたびに再同期します。 停止するまで実行し続けます。 | 対話的なローカル開発。 |
-| `yarn twenty dev --once` | ビルドと同期を一度だけ実行し、成功時はコード `0`、失敗時は `1` で終了します。 | CI、pre-commit フック、AI エージェント、スクリプト化されたワークフロー。 |
-| `yarn twenty dev --once --dry-run` | メタデータの変更をビルドして出力しますが、**実際には適用しません**。 | 同期によってどのような変更が行われるかを、実行を確定する前に確認します。 |
+| コマンド | 動作 | 使用する場面 |
+| ------------------- | ------------------------------------------------------------------------------------------- | -------------------------------------------- |
+| `yarn twenty dev` | ソースファイルを監視し、変更のたびに再同期します。 停止するまで実行し続けます。 | 対話的なローカル開発。 |
+| `yarn twenty apply` | ビルドと同期を一度だけ実行し、成功時はコード `0`、失敗時は `1` で終了します。 破壊的な変更がある場合に確認を求めます(スキップするには `--force` を指定します)。 | CI、pre-commit フック、AI エージェント、スクリプト化されたワークフロー。 |
+| `yarn twenty plan` | メタデータの変更をビルドして出力しますが、**実際には適用しません**。 | 同期によってどのような変更が行われるかを、実行を確定する前に確認します。 |
-どちらのモードも、認証済みのリモートが必要です。 `--dry-run` について詳しくは、[Syncing & recovery](/l/ja/developers/extend/apps/operations/sync-and-recovery#previewing-changes-dry-run) を参照してください。
+すべてのモードで、認証済みのリモートが必要です。 `plan` の詳細については、[Syncing & recovery](/l/ja/developers/extend/apps/operations/sync-and-recovery#previewing-changes-plan) を参照してください。
+
+
+`yarn twenty dev --once` および `yarn twenty dev --once --dry-run` は非推奨であり、それぞれ `yarn twenty apply` および `yarn twenty plan` のエイリアスです。
+
### Dev モードのオプション
-| フラグ | 説明 |
-| ------------------------------------- | --------------------------------------------------- |
-| `--once` | 一度ビルドと同期を実行したら終了します。 |
-| `--dry-run` | `--once` を使用すると、メタデータの変更を適用せずにプレビューできます。 何も書き込みません。 |
-| `--debounceMs \` | ファイル変更のデバウンス遅延をミリ秒単位で設定します (既定値: `2000`)。 |
-| `--verbose` / `--debug` | 詳細なビルドログ、同期リクエスト、およびエラートレースを表示します。 |
+| フラグ | 説明 |
+| ------------------------------------- | ----------------------------------------- |
+| `--force` | 確認なしで破壊的な変更(削除)を適用します。 |
+| `--debounceMs \` | ファイル変更のデバウンス遅延をミリ秒単位で設定します (既定値: `1000`)。 |
+| `--verbose` / `--debug` | 詳細なビルドログ、同期リクエスト、およびエラートレースを表示します。 |
## 構築できるもの
diff --git a/packages/twenty-docs/l/ja/developers/extend/apps/getting-started/scaffolding.mdx b/packages/twenty-docs/l/ja/developers/extend/apps/getting-started/scaffolding.mdx
index 603b66ff56..204d8c0755 100644
--- a/packages/twenty-docs/l/ja/developers/extend/apps/getting-started/scaffolding.mdx
+++ b/packages/twenty-docs/l/ja/developers/extend/apps/getting-started/scaffolding.mdx
@@ -34,6 +34,10 @@ yarn twenty dev:add frontComponent
| ビュー | `yarn twenty dev:add view` | `src/views/\.ts` |
| ナビゲーションメニュー項目 | `yarn twenty dev:add navigationMenuItem` | `src/navigation-menu-items/\.ts` |
| ページレイアウト | `yarn twenty dev:add pageLayout` | `src/page-layouts/\.ts` |
+| ページレイアウトタブ | `yarn twenty dev:add pageLayoutTab` | `src/page-layout-tabs/\.ts` |
+| コマンドメニュー項目 | `yarn twenty dev:add commandMenuItem` | `src/command-menu-items/\.ts` |
+| ビューフィールド | `yarn twenty dev:add viewField` | `src/view-fields/\.ts` |
+| 接続プロバイダー | `yarn twenty dev:add connectionProvider` | `src/connection-providers/\.ts` |
## スキャフォルダーが生成するもの
diff --git a/packages/twenty-docs/l/ja/developers/extend/apps/getting-started/troubleshooting.mdx b/packages/twenty-docs/l/ja/developers/extend/apps/getting-started/troubleshooting.mdx
index 40a2d432a4..8da4fd7725 100644
--- a/packages/twenty-docs/l/ja/developers/extend/apps/getting-started/troubleshooting.mdx
+++ b/packages/twenty-docs/l/ja/developers/extend/apps/getting-started/troubleshooting.mdx
@@ -5,10 +5,10 @@ icon: wrench
---
* **Docker のエラー** — `yarn twenty docker:start` の前に Docker Desktop(またはデーモン)が起動していることを確認してください。 エラーメッセージに、OS に適した起動コマンドが表示されます。
-* **Node のバージョンが違います** — 24 以上が必要です。 `node -v` で確認してください。
+* **誤った Node のバージョン** — 24.5 以上が必要です(`engines.node: ^24.5.0`)。 `node -v` で確認してください。
* **Yarn 4 が見つからない** — `corepack enable` を実行してください。
* **依存関係の破損** — `rm -rf node_modules && yarn install`。
* **`twenty-sdk` が v2.8.0 へのアップグレード後にエラーになる** — v2.8.0 で `dependencies` から `devDependencies` に移動しました。 [Project Structure → Dependencies](/l/ja/developers/extend/apps/getting-started/project-structure#dependencies) を参照してください。
-* **`twenty build` は `dependencies` 配下の `twenty-client-sdk` について警告します** — これは Twenty によって実行時に提供されるため、`twenty-sdk` と同様に `devDependencies` へ移動する必要があります。 [Project Structure → Dependencies](/l/ja/developers/extend/apps/getting-started/project-structure#dependencies) を参照してください。
+* **`twenty dev:build` は `dependencies` 配下の `twenty-client-sdk` について警告します** — これは Twenty によって実行時に提供されるため、`twenty-sdk` と同様に `devDependencies` へ移動する必要があります。 [Project Structure → Dependencies](/l/ja/developers/extend/apps/getting-started/project-structure#dependencies) を参照してください。
行き詰まりましたか? [Twenty の Discord](https://discord.com/channels/1130383047699738754/1130386664812982322)でヘルプを依頼してください。
diff --git a/packages/twenty-docs/l/ja/developers/extend/apps/layout/command-menu-items.mdx b/packages/twenty-docs/l/ja/developers/extend/apps/layout/command-menu-items.mdx
index b944faa35a..573dc304b9 100644
--- a/packages/twenty-docs/l/ja/developers/extend/apps/layout/command-menu-items.mdx
+++ b/packages/twenty-docs/l/ja/developers/extend/apps/layout/command-menu-items.mdx
@@ -13,7 +13,6 @@ export default defineCommandMenuItem({
universalIdentifier: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
label: 'Open Dashboard',
shortLabel: 'Dashboard',
- icon: 'IconLayoutDashboard',
isPinned: true,
availabilityType: 'GLOBAL',
frontComponentUniversalIdentifier: '74c526eb-cb68-4cf7-b05c-0dd8c288d948',
@@ -22,51 +21,23 @@ export default defineCommandMenuItem({
## 設定フィールド
-| フィールド | 必須 | 説明 |
-| --------------------------------------- | --- | ------------------------------------------------------------------------------------------------ |
-| `universalIdentifier` | はい | コマンドの安定した一意の ID |
-| `label` | はい | コマンドメニュー(Cmd+K)に表示されるフルラベル |
-| `frontComponentUniversalIdentifier` | はい | このコマンドが開くフロントコンポーネントの `universalIdentifier` |
-| `shortLabel` | いいえ | ピン留めされたクイックアクションボタンに表示される短いラベル |
-| `icon` | いいえ | ラベルの横に表示するアイコン名(例:'IconBolt'、'IconSend') |
-| `isPinned` | いいえ | `true` の場合、ページ右上にクイックアクションボタンとして表示します |
-| `availabilityType` | いいえ | コマンドの表示場所を制御します:'GLOBAL'(常に利用可能)、'RECORD_SELECTION'(レコード選択時のみ)、または 'FALLBACK'(他のコマンドが一致しないときに表示) |
-| `availabilityObjectUniversalIdentifier` | いいえ | コマンドを特定のオブジェクトタイプのページに制限します(例:Company レコードのみ) |
-| `conditionalAvailabilityExpression` | いいえ | 表示可否を動的に制御するブール式(下記参照) |
+| フィールド | 必須 | 説明 |
+| --------------------------------------- | --- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `universalIdentifier` | はい | コマンドの安定した一意の ID |
+| `label` | はい | コマンドメニュー(Cmd+K)に表示されるフルラベル |
+| `frontComponentUniversalIdentifier` | はい | このコマンドが開くフロントコンポーネントの `universalIdentifier` |
+| `shortLabel` | いいえ | ピン留めされたクイックアクションボタンに表示される短いラベル |
+| `icon` | いいえ | **非推奨** — アプリケーションアイコンが優先されるため無視されます。設定されている場合は、ビルド時に警告が出力されます。 |
+| `isPinned` | いいえ | `true` の場合、ページ右上にクイックアクションボタンとして表示します |
+| `availabilityType` | いいえ | コマンドの表示場所を制御します:`'GLOBAL'`(常に利用可能)、`'GLOBAL_OBJECT_CONTEXT'`(オブジェクトコンテキストを持つページ上のみ ― インデックスページおよびレコードページ)、`'RECORD_SELECTION'`(レコード選択時のみ)、または`'FALLBACK'`(他のコマンドが一致しないときに表示) |
+| `availabilityObjectUniversalIdentifier` | いいえ | コマンドを特定のオブジェクトタイプのページに制限します(例:Company レコードのみ) |
+| `conditionalAvailabilityExpression` | いいえ | 表示可否を動的に制御するブール式(下記参照) |
## ヘッドレスコマンド
[ヘッドレスフロントコンポーネント](/l/ja/developers/extend/apps/layout/front-components#headless-vs-non-headless)とペアになったコマンドメニュー項目は、ワンクリックアクション(コードの実行、ナビゲーション、確認と実行)を提供するための一般的な方法です。 Front Components のページでは、アクション実行後にアンマウントするパターンを処理する [SDK Command components](/l/ja/developers/extend/apps/layout/front-components#sdk-command-components)(`Command`、`CommandLink`、`CommandModal`、`CommandOpenSidePanelPage`)について説明しています。
-一般的なフロー:
-
-```tsx src/front-components/run-action.tsx
-import { defineFrontComponent } from 'twenty-sdk/define';
-import { Command } from 'twenty-sdk/command';
-import { CoreApiClient } from 'twenty-sdk/clients';
-
-const RunAction = () => {
- const execute = async () => {
- const client = new CoreApiClient();
- await client.mutation({
- createTask: {
- __args: { data: { title: 'Created by my app' } },
- id: true,
- },
- });
- };
-
- return ;
-};
-
-export default defineFrontComponent({
- universalIdentifier: 'e5f6a7b8-c9d0-1234-efab-345678901234',
- name: 'run-action',
- description: 'Creates a task from the command menu',
- component: RunAction,
- isHeadless: true,
-});
-```
+一般的なフロー:ヘッドレスコンポーネントが `` をレンダーし([完全なサンプル](/l/ja/developers/extend/apps/layout/front-components#sdk-command-components)を参照)、コマンドメニュー項目がそれを指すようにします:
```ts src/command-menu-items/run-action.command-menu-item.ts
import { defineCommandMenuItem } from 'twenty-sdk/define';
@@ -74,7 +45,6 @@ import { defineCommandMenuItem } from 'twenty-sdk/define';
export default defineCommandMenuItem({
universalIdentifier: 'f6a7b8c9-d0e1-2345-fabc-456789012345',
label: 'Run my action',
- icon: 'IconPlayerPlay',
frontComponentUniversalIdentifier: 'e5f6a7b8-c9d0-1234-efab-345678901234',
});
```
diff --git a/packages/twenty-docs/l/ja/developers/extend/apps/layout/front-components.mdx b/packages/twenty-docs/l/ja/developers/extend/apps/layout/front-components.mdx
index c3cad6a910..f08cc1d88e 100644
--- a/packages/twenty-docs/l/ja/developers/extend/apps/layout/front-components.mdx
+++ b/packages/twenty-docs/l/ja/developers/extend/apps/layout/front-components.mdx
@@ -49,14 +49,13 @@ export default defineCommandMenuItem({
universalIdentifier: 'd4e5f6a7-b8c9-0123-defa-456789012345',
shortLabel: 'Hello',
label: 'Hello World',
- icon: 'IconBolt',
isPinned: true,
availabilityType: 'GLOBAL',
frontComponentUniversalIdentifier: '74c526eb-cb68-4cf7-b05c-0dd8c288d948',
});
```
-`yarn twenty dev` で同期するか(または 1 回限りで `yarn twenty dev --once` を実行すると)、ページ右上にクイックアクションが表示されます:
+`yarn twenty dev` で同期するか(または 1 回限りで `yarn twenty apply` を実行すると)、ページ右上にクイックアクションが表示されます:

@@ -88,11 +87,11 @@ export default defineCommandMenuItem({
```tsx src/front-components/sync-tracker.tsx
import { defineFrontComponent } from 'twenty-sdk/define';
-import { useRecordId, enqueueSnackbar } from 'twenty-sdk/front-component';
+import { useSelectedRecordIds, enqueueSnackbar } from 'twenty-sdk/front-component';
import { useEffect } from 'react';
const SyncTracker = () => {
- const recordId = useRecordId();
+ const [recordId] = useSelectedRecordIds();
useEffect(() => {
enqueueSnackbar({ message: `Tracking record ${recordId}`, variant: 'info' });
@@ -116,7 +115,7 @@ export default defineFrontComponent({
`twenty-sdk` パッケージは、ヘッドレスのフロントコンポーネント向けに設計された4つの Command ヘルパーコンポーネントを提供します。 各コンポーネントは、マウント時にアクションを実行し、エラーをスナックバー通知で処理し、完了時にフロントコンポーネントを自動的にアンマウントします。
-`twenty-sdk/command` からインポートします:
+`twenty-sdk/front-component` からインポートします:
* **`Command`** — `execute` プロップ経由で非同期コールバックを実行します。
* **`CommandLink`** — アプリのパスにナビゲートします。 Props: `to`, `params`, `queryParams`, `options`.
@@ -127,8 +126,8 @@ export default defineFrontComponent({
```tsx src/front-components/run-action.tsx
import { defineFrontComponent } from 'twenty-sdk/define';
-import { Command } from 'twenty-sdk/command';
-import { CoreApiClient } from 'twenty-sdk/clients';
+import { Command } from 'twenty-sdk/front-component';
+import { CoreApiClient } from 'twenty-client-sdk/core';
const RunAction = () => {
const execute = async () => {
@@ -160,7 +159,6 @@ import { defineCommandMenuItem } from 'twenty-sdk/define';
export default defineCommandMenuItem({
universalIdentifier: 'f6a7b8c9-d0e1-2345-fabc-456789012345',
label: 'Run my action',
- icon: 'IconPlayerPlay',
frontComponentUniversalIdentifier: 'e5f6a7b8-c9d0-1234-efab-345678901234',
});
```
@@ -169,7 +167,7 @@ export default defineCommandMenuItem({
```tsx src/front-components/delete-draft.tsx
import { defineFrontComponent } from 'twenty-sdk/define';
-import { CommandModal } from 'twenty-sdk/command';
+import { CommandModal } from 'twenty-sdk/front-component';
const DeleteDraft = () => {
const execute = async () => {
@@ -202,7 +200,7 @@ export default defineFrontComponent({
`httpRouteTriggerSettings` で宣言されたロジック関数は、そのルートパスで HTTP 経由でアクセスできます。 Twenty は、関数が提供されるベース URL を `TWENTY_FUNCTIONS_URL` としてワーカーに注入し、呼び出しを認証する `TWENTY_APP_ACCESS_TOKEN` も併せて渡します。 独自の関数を呼び出すための専用 SDK クライアントはまだないため、シンプルな `fetch` を使って呼び出してください。
-> **Twenty Cloud では、HTTP トリガーのロジック関数はワークスペースごとの専用ドメインで提供されます**。`https://\
.twenty.com\` がそのドメインであり、これが `TWENTY_FUNCTIONS_URL` が解決される先とまったく同じです。 外部から呼び出す場合は、関数の **HTTP trigger** 設定、もしくはアプリケーションの **Settings** タブから、正確な URL をコピーしてください。
+> **Twenty Cloud では、HTTP トリガーのロジック関数はワークスペースごとの専用ドメインで提供されます**。`https://\.withtwenty.com\` がそのドメインであり、これが `TWENTY_FUNCTIONS_URL` が解決される先とまったく同じです。 外部から呼び出す場合は、関数の **HTTP trigger** 設定、もしくはアプリケーションの **Settings** タブから、正確な URL をコピーしてください。
レガシーな `/s/` 関数ルートは**非推奨**となっており、**2026-07-24 に無効化されます**。 代わりに(上記の)`TWENTY_FUNCTIONS_URL` を使用し、その日までにハードコードされた `/s/` URL をすべて移行してください。 `/s/` ルートはセルフホスティング向けには引き続き利用可能です。
@@ -212,7 +210,7 @@ export default defineFrontComponent({
```tsx src/front-components/sync-prs.tsx
import { defineFrontComponent } from 'twenty-sdk/define';
-import { Command } from 'twenty-sdk/command';
+import { Command } from 'twenty-sdk/front-component';
const SyncPrs = () => {
const execute = async () => {
@@ -316,13 +314,13 @@ try {
import { defineFrontComponent } from 'twenty-sdk/define';
import {
useUserId,
- useRecordId,
+ useSelectedRecordIds,
useFrontComponentId,
} from 'twenty-sdk/front-component';
const RecordInfo = () => {
const userId = useUserId();
- const recordId = useRecordId();
+ const [recordId] = useSelectedRecordIds();
const componentId = useFrontComponentId();
return (
@@ -405,12 +403,11 @@ export default defineFrontComponent({
```tsx src/front-components/archive-record.tsx
import { defineFrontComponent } from 'twenty-sdk/define';
-import { useRecordId } from 'twenty-sdk/front-component';
-import { enqueueSnackbar, closeSidePanel } from 'twenty-sdk/front-component';
-import { CoreApiClient } from 'twenty-sdk/clients';
+import { enqueueSnackbar, closeSidePanel, useSelectedRecordIds } from 'twenty-sdk/front-component';
+import { CoreApiClient } from 'twenty-client-sdk/core';
const ArchiveRecord = () => {
- const recordId = useRecordId();
+ const [recordId] = useSelectedRecordIds();
const handleArchive = async () => {
const client = new CoreApiClient();
@@ -451,10 +448,10 @@ export default defineFrontComponent({
複数の選択されたレコードを処理するには `useSelectedRecordIds()` を使用してください。 これは一括操作に役立ちます:
```tsx src/front-components/bulk-export.tsx
-import { defineFrontComponent, numberOfSelectedRecords } from 'twenty-sdk/define';
+import { defineFrontComponent } from 'twenty-sdk/define';
import { useSelectedRecordIds } from 'twenty-sdk/front-component';
import { enqueueSnackbar, closeSidePanel } from 'twenty-sdk/front-component';
-import { CoreApiClient } from 'twenty-sdk/clients';
+import { CoreApiClient } from 'twenty-client-sdk/core';
const BulkExport = () => {
const selectedRecordIds = useSelectedRecordIds();
@@ -492,12 +489,19 @@ export default defineFrontComponent({
name: 'bulk-export',
description: 'Export selected records',
component: BulkExport,
- command: {
- universalIdentifier: 'd0e1f2a3-b4c5-6789-defa-012345678902',
- label: 'Bulk Export',
- availabilityType: 'RECORD_SELECTION',
- conditionalAvailabilityExpression: numberOfSelectedRecords > 0,
- },
+});
+```
+
+レコードの選択に制限された[コマンドメニューアイテム](/l/ja/developers/extend/apps/layout/command-menu-items)として表示します:
+
+```ts src/command-menu-items/bulk-export.command-menu-item.ts
+import { defineCommandMenuItem } from 'twenty-sdk/define';
+
+export default defineCommandMenuItem({
+ universalIdentifier: 'd0e1f2a3-b4c5-6789-defa-012345678902',
+ label: 'Bulk Export',
+ availabilityType: 'RECORD_SELECTION',
+ frontComponentUniversalIdentifier: 'd0e1f2a3-b4c5-6789-defa-012345678901',
});
```
diff --git a/packages/twenty-docs/l/ja/developers/extend/apps/layout/navigation-menu-items.mdx b/packages/twenty-docs/l/ja/developers/extend/apps/layout/navigation-menu-items.mdx
index 0f9254ff54..acbb9e16f0 100644
--- a/packages/twenty-docs/l/ja/developers/extend/apps/layout/navigation-menu-items.mdx
+++ b/packages/twenty-docs/l/ja/developers/extend/apps/layout/navigation-menu-items.mdx
@@ -35,6 +35,8 @@ export default defineNavigationMenuItem({
* `position` はサイドバーでの表示順を制御します。
+* enum には、ユーザーが作成したレコードのお気に入りを内部的に扱うために使用される `NavigationMenuItemType.RECORD` も含まれています。これはアプリのマニフェストからは使用できません(レコードを参照するフィールドが存在しません)。
+
* `icon` と `color` は任意で、エントリの見た目をカスタマイズします。
* `folderUniversalIdentifier` は、任意の項目で利用でき、その項目を `FOLDER` タイプの親の内側にネストするために使用します。
diff --git a/packages/twenty-docs/l/ja/developers/extend/apps/layout/views.mdx b/packages/twenty-docs/l/ja/developers/extend/apps/layout/views.mdx
index c096536190..cf976eef66 100644
--- a/packages/twenty-docs/l/ja/developers/extend/apps/layout/views.mdx
+++ b/packages/twenty-docs/l/ja/developers/extend/apps/layout/views.mdx
@@ -33,17 +33,32 @@ export default defineView({
## 主なポイント
* `objectUniversalIdentifier` は、このビューを適用するオブジェクトを指定します。 定義したカスタムオブジェクトでも、Twenty の標準オブジェクトでも可能です。
-* `key` はビューの種類を決定します。`ViewKey.INDEX` は、そのオブジェクトのメインのリストビューです。
+* `key: ViewKey.INDEX` は、そのビューがオブジェクトのメイン一覧ビュー(`OBJECT` ナビゲーション項目を開いたときに表示されるビュー)であることを示します。
* `fields` は、どの列をどの順序で表示するかを制御します。 各フィールドは `fieldMetadataUniversalIdentifier` を参照します。
-* さらに高度な構成のために、`filters`、`filterGroups`、`groups`、`fieldGroups` も定義できます。
+* さらに高度な構成のために、`filters`、`filterGroups`、`sorts`、`groups`、`fieldGroups` も定義できます。
* 同じオブジェクトに複数のビューがある場合、`position` が表示順を制御します。
+## オプションのプロパティ
+
+| プロパティ | 値 | 説明 |
+| ----------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- |
+| `type` | `ViewType.TABLE` (デフォルト), `ViewType.KANBAN`, `ViewType.CALENDAR` | レコードのレイアウト方法。 (`FIELDS_WIDGET` / `TABLE_WIDGET` も存在しますが、ページレイアウトウィジェットによって内部的に使用されます。) |
+| `visibility` | `ViewVisibility.WORKSPACE` (デフォルト), `ViewVisibility.UNLISTED` | ビューがワークスペース全体で一覧表示されるか、ピッカーから非表示にするか。 |
+| `openRecordIn` | `ViewOpenRecordIn.SIDE_PANEL` (デフォルト), `ViewOpenRecordIn.RECORD_PAGE` | レコードをクリックしたときに、どこで開くか。 |
+| `sorts` | `{ fieldMetadataUniversalIdentifier, direction: ViewSortDirection.ASC \| DESC }[]` | デフォルトのソート順。 |
+| `isCompact` | `boolean` | 行をコンパクトに表示します。 |
+| `mainGroupByFieldMetadataUniversalIdentifier` + `shouldHideEmptyGroups` | — | レコードをフィールドでグループ化します(例: かんばんのカラム)。 |
+| `kanbanAggregateOperation`, `kanbanAggregateOperationFieldMetadataUniversalIdentifier`, `kanbanColumnWidth` | `AggregateOperations.*` | かんばんカラムの集計とサイズ設定。 |
+| `calendarLayout`, `calendarFieldMetadataUniversalIdentifier` | `ViewCalendarLayout.DAY` / `WEEK` / `MONTH` | カレンダービュー: レイアウトと、レコードの位置を決める日付フィールド。 |
+
+上記のすべての enum は `twenty-sdk/define` からエクスポートされています。
+
## フィルター
ビューには、あらかじめフィルターを適用した状態で提供できます。 各フィルターには 3 つの要素があります: フィルタリング対象の**フィールド**、**オペランド**(どのように比較するか)、**値**(何と比較するか)。 この 3 つがすべてそろっている必要があります — フィールドの型に適用できないオペランドを使用すると、同期時に拒否されます。
```ts
-import { ViewFilterOperand } from 'twenty-shared/types';
+import { ViewFilterOperand } from 'twenty-sdk/define';
filters: [
{
diff --git a/packages/twenty-docs/l/ja/developers/extend/apps/logic/logic-functions.mdx b/packages/twenty-docs/l/ja/developers/extend/apps/logic/logic-functions.mdx
index d6ffe986c6..1959e260b1 100644
--- a/packages/twenty-docs/l/ja/developers/extend/apps/logic/logic-functions.mdx
+++ b/packages/twenty-docs/l/ja/developers/extend/apps/logic/logic-functions.mdx
@@ -51,8 +51,12 @@ export default defineLogicFunction({
```
利用可能なトリガーの種類:
-* **httpRoute**:`/s/` エンドポイント配下で、HTTP のパスとメソッドで関数を公開します:
-> 例:`path: '/post-card/create'` は `https://your-twenty-server.com/s/post-card/create` で呼び出せます
+* **httpRoute**: ワークスペースの **関数 ベース URL** の HTTP パスとメソッドにあなたの関数を公開します。値 Twenty_FUNCTIONS_URL\` (Twenty Cloud 上) ワークスペースごとの専用ドメイン:
+> 例:`path: '/post-card/create'` は `https://your-workspace.withtwenty.com/post-card/create` で呼び出せます
+
+
+レガシーの `/s/` prefix route (`https://your-20-server.com/s/post-card/create`)は\*\*Twenty Cloudで非推奨になっており、**2026-07-24**で無効になります。 分離された関数ドメインを設定しない自己ホストおよびローカルインスタンスでも使用できます — 設定時は `TWENTY_FUNCTIONS_URL` を使用してください。 そして、 `\/s/\` に戻ります。
+
(ヘッドレスの)フロントコンポーネントからルートトリガー型ロジック関数を呼び出す方法については、[ロジック関数を呼び出す](/l/ja/developers/extend/apps/layout/front-components#calling-a-logic-function)を参照してください。
diff --git a/packages/twenty-docs/l/ja/developers/extend/apps/logic/overview.mdx b/packages/twenty-docs/l/ja/developers/extend/apps/logic/overview.mdx
index 11b4f2c6da..c06491f9eb 100644
--- a/packages/twenty-docs/l/ja/developers/extend/apps/logic/overview.mdx
+++ b/packages/twenty-docs/l/ja/developers/extend/apps/logic/overview.mdx
@@ -40,13 +40,13 @@ Twenty アプリの **ロジックレイヤー** は、*実行される* コー
ロジック関数は 1 つ以上のトリガーを選択します。以下の各項目は、`defineLogicFunction()` 上の個別のフィールドです。
-| トリガー | 実行タイミング | 設定 |
-| --------------- | --------------------------------------------------- | ------------------------------- |
-| **HTTP ルート** | リクエストが `/s/\` エンドポイントに到達したとき | `httpRouteTriggerSettings` |
-| **クロン** | CRON 式が一致したとき | `cronTriggerSettings` |
-| **データベースイベント** | ワークスペースのレコードが作成、更新、または削除されたとき | `databaseEventTriggerSettings` |
-| **AI ツール** | Twenty の AI 機能が関数を呼び出すことを決定したとき | `toolTriggerSettings` |
-| **ワークフローアクション** | ワークフローステップが関数を呼び出したとき | `workflowActionTriggerSettings` |
+| トリガー | 実行タイミング | 設定 |
+| --------------- | ------------------------------- | ------------------------------- |
+| **HTTP ルート** | リクエストがあなたの関数の公開 URL に一致しました | `httpRouteTriggerSettings` |
+| **クロン** | CRON 式が一致したとき | `cronTriggerSettings` |
+| **データベースイベント** | ワークスペースのレコードが作成、更新、または削除されたとき | `databaseEventTriggerSettings` |
+| **AI ツール** | Twenty の AI 機能が関数を呼び出すことを決定したとき | `toolTriggerSettings` |
+| **ワークフローアクション** | ワークフローステップが関数を呼び出したとき | `workflowActionTriggerSettings` |
関数は分離された Node.js プロセス内でサンドボックス実行され、[`defineApplication()`](/l/ja/developers/extend/apps/config/application) で宣言されたロールにスコープされた型付き API クライアントを通じてワークスペースにアクセスします。
diff --git a/packages/twenty-docs/l/ja/developers/extend/apps/operations/cli.mdx b/packages/twenty-docs/l/ja/developers/extend/apps/operations/cli.mdx
index 95e9e4f903..4cac830ad4 100644
--- a/packages/twenty-docs/l/ja/developers/extend/apps/operations/cli.mdx
+++ b/packages/twenty-docs/l/ja/developers/extend/apps/operations/cli.mdx
@@ -4,7 +4,25 @@ description: 関数の実行、ログのストリーミング、アプリのイ
icon: terminal
---
-`dev`、`dev:build`、`dev:add`、`dev:typecheck` 以外にも、`yarn twenty` CLI には関数の実行、ログの表示、アプリのインストール管理のためのコマンドがあります。
+`yarn twenty` CLI は、アプリ関連のすべてを操作するためのインターフェースです。 コマンド一覧:
+
+| コマンド | 機能 | 以下でドキュメント化 |
+| ----------------------------------------------- | -------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- |
+| `dev` | ソースファイルを監視し、変更をライブ同期します | [クイックスタート](/l/ja/developers/extend/apps/getting-started/quick-start) |
+| `plan` | メタデータの変更を適用せずにプレビュー | [Syncing & recovery](/l/ja/developers/extend/apps/operations/sync-and-recovery#previewing-changes-plan) |
+| `apply` | プランを表示した後にメタデータの変更を適用 | [Syncing & recovery](/l/ja/developers/extend/apps/operations/sync-and-recovery) |
+| `dev:build` | アプリをコンパイルし、API クライアントを生成します(`.tgz` にパッケージするには `--tarball` を使用) | [公開](/l/ja/developers/extend/apps/operations/publishing) |
+| `dev:typecheck` | TypeScript の型チェックを実行 | [テスト](/l/ja/developers/extend/apps/operations/testing) |
+| `dev:add` | 新しいエンティティをスキャフォールディング | [スキャフォールディング](/l/ja/developers/extend/apps/getting-started/scaffolding) |
+| `dev:generate-client` | 型付き API クライアントを再生成 | このページ |
+| `dev:function:exec` / `dev:function:logs` | 関数を実行し、そのログをストリーミング | このページ |
+| `dev:translations-extract` | 翻訳可能な文字列を `locales/` カタログに抽出 | [翻訳](/l/ja/developers/extend/apps/translations/overview) |
+| `dev:catalog-sync` | マーケットプレイスのカタログ同期をトリガー | [公開](/l/ja/developers/extend/apps/operations/publishing#how-marketplace-discovery-works) |
+| `app:publish` / `app:install` / `app:uninstall` | リリースライフサイクル | [公開](/l/ja/developers/extend/apps/operations/publishing) とこのページ |
+| `docker:*` | ローカルの Twenty サーバーコンテナを管理 | [ローカルサーバー](/l/ja/developers/extend/apps/getting-started/local-server) |
+| `remote:*` | サーバー接続を管理 | このページ |
+
+すべてのコマンドは、デフォルトではない特定のリモートを対象にするために `-r, --remote \` を受け付けます。
## 関数の実行(`yarn twenty dev:function:exec`)
@@ -20,8 +38,9 @@ yarn twenty dev:function:exec -u e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf
# Pass a JSON payload
yarn twenty dev:function:exec -n create-new-post-card -p '{"name": "Hello"}'
-# Execute the post-install function
+# Execute the install hooks
yarn twenty dev:function:exec --postInstall
+yarn twenty dev:function:exec --preInstall
```
## 関数ログの表示(`yarn twenty dev:function:logs`)
@@ -100,6 +119,12 @@ yarn twenty remote:list
# Set the active remote
yarn twenty remote:use
+
+# Check that the active remote's authentication is still valid
+yarn twenty remote:status
+
+# Remove a remote
+yarn twenty remote:remove
```
認証情報は `~/.twenty/config.json` に保存されます。
diff --git a/packages/twenty-docs/l/ja/developers/extend/apps/operations/publishing.mdx b/packages/twenty-docs/l/ja/developers/extend/apps/operations/publishing.mdx
index 17c9251015..b34b3d24d5 100644
--- a/packages/twenty-docs/l/ja/developers/extend/apps/operations/publishing.mdx
+++ b/packages/twenty-docs/l/ja/developers/extend/apps/operations/publishing.mdx
@@ -229,7 +229,7 @@ yarn twenty dev:catalog-sync
# yarn twenty dev:catalog-sync --remote production
```
-マーケットプレイスに表示されるメタデータは、`defineApplication()` の設定に由来します。`displayName`、`description`、`author`、`category`、`logoUrl`、`screenshots`、`aboutDescription`、`websiteUrl`、`termsUrl` などのフィールドです。
+マーケットプレイスに表示されるメタデータは、`defineApplication()` 設定から取得されます。上記の [Marketplace metadata](#marketplace-metadata) を参照してください。
アプリで`defineApplication()`内に`aboutDescription`が定義されていない場合、マーケットプレイスはnpm上のパッケージの`README.md`を概要ページのコンテンツとして自動的に使用します。 つまり、npm と Twenty のマーケットプレイスの両方に対して、1 つの README を維持できます。 マーケットプレイスで異なる説明文を使用したい場合は、`aboutDescription` を明示的に設定してください。
diff --git a/packages/twenty-docs/l/ja/developers/extend/apps/operations/sync-and-recovery.mdx b/packages/twenty-docs/l/ja/developers/extend/apps/operations/sync-and-recovery.mdx
index 80d59d29a2..2cce4a8f98 100644
--- a/packages/twenty-docs/l/ja/developers/extend/apps/operations/sync-and-recovery.mdx
+++ b/packages/twenty-docs/l/ja/developers/extend/apps/operations/sync-and-recovery.mdx
@@ -15,33 +15,44 @@ icon: compass
| やりたいこと… | コマンド | ノート |
| ------------------------------ | ----------------------------------- | ----------------------------------------------------------------------------------------------------------------- |
| ライブ同期でローカルに反復開発する | `yarn twenty dev` | ファイルを監視し、変更のたびに同期します。 |
-| 1 回だけ同期して終了(CI、スクリプト、フック向け) | `yarn twenty dev --once` | 1 回ビルドして同期し、その後終了します。 |
-| 変更を**適用せずに**プレビュー | `yarn twenty dev --once --dry-run` | 差分を計算して表示しますが、何も書き込みません。 |
+| 1 回だけ同期して終了(CI、スクリプト、フック向け) | `yarn twenty apply` | 1 回ビルドして同期し、その後終了します。 破壊的変更の確認をスキップするには、`--force` を追加します。 |
+| 変更を**適用せずに**プレビュー | `yarn twenty plan` | 差分を計算して表示しますが、何も書き込みません。 |
| ワークスペースからアプリを削除する | `yarn twenty app:uninstall` | プロンプトをスキップするには、`--yes` を追加します。 |
| サーバーに tarball をアップロードする | `yarn twenty app:publish --private` | `package.json` のバージョンが**厳密により高い**必要があります — [Publishing](/l/ja/developers/extend/apps/operations/publishing) を参照してください。 |
| マーケットプレイス(npm)に公開する | `yarn twenty app:publish` | — |
| デプロイ済みバージョンをインストール / アップグレードする | `yarn twenty app:install` | 現在デプロイされているバージョンをインストールします。 |
| ローカルサーバーを消去してクリーンに開始する | `yarn twenty docker:reset` | ローカルデータを**すべて**削除します — 最終手段です。 |
+
+`yarn twenty dev --once` および `yarn twenty dev --once --dry-run` は、`yarn twenty apply` と `yarn twenty plan` の非推奨エイリアスとして依然として動作します。
+
+
### ローカル同期ではバージョンの更新は不要
厳密に増加する `version` のルール(デプロイ時の `VERSION_ALREADY_EXISTS`、インストール時の `APP_ALREADY_INSTALLED` / `CANNOT_DOWNGRADE_APPLICATION`)は、リリースパスである **`app:publish` / `app:install`** に適用されます。 `yarn twenty dev` はマニフェストをその場で同期し、バージョン変更を要求することはないため、反復するのに `package.json` を触る必要はありません。 ローカルの変更をテストするためにバージョンを上げている場合は、開発ループが必要なところでリリース経路を使ってしまっています。
## 同期出力の読み方
-各同期では、適用された(`--dry-run` の場合は適用されるはずだった)メタデータ変更が出力されます。
+各同期では、適用した(`plan` の場合は適用される)メタデータの変更が出力されます。Terraform と同様に、エンティティごとにその属性を含むブロックが 1 つずつ表示され、その後にサマリー行が続きます。
```text filename="Terminal"
-Metadata changes: 2 created, 1 updated, 1 deleted
- created objectMetadata rocket
- created fieldMetadata timelineActivities
- updated fieldMetadata launchedAt
- deleted pageLayout legacyTab
-✓ Synced
+ # objectMetadata "rocket" will be created
+ + icon = "IconRocket"
+ + labelSingular = "Rocket"
+ + ...
+
+ # fieldMetadata "launchedAt" will be updated
+ ~ isNullable = false -> true
+
+Plan: 2 to add, 1 to change, 1 to destroy.
+
+✓ Synced My App (4 files)
```
これは最初の診断手段です。どのオブジェクト、フィールド、レイアウトが変更されたかを正確に示すので、UI を確認する前に、同期が想定どおりに動作したかを確認できます。
+破壊的変更(`to destroy`)は、何を削除するかとあわせて一覧表示され(例: `objectMetadata "auditNote" — drops the table and all its rows`)、対話的な確認、またはスクリプト内での `--force` が必要です。
+
同期が単一のエンティティで失敗した場合、エラーには問題のエンティティとその `universalIdentifier` が、次のように示されます。
```text
@@ -50,39 +61,42 @@ Migration action 'create' for 'fieldMetadata' (universalIdentifier: 2020...4337)
その識別子を使って、推測で衝突元を探すのではなく、マニフェスト内(必要であればワークスペース内)のエンティティを特定してください。
-## 変更内容のプレビュー(ドライラン)
+## 変更内容のプレビュー(プラン)
-`yarn twenty dev --once --dry-run` はマニフェストをビルドし、サーバーにマイグレーションプランを問い合わせ、その内容を**何も適用せずに**表示します。 コミットする前に「この同期は何を変更するか?」という問いに安全に答える方法です。
+`yarn twenty plan` はマニフェストをビルドし、サーバーにマイグレーションプランを問い合わせ、その内容を **何も適用せずに** 表示します。 コミットする前に「この同期は何を変更するか?」という問いに安全に答える方法です。
```bash filename="Terminal"
-yarn twenty dev --once --dry-run
+yarn twenty plan
```
```text filename="Terminal"
Building manifest...
-Computing metadata diff (dry run, nothing will be applied)...
-Metadata changes: 1 created, 1 updated
- created fieldMetadata timelineActivities
- updated objectMetadata rocket
-✓ Dry run complete for My App — no changes were applied
+Computing metadata plan (read-only, nothing will be applied)...
+
+ # fieldMetadata "timelineActivities" will be created
+ + ...
+
+Plan: 1 to add, 1 to change, 0 to destroy.
+
+✓ Plan complete for My App — no changes were applied
```
-ドライランでは次のことが行われます。
+プランの例:
* **何も書き込みません** — メタデータマイグレーション、アプリケーションレコードの更新、デフォルトのロール / タブの変更、API クライアントの生成は一切行いません。
* 実際の同期が適用するのと**同じ差分**を返すため、作成 / 更新 / 削除されるエンティティを事前に確認できます。
* リスクの高い変更の前や、AI 生成の変更をレビューするとき、または予期せぬ変更が行われそうな場合にスクリプトを失敗させたいときなどに有用です。
-ドライランでは**メタデータ**の変更のみをプレビューします。また、アプリが少なくとも一度は同期されている(ワークスペース側がその存在を知っている)必要があります。 一度も同期されていないアプリに対して実行すると、サーバーはそのアプリがインストールされていないと報告します — まず一度 `yarn twenty dev` を実行してください。
+プランでは **メタデータ** の変更のみがプレビューされます。また、アプリが少なくとも一度は同期されている(ワークスペース側がその存在を知っている)必要があります。 一度も同期されていないアプリに対して実行すると、サーバーはそのアプリがインストールされていないと報告します — まず一度 `yarn twenty dev` を実行してください。
## リカバリーラダー
ローカルのメタデータが正しくないように見える場合は、次の順番でエスカレートし、問題が解消したところで止めてください。 各ステップは前のものよりも影響が大きくなります。
-1. **再同期。** `yarn twenty dev --once` を再度実行します。 同期はべき等であり、クリーンなマニフェストを再実行しても安全で、多くの場合は一時的な不具合が解消されます。
-2. **プランをプレビュー。** `yarn twenty dev --once --dry-run` を実行して、次の同期が何を変更しようとしているのかを、適用せずに正確に確認します。
+1. **再同期。** `yarn twenty apply` を再度実行します。 同期はべき等であり、クリーンなマニフェストを再実行しても安全で、多くの場合は一時的な不具合が解消されます。
+2. **プランをプレビュー。** `yarn twenty plan` を実行して、次の同期が何を変更しようとしているのかを、適用せずに正確に確認します。
3. **名前付きエラーを読む。** 同期が失敗した場合は、メッセージ内のメタデータタイプと `universalIdentifier`(上記参照)を確認し、そのエンティティをマニフェスト内で特定します。 コンフリクトは、重複または再利用された識別子を指していることがほとんどです。
4. **アンインストールして再インストール。** `yarn twenty app:uninstall` を実行し、その後再度同期します(`yarn twenty dev`)。 これにより、ワークスペースの残りを維持したまま、アプリのメタデータをクリーンな状態から再構築します。
5. **フルリセット(最後の手段)。** `yarn twenty docker:reset` を実行し、その後再シードと再同期を行います。
diff --git a/packages/twenty-docs/l/ja/developers/extend/apps/operations/testing.mdx b/packages/twenty-docs/l/ja/developers/extend/apps/operations/testing.mdx
index b3fb527f1e..2ef6696d87 100644
--- a/packages/twenty-docs/l/ja/developers/extend/apps/operations/testing.mdx
+++ b/packages/twenty-docs/l/ja/developers/extend/apps/operations/testing.mdx
@@ -78,6 +78,13 @@ yarn add -D vitest vite-tsconfig-paths
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 ?? '';
+
+// 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({
@@ -88,66 +95,74 @@ export default defineConfig({
test: {
testTimeout: 120_000,
hookTimeout: 120_000,
+ fileParallelism: false,
include: ['src/**/*.integration-test.ts'],
- setupFiles: ['src/__tests__/setup-test.ts'],
+ globalSetup: ['src/__tests__/global-setup.ts'],
env: {
- TWENTY_API_URL: 'http://localhost:2020',
- TWENTY_API_KEY: 'your-api-key',
+ TWENTY_API_URL,
+ TWENTY_API_KEY,
},
},
});
```
-テストを実行する前にサーバーに到達可能であることを検証するセットアップファイルを作成します:
+サーバーに到達可能であることを検証し、SDK 用のテスト用コンフィグ(`~/.twenty/config.test.json`)を書き込み、テストが実行される前にアプリを同期するグローバルセットアップファイルを作成します。
-```ts src/__tests__/setup-test.ts
+```ts src/__tests__/global-setup.ts
import * as fs from 'fs';
import * as os from 'os';
import * as path from 'path';
-import { beforeAll } from 'vitest';
-const TWENTY_API_URL = process.env.TWENTY_API_URL ?? 'http://localhost:2020';
-const TEST_CONFIG_DIR = path.join(os.tmpdir(), '.twenty-sdk-test');
+import { appDevOnce, appUninstall } from 'twenty-sdk/cli';
+
+const APP_PATH = process.cwd();
+const CONFIG_DIR = path.join(os.homedir(), '.twenty');
+
+export async function setup() {
+ const apiUrl = process.env.TWENTY_API_URL!;
+ const apiKey = process.env.TWENTY_API_KEY!;
-beforeAll(async () => {
// Verify the server is running
- const response = await fetch(`${TWENTY_API_URL}/healthz`);
-
+ const response = await fetch(`${apiUrl}/healthz`);
if (!response.ok) {
- throw new Error(
- `Twenty server is not reachable at ${TWENTY_API_URL}. ` +
- 'Start the server before running integration tests.',
- );
+ throw new Error(`Twenty server is not reachable at ${apiUrl}.`);
}
- // Write a temporary config for the SDK
- fs.mkdirSync(TEST_CONFIG_DIR, { recursive: true });
-
+ // Write the SDK's test config (the CLI reads config.test.json when NODE_ENV=test)
+ fs.mkdirSync(CONFIG_DIR, { recursive: true });
fs.writeFileSync(
- path.join(TEST_CONFIG_DIR, 'config.json'),
+ path.join(CONFIG_DIR, 'config.test.json'),
JSON.stringify({
- remotes: {
- local: {
- apiUrl: process.env.TWENTY_API_URL,
- apiKey: process.env.TWENTY_API_KEY,
- },
- },
+ remotes: { local: { apiUrl, apiKey } },
defaultRemote: 'local',
}, null, 2),
);
-});
+
+ // Start from a clean slate, then sync the app
+ await appUninstall({ appPath: APP_PATH }).catch(() => {});
+
+ const result = await appDevOnce({ appPath: APP_PATH });
+ if (!result.success) {
+ throw new Error(`Dev sync failed: ${result.error?.message}`);
+ }
+}
+
+export async function teardown() {
+ await appUninstall({ appPath: APP_PATH });
+}
```
## プログラム用 SDK API
`twenty-sdk/cli` サブパスは、テストコードから直接呼び出せる関数をエクスポートします:
-| 関数 | 説明 |
-| -------------- | ------------------------------- |
-| `appBuild` | アプリをビルドし、必要に応じて tarball にパッケージ化 |
-| `appDeploy` | tarball をサーバーにアップロード |
-| `appInstall` | アクティブなワークスペースにアプリをインストール |
-| `appUninstall` | アクティブなワークスペースからアプリをアンインストール |
+| 関数 | 説明 |
+| -------------- | -------------------------------------------- |
+| `appBuild` | アプリをビルドし、必要に応じて tarball にパッケージ化 |
+| `appDeploy` | tarball をサーバーにアップロード |
+| `appDevOnce` | アプリを 1 回ビルドして同期します(`yarn twenty apply` と同じ)。 |
+| `appInstall` | アクティブなワークスペースにアプリをインストール |
+| `appUninstall` | アクティブなワークスペースからアプリをアンインストール |
各関数は、`success: boolean` と `data` または `error` のいずれかを含む結果オブジェクトを返します。
@@ -238,64 +253,10 @@ yarn test:watch
yarn twenty dev:typecheck
```
-これは `tsc --noEmit` を実行し、型エラーを報告します。
+これは、あなたのアプリの `tsconfig.json` に対して `tsc --noEmit` を実行し、型エラーを報告します。 スキャフォルドされたアプリには、テストファイル(`tsconfig.spec.json`)も対象とする `yarn typecheck` スクリプトも同梱されています。
## GitHub Actions による CI
-スキャフォルダーは、すぐに使える GitHub Actions ワークフローを `.github/workflows/ci.yml` に生成します。 `main` へのプッシュやプルリクエストのたびに、統合テストを自動実行します。
+スキャフォルダーは、すぐに使えるワークフローを `.github/workflows/ci.yml` に生成します。 `main` へのすべてのプッシュおよびすべてのプルリクエスト時に、ランナー内で一時的な Twenty サーバーを起動(`twentyhq/twenty/.github/actions/spawn-twenty-app-dev-test` アクション経由)し、そのサーバーを指すように `TWENTY_API_URL` / `TWENTY_API_KEY` を設定した上で、`yarn lint`、`yarn typecheck`、`yarn test:unit`、`yarn test` を実行します。 シークレットは一切不要で、ワークフローの先頭にある `TWENTY_VERSION` 環境変数を通じてサーバーバージョンを固定できます。
-ワークフローの内容:
-
-1. コードをチェックアウトする
-2. `twentyhq/twenty/.github/actions/spawn-twenty-docker-image` アクションを使って一時的な Twenty サーバーを起動する
-3. `yarn install --immutable` で依存関係をインストールする
-4. アクションの出力から注入された `TWENTY_API_URL` と `TWENTY_API_KEY` を用いて `yarn test` を実行する
-
-```yaml .github/workflows/ci.yml
-name: CI
-
-on:
- push:
- branches:
- - main
- pull_request: {}
-
-env:
- TWENTY_VERSION: latest
-
-jobs:
- test:
- runs-on: ubuntu-latest
- steps:
- - name: Checkout
- uses: actions/checkout@v4
-
- - name: Spawn Twenty instance
- id: twenty
- uses: twentyhq/twenty/.github/actions/spawn-twenty-docker-image@main
- with:
- twenty-version: ${{ env.TWENTY_VERSION }}
- github-token: ${{ secrets.GITHUB_TOKEN }}
-
- - name: Enable Corepack
- run: corepack enable
-
- - name: Setup Node.js
- uses: actions/setup-node@v4
- with:
- node-version-file: '.nvmrc'
- cache: 'yarn'
-
- - name: Install dependencies
- run: yarn install --immutable
-
- - name: Run integration tests
- run: yarn test
- env:
- TWENTY_API_URL: ${{ steps.twenty.outputs.server-url }}
- TWENTY_API_KEY: ${{ steps.twenty.outputs.access-token }}
-```
-
-シークレットを設定する必要はありません。`spawn-twenty-docker-image` アクションがランナー内で一時的な Twenty サーバーを直接起動し、接続情報を出力します。 `GITHUB_TOKEN` シークレットは GitHub によって自動的に提供されます。
-
-`latest` の代わりに特定の Twenty バージョンを固定するには、ワークフローの先頭にある `TWENTY_VERSION` 環境変数を変更します。
+スキャフォルドされた 2 つのワークフロー(`ci.yml` と `cd.yml` デプロイパイプライン)の詳細な手順については、[Publishing → Automated CI/CD](/l/ja/developers/extend/apps/operations/publishing#automated-cicd-scaffolded-workflows) を参照してください。
diff --git a/packages/twenty-docs/l/ja/developers/extend/apps/tutorials/document-generator/building-the-ui.mdx b/packages/twenty-docs/l/ja/developers/extend/apps/tutorials/document-generator/building-the-ui.mdx
index 9cf4588b6d..431ebb3b11 100644
--- a/packages/twenty-docs/l/ja/developers/extend/apps/tutorials/document-generator/building-the-ui.mdx
+++ b/packages/twenty-docs/l/ja/developers/extend/apps/tutorials/document-generator/building-the-ui.mdx
@@ -88,9 +88,11 @@ const GenerateDocumentForm = () => {
}, []);
const generate = async () => {
- const apiBaseUrl = process.env.TWENTY_API_URL;
+ // Prefer the injected functions URL; fall back to the legacy /s prefix (self-hosted/local)
+ const functionsBaseUrl =
+ process.env.TWENTY_FUNCTIONS_URL || `${process.env.TWENTY_API_URL}/s`;
const token = process.env.TWENTY_APP_ACCESS_TOKEN ?? process.env.TWENTY_API_KEY;
- const res = await fetch(`${apiBaseUrl}/s/documents/generate`, {
+ const res = await fetch(`${functionsBaseUrl}/documents/generate`, {
method: 'POST',
headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` },
body: JSON.stringify({ templateId, recordId }),
@@ -181,7 +183,9 @@ const DocumentViewer = () => {
const recordId = useFrontComponentExecutionContext((c) => c.recordId ?? null);
// ...load { content, file } for recordId, then derive the links:
const pdfUrl = document.file?.[0]?.url;
- const webUrl = `${process.env.TWENTY_API_URL ?? ''}/s/documents/view?id=${recordId}`;
+ const functionsBaseUrl =
+ process.env.TWENTY_FUNCTIONS_URL || `${process.env.TWENTY_API_URL ?? ''}/s`;
+ const webUrl = `${functionsBaseUrl}/documents/view?id=${recordId}`;
// Render the template body, plus quick links to the web page and the PDF.
// Links open in a new tab so they don't navigate the embedded component.
diff --git a/packages/twenty-docs/l/ja/developers/extend/apps/tutorials/document-generator/http-routes.mdx b/packages/twenty-docs/l/ja/developers/extend/apps/tutorials/document-generator/http-routes.mdx
index 83e8400e2f..37acb5e320 100644
--- a/packages/twenty-docs/l/ja/developers/extend/apps/tutorials/document-generator/http-routes.mdx
+++ b/packages/twenty-docs/l/ja/developers/extend/apps/tutorials/document-generator/http-routes.mdx
@@ -9,8 +9,14 @@ description: HTTP 経由で関数をトリガーし、ドキュメントを Web
* ドキュメントを生成するUI呼び出しの **POST** エンドポイントと
* ドキュメントを印刷可能なウェブページとしてレンダリングするパブリック**GET** エンドポイント。
-どちらも `httpRouteTriggerSettings` を使用します。 アプリのルートはあなたの
-20のサーバーの`/s`の下で提供されます(例:`http://localhost:2020/s/documents/generate`)。
+どちらも `httpRouteTriggerSettings` を使用します。 ローカル開発サーバーでは、アプリのルートは `/s` プレフィックスの下で提供されます(例:`http://localhost:2020/s/documents/generate`)。
+
+
+Twenty Cloud では、ルートはワークスペースの専用関数ドメイン
+で提供されます。URL Twenty は `TWENTY_FUNCTIONS_URL` として挿入され、`/s` プレフィックスはありません。 `/s`
+プレフィックスは非推奨で、自己ホストおよびローカルインスタンスのみが使用できます。
+[ロジック関数の呼び出し](/l/ja/developers/extend/apps/layout/front-components#calling-a-logic-function)を参照してください。
+
## POST route — オンデマンドで生成
diff --git a/packages/twenty-docs/l/ja/developers/extend/apps/tutorials/document-generator/publishing.mdx b/packages/twenty-docs/l/ja/developers/extend/apps/tutorials/document-generator/publishing.mdx
index c61c5f18a3..4a14214df9 100644
--- a/packages/twenty-docs/l/ja/developers/extend/apps/tutorials/document-generator/publishing.mdx
+++ b/packages/twenty-docs/l/ja/developers/extend/apps/tutorials/document-generator/publishing.mdx
@@ -76,11 +76,11 @@ CI と同じゲートを実行します。
yarn lint # oxlint
yarn typecheck # tsgo
yarn test:unit # unit tests
-yarn twenty dev --once --dry-run # preview the metadata diff
+yarn twenty plan # preview the metadata diff
```
-ドライランは、それを適用せずにサーバー上で何が変更されるかを正確にプリントします —
-良い最終正常性チェックです。
+このプランは、適用せずにサーバー上で何が変わるかを正確に出力します。
+最終確認として有用です。
[Testing](/l/ja/developers/extend/apps/operations/testing) と
[Syncing & recovery](/l/ja/developers/extend/apps/operations/sync-and-recovery) を参照してください。
diff --git a/packages/twenty-docs/l/ko/developers/extend/apps/config/install-hooks.mdx b/packages/twenty-docs/l/ko/developers/extend/apps/config/install-hooks.mdx
index 238b580561..522665b560 100644
--- a/packages/twenty-docs/l/ko/developers/extend/apps/config/install-hooks.mdx
+++ b/packages/twenty-docs/l/ko/developers/extend/apps/config/install-hooks.mdx
@@ -4,9 +4,9 @@ description: 설치 전에나 후에 로직을 실행하여 시드 데이터를
icon: wrench
---
-설치 훅은 설치 또는 업그레이드 라이프사이클 동안 실행되는 특수한 로직 함수입니다. 이들은 일반 [로직 함수](/l/ko/developers/extend/apps/logic/logic-functions)와 동일한 핸들러 런타임을 공유하고 `InstallPayload`를 받지만, 자체 정의 함수인 `definePostInstallLogicFunction()` 및 `definePreInstallLogicFunction()`으로 선언되며, 일반 트리거 모델(HTTP, cron, 데이터베이스 이벤트) 외부에서 동작합니다.
+설치 훅은 설치 또는 업그레이드 라이프사이클 동안 실행되는 특수한 로직 함수입니다. 이들은 일반 [로직 함수](/l/ko/developers/extend/apps/logic/logic-functions)와 동일한 핸들러 런타임을 공유하고 `InstallPayload`를 받습니다(`{ previousVersion?: string; newVersion: string }` — 새로운 설치에서는 `previousVersion`이 `undefined`임). 하지만 자체 define 함수로 선언되며, 일반 트리거 모델(HTTP, cron, 데이터베이스 이벤트) 외부에서 동작합니다.
-각 앱은 **최대 하나의 pre-install** 함수와 **최대 하나의 post-install** 함수만 정의할 수 있습니다. 둘 이상이 감지되면 매니페스트 빌드에서 오류가 발생합니다.
+각 앱은 **최대 하나의 pre-install** 함수와 **최대 하나의 post-install** 함수만 정의할 수 있습니다. 둘 중 하나가 둘 이상 감지되면 매니페스트 빌드에서 오류가 발생합니다.
```
┌─────────────────────────────────────────────────────────────┐
@@ -19,111 +19,59 @@ icon: wrench
└─────────────────────────────────────────────────────────────┘
```
-
-
+## 한눈에 보기
-설치 후 함수는 워크스페이스에 앱 설치가 완료된 뒤 자동으로 실행되는 로직 함수입니다. 서버는 앱의 메타데이터가 동기화되고 SDK 클라이언트가 생성된 **이후** 이를 실행하므로, 워크스페이스는 완전히 사용할 준비가 되었고 새 스키마가 적용된 상태입니다. 일반적인 사용 사례로는 기본 데이터를 시드하는 것, 초기 레코드를 생성하는 것, 워크스페이스 설정을 구성하는 것, 서드파티 서비스에서 리소스를 프로비저닝하는 것 등이 있습니다.
+| | `definePreInstallLogicFunction` | `definePostInstallLogicFunction` |
+| ------- | ------------------------------------------------- | ------------------------------------------------------------------------------ |
+| 실행 | 메타데이터 마이그레이션 이전 — **이전** 스키마와 데이터는 그대로 유지됨 | 마이그레이션 및 SDK 생성 이후 — **새로운** 스키마가 적용됨 |
+| 실행 | 항상 동기식; 설치를 차단함 | 기본적으로 비동기(대기열에 등록, 최대 3회 재시도); `shouldRunSynchronously: true`로 동기 실행을 선택할 수 있음 |
+| 실패 시 | 스키마 변경 이전에 설치가 **중단**됨 | 비동기: 최대 3회까지 재시도됩니다. 동기: 호출자는 `POST_INSTALL_ERROR`를 받음(스키마 변경은 **롤백되지 않습니다**) |
+| 일반적인 사용 | 마이그레이션으로 손실될 데이터를 백업하거나 수정함; 예외를 던져 위험한 업그레이드를 거부 | 기본 데이터 시딩, 워크스페이스 구성, 외부 리소스 등록 |
-```ts src/logic-functions/post-install.ts
-import { definePostInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define';
+**기본 원칙:** 기본값은 post-install로 사용하세요. 마이그레이션 자체가 파괴적이며 이전 상태가 사라지기 전에 이를 가로채야 할 때에만 pre-install을 사용하세요.
-const handler = async (payload: InstallPayload): Promise => {
- console.log('Post install logic function executed successfully!', payload.previousVersion);
-};
+| 원하는 작업... | 사용 |
+| ------------------------------ | --------------------------------------------------- |
+| 데이터 시딩, 워크스페이스 구성, 외부 리소스 등록 | `post-install` |
+| 설치 응답을 차단해서는 안 되는 장시간 작업 | `post-install` (기본 비동기 모드, 워커 재시도 포함) |
+| 설치가 반환된 직후 호출자가 즉시 의존하는 빠른 설정 | `shouldRunSynchronously: true`를 사용하는 `post-install` |
+| 곧 진행될 마이그레이션으로 손실될 데이터를 읽거나 백업 | `pre-install` |
+| 기존 데이터를 손상시킬 업그레이드를 거부 | `pre-install` (핸들러에서 예외를 던짐) |
+| 모든 업그레이드 시 상태 조정 수행 | `shouldRunOnVersionUpgrade: true`가 설정된 어느 훅이든 사용 |
-export default definePostInstallLogicFunction({
- universalIdentifier: 'f7a2b9c1-3d4e-5678-abcd-ef9876543210',
- name: 'post-install',
- description: 'Runs after installation to set up the application.',
- timeoutSeconds: 300,
- shouldRunOnVersionUpgrade: false,
- shouldRunSynchronously: false,
- handler,
-});
-```
+## 두 훅에 공통으로 적용되는 동작
-CLI를 사용하여 언제든지 설치 후 함수를 수동으로 실행할 수도 있습니다:
+* 구성은 트리거 설정을 제외한 `defineLogicFunction` 구성에 `shouldRunOnVersionUpgrade`가 추가된 형태입니다.
+* **실행 시점**: 기본적으로 신규 설치에서만 실행됩니다. 업그레이드 시에도 실행하려면 `shouldRunOnVersionUpgrade: true`를 설정합니다. 업그레이드 경로에 따라 분기하기 위해 `previousVersion` / `newVersion`을 사용합니다.
+* **멱등성이 중요합니다**: 비동기 post-install은 재시도될 수 있고, `shouldRunOnVersionUpgrade`가 켜져 있으면 두 훅 모두 업그레이드 시 다시 실행됩니다.
+* 일반적인 로직 함수 환경(`APPLICATION_ID`, `APP_ACCESS_TOKEN`, `API_URL`)이 주입되므로, 앱의 토큰으로 Twenty API를 호출할 수 있습니다.
+* 훅은 빌드 시 애플리케이션 매니페스트에 자동으로 연결됩니다(`preInstallLogicFunction` / `postInstallLogicFunction`) — [`defineApplication()`](/l/ko/developers/extend/apps/config/application)에서 참조할 것은 없습니다.
+* 기본 `timeoutSeconds`는 데이터 시딩과 같은 더 긴 설정 작업을 허용하기 위해 300으로 설정되어 있습니다.
+* **dev 모드에서는 실행되지 않음**: `yarn twenty dev`는 설치 플로우를 건너뛰고 파일을 직접 동기화하므로, 해당 환경에서는 훅이 전혀 실행되지 않습니다. 대신 수동으로 트리거하세요:
```bash filename="Terminal"
yarn twenty dev:function:exec --postInstall
-```
-
-핵심 요점:
-* 설치 후 함수는 `definePostInstallLogicFunction()`을 사용합니다 — 트리거 설정(`cronTriggerSettings`, `databaseEventTriggerSettings`, `httpRouteTriggerSettings`, `toolTriggerSettings`, `workflowActionTriggerSettings`)을 생략한 특수 변형입니다.
-* 핸들러는 `{ previousVersion?: string; newVersion: string }` 형태의 `InstallPayload`를 받습니다 — `newVersion`은 현재 설치 중인 버전이고, `previousVersion`은 이전에 설치되었던 버전입니다(처음 설치인 경우에는 `undefined`). 이 값들을 사용하여 신규 설치와 업그레이드를 구분하고, 버전별 마이그레이션 로직을 실행하세요.
-* **훅이 실행되는 시점**: 기본적으로 신규 설치에서만 실행됩니다. 이전 버전에서 앱이 업그레이드될 때도 실행되게 하려면 `shouldRunOnVersionUpgrade: true`를 전달하세요. 생략하면 플래그는 기본값 `false`가 되며, 업그레이드 시 훅을 건너뜁니다.
-* **실행 모델 — 기본은 비동기, 동기는 선택적**: `shouldRunSynchronously` 플래그는 설치 후 작업이 *어떤 방식으로* 실행되는지 제어합니다.
- * `shouldRunSynchronously: false` *(기본값)* — 훅은 `retryLimit: 3`와 함께 **메시지 큐에 등록**되며 워커에서 비동기적으로 실행됩니다. 작업이 큐에 등록되는 즉시 설치 응답이 반환되므로, 처리 속도가 느리거나 실패하는 핸들러가 호출자를 차단하지 않습니다. 워커는 최대 세 번까지 재시도합니다. **장시간 실행되는 작업에 사용하세요** — 대규모 데이터셋 시딩, 느린 서드파티 API 호출, 외부 리소스 프로비저닝 등 합리적인 HTTP 응답 시간 창을 초과할 수 있는 모든 작업.
- * `shouldRunSynchronously: true` — 훅이 **설치 플로우 중에 인라인으로** 실행됩니다(설치 전과 동일한 실행기). 핸들러가 완료될 때까지 설치 요청이 블록되고, 예외가 발생하면 설치 호출자는 `POST_INSTALL_ERROR`를 받습니다. 자동 재시도 없음. **응답 전에 반드시 완료되어야 하는 빠른 작업에 사용하세요** — 예: 사용자에게 검증 오류를 표시하거나, 설치 호출이 반환된 직후 클라이언트가 즉시 의존하는 빠른 설정. post-install이 실행될 시점에는 메타데이터 마이그레이션이 이미 적용되었음을 유의하세요. 따라서 동기 모드에서 실패하더라도 스키마 변경이 **롤백되지 않으며**, 오류만 노출됩니다.
-* 핸들러가 멱등적임을 보장하세요. 비동기 모드에서는 큐가 최대 세 번까지 재시도할 수 있습니다. 어떤 모드이든 `shouldRunOnVersionUpgrade: true`인 경우 업그레이드 시 훅이 다시 실행될 수 있습니다.
-* 환경 변수 `APPLICATION_ID`, `APP_ACCESS_TOKEN`, `API_URL`은 핸들러 내부에서 사용할 수 있습니다(다른 로직 함수와 동일). 따라서 앱에 범위가 지정된 애플리케이션 액세스 토큰으로 Twenty API를 호출할 수 있습니다.
-* 애플리케이션당 설치 후 함수는 하나만 허용됩니다. 둘 이상이 감지되면 매니페스트 빌드에서 오류가 발생합니다.
-* 함수의 `universalIdentifier`, `shouldRunOnVersionUpgrade`, `shouldRunSynchronously`는 빌드 중에 애플리케이션 매니페스트의 `postInstallLogicFunction` 필드에 자동으로 첨부됩니다 — 따라서 [`defineApplication()`](/l/ko/developers/extend/apps/config/application)에서 이들을 참조할 필요가 없습니다.
-* 기본 시간 제한은 데이터 시딩과 같은 더 긴 설정 작업을 허용하기 위해 300초(5분)로 설정되어 있습니다.
-* **개발 모드에서 실행되지 않음**: 앱이 로컬로 등록된 경우(`yarn twenty dev`), 서버는 설치 플로우를 완전히 건너뛰고 CLI 워처를 통해 파일을 직접 동기화합니다 — 따라서 `shouldRunSynchronously` 여부와 관계없이 개발 모드에서는 post-install이 절대 실행되지 않습니다. 실행 중인 워크스페이스에 대해 수동으로 트리거하려면 `yarn twenty dev:function:exec --postInstall`을 사용하세요.
-
-
-
-
-pre-install 함수는 설치 중에 자동으로 실행되는 로직 함수로, **워크스페이스 메타데이터 마이그레이션이 적용되기 전에** 실행됩니다. post-install과 동일한 페이로드 형태(`InstallPayload`)를 사용하지만, 설치 플로우에서 더 이른 단계에 위치하여 곧 진행될 마이그레이션이 의존하는 상태를 준비할 수 있습니다 — 일반적인 사용 사례로는 데이터 백업, 새 스키마와의 호환성 검증, 재구조화되거나 삭제될 레코드의 보관 등이 있습니다.
-
-```ts src/logic-functions/pre-install.ts
-import { definePreInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define';
-
-const handler = async (payload: InstallPayload): Promise => {
- console.log('Pre install logic function executed successfully!', payload.previousVersion);
-};
-
-export default definePreInstallLogicFunction({
- universalIdentifier: 'a1b2c3d4-5678-90ab-cdef-1234567890ab',
- name: 'pre-install',
- description: 'Runs before installation to prepare the application.',
- timeoutSeconds: 300,
- shouldRunOnVersionUpgrade: true,
- handler,
-});
-```
-
-CLI를 사용하여 언제든지 설치 전 함수를 수동으로 실행할 수도 있습니다:
-
-```bash filename="Terminal"
yarn twenty dev:function:exec --preInstall
```
-핵심 요점:
-* pre-install 함수는 `definePreInstallLogicFunction()`을 사용합니다 — post-install과 동일한 특수화된 구성을 사용하되, 서로 다른 라이프사이클 슬롯에 연결됩니다.
-* pre-install과 post-install 핸들러는 동일한 `InstallPayload` 타입을 받습니다: `{ previousVersion?: string; newVersion: string }`. 한 번만 임포트하여 두 훅에서 재사용하세요.
-* **훅이 실행되는 시점**: 워크스페이스 메타데이터 마이그레이션(`synchronizeFromManifest`) 직전. 실행에 앞서, 서버는 워크스페이스 메타데이터에 **새로운** 버전의 pre-install 함수를 등록하는 순수 추가식의 "간소화된 동기화"를 수행합니다 — 그 외에는 아무것도 변경하지 않습니다 — 그리고 나서 이를 실행합니다. 이 동기화는 추가 전용이므로, 핸들러가 실행될 때 이전 버전의 객체, 필드, 데이터는 그대로 유지됩니다. 따라서 마이그레이션 이전 상태를 안전하게 읽고 백업할 수 있습니다.
-* **실행 모델**: pre-install은 **동기적으로** 실행되며 **설치를 차단**합니다. 핸들러에서 예외를 던지면, 어떤 스키마 변경도 적용되기 전에 설치가 중단되며 — 워크스페이스는 일관된 상태로 이전 버전에 머무릅니다. 이는 의도된 동작입니다: pre-install은 위험한 업그레이드를 거부할 수 있는 마지막 기회입니다.
-* post-install과 마찬가지로, 애플리케이션당 pre-install 함수는 하나만 허용됩니다. 빌드 중에 애플리케이션 매니페스트의 `preInstallLogicFunction` 아래에 자동으로 연결됩니다.
-* **개발 모드에서 실행되지 않음**: post-install과 동일하게 — 로컬로 등록된 앱은 설치 플로우가 완전히 건너뛰어지므로 `yarn twenty dev` 환경에서 pre-install은 실행되지 않습니다. `yarn twenty dev:function:exec --preInstall`를 사용하여 수동으로 트리거하세요.
+
+
-
-
-
-두 훅 모두 동일한 설치 플로우의 일부이며 같은 `InstallPayload`를 받습니다. 차이점은 워크스페이스 메타데이터 마이그레이션과의 상대적인 실행 **시점**이며, 이에 따라 안전하게 다룰 수 있는 데이터가 달라집니다.
-
-pre-install은 항상 **동기식**입니다(설치를 차단하고 중단할 수 있음). post-install은 **기본적으로 비동기식**입니다 — 워커에 큐잉되고 자동 재시도가 수행됩니다 — 하지만 `shouldRunSynchronously: true`로 동기 실행을 선택할 수 있습니다. 각 모드를 언제 사용할지에 대해서는 위의 `definePostInstallLogicFunction` 아코디언을 참고하세요.
-
-**새로운 스키마의 존재가 필요한 작업에는 `post-install`을 사용하세요.** 일반적인 경우입니다:
-
-* 새로 추가된 객체와 필드를 대상으로 기본 데이터를 시딩(초기 레코드, 기본 보기, 데모 콘텐츠 생성)하는 작업.
-* 앱에 자격 증명이 생겼으므로 서드파티 서비스에 웹훅을 등록하는 작업.
-* 동기화된 메타데이터에 의존하는 설정을 완료하기 위해 자체 API를 호출하는 작업.
-* 모든 업그레이드마다 상태를 조정해야 하는 멱등적인 "존재함을 보장(ensure this exists)" 로직 — `shouldRunOnVersionUpgrade: true`와 함께 사용하세요.
-
-예시 — 설치 후 기본 `PostCard` 레코드를 시딩하기:
+앱 설치가 완료된 후 한 번 실행됩니다: 메타데이터 동기화 완료, SDK 클라이언트 생성, 새로운 스키마 쿼리 가능 상태. 예시 — 신규 설치에서 기본 레코드를 시딩하기:
```ts src/logic-functions/post-install.ts
import { definePostInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define';
-import { createClient } from './generated/client';
+import { CoreApiClient } from 'twenty-client-sdk/core';
const handler = async ({ previousVersion }: InstallPayload): Promise => {
if (previousVersion) return; // fresh installs only
- const client = createClient();
- await client.postCard.create({
- data: { title: 'Welcome to Postcard', content: 'Your first card!' },
+ const client = new CoreApiClient();
+ await client.mutation({
+ createPostCard: {
+ __args: { data: { name: 'Welcome to Postcard', content: 'Your first card!' } },
+ id: true,
+ },
});
};
@@ -133,22 +81,28 @@ export default definePostInstallLogicFunction({
description: 'Seeds a welcome post card after install.',
timeoutSeconds: 300,
shouldRunOnVersionUpgrade: false,
+ shouldRunSynchronously: false,
handler,
});
```
-**마이그레이션으로 인해 기존 데이터가 손실되거나 손상될 우려가 있을 때는 `pre-install`을 사용하세요.** pre-install은 *이전* 스키마에 대해 실행되고 실패 시 업그레이드를 롤백하므로, 위험한 작업에 적합합니다:
+`shouldRunSynchronously` 플래그가 실행 모델을 제어합니다:
-* **곧 삭제되거나 재구조화될 데이터를 백업** — 예: v2에서 필드를 제거하므로, 마이그레이션이 실행되기 전에 해당 값을 다른 필드로 복사하거나 스토리지로 내보내야 하는 경우.
-* **새로운 제약으로 인해 무효화될 레코드를 보관** — 예: 어떤 필드가 `NOT NULL`로 바뀌어 null 값을 가진 행을 먼저 삭제하거나 수정해야 하는 경우.
-* **호환성을 검증하고, 현재 데이터를 깔끔하게 마이그레이션할 수 없는 경우 업그레이드를 거부** — 핸들러에서 예외를 던지면 아무 변경도 적용되지 않은 채 설치가 중단됩니다. 이는 마이그레이션 도중에 비호환성을 발견하는 것보다 더 안전합니다.
-* 연관이 끊어질 수 있는 스키마 변경에 앞서 **데이터 이름 변경 또는 키 재지정**.
+* `false` *(기본값)* — 메시지 큐에 등록되고(`retryLimit: 3`), 워커에 의해 실행됩니다. 작업이 큐에 등록되면 설치 응답이 즉시 반환됩니다. **장시간 작업에 사용** — 대용량 데이터셋 시딩, 지연이 긴 서드파티 API 호출 등.
+* `true` — 설치 플로우 중에 인라인으로 실행됩니다. 설치 요청은 핸들러가 종료될 때까지 블로킹되며, 예외가 발생하면 호출자에게 `POST_INSTALL_ERROR`로 전달됩니다(재시도 없음). **빠르고, 응답 전에 반드시 완료되어야 하는 작업에 사용하세요.** 이 시점에는 이미 마이그레이션이 적용되었으므로, 실패하더라도 스키마 변경은 롤백되지 않고 오류만 노출됩니다.
-예시 — 파괴적인 마이그레이션 전에 레코드 보관하기:
+
+
+
+메타데이터 마이그레이션 이전, **이전** 스키마를 대상으로 실행됩니다 — 마이그레이션으로 손실될 데이터를 백업하거나, 위험한 업그레이드를 거부하기에 적절한 위치입니다. 실행에 앞서, 서버는 순수 추가식의 "간소화된 동기화"를 수행하여 새 버전의 pre-install 함수만 등록하고, 나머지 — 이전 버전의 오브젝트, 필드, 데이터 — 는 핸들러가 실행될 때까지 변경하지 않습니다.
+
+pre-install은 항상 **동기식**이며 설치를 차단합니다. 핸들러에서 예외를 던지면, 어떤 스키마 변경도 적용되기 전에 설치가 중단되며 — 워크스페이스는 일관된 상태로 이전 버전에 머무릅니다. 이는 의도된 동작입니다: pre-install은 위험한 업그레이드를 거부할 수 있는 마지막 기회입니다.
+
+예시 — 마이그레이션이 기존 필드를 삭제하기 전에 해당 필드 값을 복사하기:
```ts src/logic-functions/pre-install.ts
import { definePreInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define';
-import { createClient } from './generated/client';
+import { CoreApiClient } from 'twenty-client-sdk/core';
const handler = async ({ previousVersion, newVersion }: InstallPayload): Promise => {
// Only the 1.x → 2.x upgrade drops the legacy `notes` field.
@@ -156,24 +110,24 @@ const handler = async ({ previousVersion, newVersion }: InstallPayload): Promise
return;
}
- const client = createClient();
- const legacyRecords = await client.postCard.findMany({
- where: { notes: { isNotNull: true } },
+ const client = new CoreApiClient();
+ const { postCards } = await client.query({
+ postCards: {
+ __args: { filter: { notes: { isNot: null } } },
+ edges: { node: { id: true, notes: true } },
+ },
});
- if (legacyRecords.length === 0) return;
-
- // Copy legacy `notes` into the new `description` field before the migration
- // drops the `notes` column. If this fails, the upgrade is aborted and the
- // workspace stays on v1 with all data intact.
- await Promise.all(
- legacyRecords.map((record) =>
- client.postCard.update({
- where: { id: record.id },
- data: { description: record.notes },
- }),
- ),
- );
+ // Copy legacy `notes` into `description` before the migration drops the
+ // column. If this fails, the upgrade aborts and the workspace stays on v1.
+ for (const { node } of postCards.edges) {
+ await client.mutation({
+ updatePostCard: {
+ __args: { id: node.id, data: { description: node.notes } },
+ id: true,
+ },
+ });
+ }
};
export default definePreInstallLogicFunction({
@@ -186,21 +140,5 @@ export default definePreInstallLogicFunction({
});
```
-**경험칙:**
-
-| 원하는 작업... | 사용 |
-| -------------------------------------- | ---------------------------------------------------------------- |
-| 기본 데이터 시딩, 워크스페이스 구성, 외부 리소스 등록 | `post-install` |
-| 설치 응답을 차단해서는 안 되는 장시간 시딩 또는 서드파티 호출 실행 | `post-install` (기본 — `shouldRunSynchronously: false`, 워커 재시도 포함) |
-| 설치 호출이 반환된 직후 호출자가 즉시 의존하는 빠른 설정 실행 | `shouldRunSynchronously: true`를 사용하는 `post-install` |
-| 곧 진행될 마이그레이션으로 손실될 데이터를 읽거나 백업 | `pre-install` |
-| 기존 데이터를 손상시킬 업그레이드를 거부 | `pre-install` (핸들러에서 예외를 던짐) |
-| 모든 업그레이드 시 상태 조정 실행 | `shouldRunOnVersionUpgrade: true`를 사용하는 `post-install` |
-| 최초 설치에서만 1회성 설정 수행 | `shouldRunOnVersionUpgrade: false`(기본값)을 사용하는 `post-install` |
-
-
-확신이 서지 않는다면 기본적으로 **post-install**을 사용하세요. 마이그레이션 자체가 파괴적이며 이전 상태가 사라지기 전에 이를 가로채야 할 때에만 pre-install을 사용하세요.
-
-
diff --git a/packages/twenty-docs/l/ko/developers/extend/apps/data/objects.mdx b/packages/twenty-docs/l/ko/developers/extend/apps/data/objects.mdx
index d737f36e5b..2800236540 100644
--- a/packages/twenty-docs/l/ko/developers/extend/apps/data/objects.mdx
+++ b/packages/twenty-docs/l/ko/developers/extend/apps/data/objects.mdx
@@ -86,6 +86,22 @@ export default defineObject({
**기본 필드는 자동으로 추가됩니다.** 사용자 정의 개체를 정의하면 Twenty가 `id`, `name`, `createdAt`, `updatedAt`, `createdBy`, `updatedBy`, `deletedAt`와 같은 표준 필드를 자동으로 생성합니다. 이 필드들은 `fields` 배열에 선언할 필요가 없습니다 — 사용자 정의 필드만 선언하면 됩니다. 동일한 이름으로 필드를 선언하여 기본 필드를 재정의할 수 있지만, 이는 거의 바람직하지 않습니다.
+## 필드 유형들
+
+`twenty-sdk/define`에서 export된 `FieldType` 값의 전체 집합:
+
+| 카테고리 | 유형 |
+| -------- | ------------------------------------------------------------------------------------------------------------------- |
+| 텍스트 | `TEXT`, `RICH_TEXT`, `ARRAY` (문자열 배열), `RAW_JSON` |
+| 숫자형 | `NUMBER` (`universalSettings.dataType`: `'float'` / `'int'` / `'bigint'`), `NUMERIC` (임의 정밀도), `RATING`, `POSITION` |
+| 날짜 | `DATE`, `DATE_TIME` |
+| 선택 | `BOOLEAN`, `SELECT`, `MULTI_SELECT` |
+| 복합 | `FULL_NAME`, `ADDRESS`, `EMAILS`, `PHONES`, `LINKS`, `CURRENCY`, `ACTOR`, `FILES` |
+| 식별자 및 관계 | `UUID`, `RELATION`, `MORPH_RELATION` (자세한 내용은 [Relations](/l/ko/developers/extend/apps/data/relations)을 참조) |
+| 시스템 | `TS_VECTOR` (서버에서 관리되는 전체 텍스트 검색 벡터) |
+
+복합 타입은 여러 하위 필드를 저장합니다(예: `FULL_NAME` = 이름 + 성; `CURRENCY` = `amountMicros` + `currencyCode`). `SELECT` 및 `MULTI_SELECT`는 위 예시와 같이 `options` 배열이 필요합니다.
+
## 기본값
리터럴 문자열 기본값은 문자열 **내부에서** 작은따옴표로 감싸야 합니다. 즉, `defaultValue: "'Draft'"`처럼 작성해야 하며, `defaultValue: "Draft"`처럼 작성하면 안 됩니다. 그래서 위의 `status` 필드는 `` `'${PostCardStatus.DRAFT}'` ``를 사용합니다.
diff --git a/packages/twenty-docs/l/ko/developers/extend/apps/getting-started/project-structure.mdx b/packages/twenty-docs/l/ko/developers/extend/apps/getting-started/project-structure.mdx
index 47ad46a603..1e8ff2cc74 100644
--- a/packages/twenty-docs/l/ko/developers/extend/apps/getting-started/project-structure.mdx
+++ b/packages/twenty-docs/l/ko/developers/extend/apps/getting-started/project-structure.mdx
@@ -14,26 +14,39 @@ my-twenty-app/
default-role.ts # Permissions for logic functions
constants/
universal-identifiers.ts # Auto-generated UUIDs and metadata
+ front-components/
+ main-page.tsx # Welcome page component
+ navigation-menu-items/
+ main-page.navigation-menu-item.ts # Sidebar entry for the welcome page
+ page-layouts/
+ main-page.page-layout.ts # Standalone page hosting the component
__tests__/
- setup-test.ts
- app-install.integration-test.ts
- .github/workflows/ci.yml # GitHub Actions
- public/ # Static assets
- vitest.config.ts # Test runner config
+ application-config.test.ts # Unit test
+ global-setup.ts # Integration test setup (sync + uninstall)
+ schema.integration-test.ts # Integration test against a live server
+ .github/workflows/
+ ci.yml # Lint, typecheck, unit + integration tests
+ cd.yml # Deploy + install on push to main
+ public/
+ logo.svg # Static assets
+ vitest.config.ts # Integration test runner config
+ vitest.unit.config.ts # Unit test runner config
tsconfig.json, tsconfig.spec.json
.nvmrc, .yarnrc.yml, .oxlintrc.json
- README.md, LLMS.md
+ README.md, AGENTS.md, CLAUDE.md
```
## 주요 파일
-| 파일 / 폴더 | 목적 |
-| ---------------------------------------- | -------------------------------- |
-| `src/application-config.ts` | **필수.** 앱의 기본 구성 파일입니다. |
-| `src/default-role.ts` | 로직 함수가 접근할 수 있는 범위를 제어하는 기본 역할 |
-| `src/constants/universal-identifiers.ts` | 자동 생성된 UUID와 앱 메타데이터(표시 이름, 설명). |
-| `src/__tests__/` | 통합 테스트(설정 + 예제 테스트). |
-| `public/` | 앱과 함께 제공되는 정적 에셋(이미지, 폰트). |
+| 파일 / 폴더 | 목적 |
+| -------------------------------------------------------------------------- | ----------------------------------------------------------------- |
+| `src/application-config.ts` | **필수.** 앱의 기본 구성 파일입니다. |
+| `src/default-role.ts` | 로직 함수가 접근할 수 있는 범위를 제어하는 기본 역할 |
+| `src/constants/universal-identifiers.ts` | 자동 생성된 UUID와 앱 메타데이터(표시 이름, 설명). |
+| `src/front-components/`, `src/navigation-menu-items/`, `src/page-layouts/` | 시작용 환영 페이지: 사이드바에서 접근할 수 있는, 독립적인 페이지 레이아웃에 의해 렌더링되는 프런트 컴포넌트입니다. |
+| `src/__tests__/` | 실제 서버에 대해 앱을 동기화하는 통합 테스트(글로벌 설정 포함)와 단위 테스트입니다. |
+| `public/` | 앱과 함께 제공되는 정적 에셋(이미지, 폰트). |
+| `AGENTS.md` / `CLAUDE.md` | 앱에서 작업하는 AI 코딩 에이전트를 위한 안내서입니다. |
**파일 구성은 사용자의 선택입니다.** 위 폴더들은 관례일 뿐이며, SDK는 파일 위치와 관계없이 `export default defineEntity(...)` 호출에 대한 AST 분석을 통해 엔티티를 감지합니다.
@@ -47,15 +60,18 @@ my-twenty-app/
{
"dependencies": {},
"devDependencies": {
- "twenty-client-sdk": "^2.13.0",
- "twenty-sdk": "^2.13.0"
+ "twenty-client-sdk": "2.20.0",
+ "twenty-sdk": "2.20.0",
+ "twenty-ui": "1.0.0-alpha.1"
}
}
```
+스캐폴더는 `twenty-sdk`와 `twenty-client-sdk`를 자체 버전에 고정합니다. 업그레이드할 때 두 패키지의 버전을 동기화된 상태로 유지하세요.
+
* \*\*`twenty-sdk`\*\*는 `twenty` CLI와 빌드/스캐폴딩 도구를 제공합니다. 이 패키지는 개발 및 빌드 시점에만 실행되며, 배포된 앱의 런타임에서는 전혀 임포트되지 않습니다.
* \*\*`twenty-client-sdk`\*\*는 앱 코드(`CoreApiClient`, `MetadataApiClient`, `RestApiClient`)에서 임포트되지만, 런타임에는 Twenty가 이를 제공합니다. 로직 함수는 생성된 SDK 레이어에서 이를 가져오고, 프런트엔드 컴포넌트는 서버에서 제공되는 모듈에서 이를 해석하여 가져옵니다. 설치된 사본은 타입 검사와 배포 시점 빌드에만 사용되므로, 배포된 번들에 포함되어 함께 제공될 필요가 없습니다.
-어느 한 패키지라도 `dependencies` 아래에 두면, 설치된 앱의 런타임 번들에 포함되어 쓸모없는 부하가 됩니다. `twenty build`는 둘 중 하나라도 여전히 `dependencies` 아래에 나열되어 있으면 경고를 출력합니다.
+어느 한 패키지라도 `dependencies` 아래에 두면, 설치된 앱의 런타임 번들에 포함되어 쓸모없는 부하가 됩니다. `twenty dev:build`는 둘 중 하나라도 여전히 `dependencies` 아래에 나열되어 있으면 경고를 출력합니다.
앱의 실제 런타임 의존성(로직 함수가 런타임에 실제로 임포트하는 라이브러리)은 평소와 같이 `dependencies` 아래에 추가하세요.
diff --git a/packages/twenty-docs/l/ko/developers/extend/apps/getting-started/quick-start.mdx b/packages/twenty-docs/l/ko/developers/extend/apps/getting-started/quick-start.mdx
index c3b0aaa5bf..51fe8ee057 100644
--- a/packages/twenty-docs/l/ko/developers/extend/apps/getting-started/quick-start.mdx
+++ b/packages/twenty-docs/l/ko/developers/extend/apps/getting-started/quick-start.mdx
@@ -6,17 +6,17 @@ description: 몇 분 만에 첫 번째 Twenty 앱을 만들어 보세요.
## 사전 준비
-* **Node.js 24+** — [여기에서 다운로드](https://nodejs.org/)
+* **Node.js 24.5+** — [여기에서 다운로드](https://nodejs.org/)
* **Yarn 4** — Corepack을 통해 Node.js와 함께 제공됩니다. 활성화하려면: `corepack enable`
* **Docker** — [여기에서 다운로드](https://www.docker.com/products/docker-desktop/). 로컬 Twenty 서버를 실행하려면 필요합니다. 이미 다른 곳에서 Twenty가 실행 중이라면 건너뛰세요.
Twenty 앱을 빌드하는 과정은 세 단계로 이루어집니다. 스캐폴더는 이를 단일 해피 패스 명령으로 합쳐 주지만, 각 단계는 별개의 개념입니다 — 문제가 발생했을 때 현재 단계가 어디인지 알면 무엇을 고쳐야 하는지 파악할 수 있습니다.
-| 단계 | 하는 일 | 도구 | 결과 |
-| ------------ | ------------------------- | ----------------------------- | -------------------- |
-| **1. 스캐폴딩** | 앱의 소스 코드를 생성 | `npx create-twenty-app` | 디스크에 TypeScript 프로젝트 |
-| **2. 서버 실행** | 동기화 대상으로 사용할 Twenty 서버 시작 | Docker + `yarn twenty server` | 실행 중인 Twenty 인스턴스 |
-| **3. 동기화** | 코드를 서버와 실시간 동기화 | `yarn twenty dev` | 변경 사항이 UI에 표시됨 |
+| 단계 | 하는 일 | 도구 | 결과 |
+| ------------ | ------------------------- | ----------------------------------- | -------------------- |
+| **1. 스캐폴딩** | 앱의 소스 코드를 생성 | `npx create-twenty-app` | 디스크에 TypeScript 프로젝트 |
+| **2. 서버 실행** | 동기화 대상으로 사용할 Twenty 서버 시작 | Docker + `yarn twenty docker:start` | 실행 중인 Twenty 인스턴스 |
+| **3. 동기화** | 코드를 서버와 실시간 동기화 | `yarn twenty dev` | 변경 사항이 UI에 표시됨 |
---
@@ -28,7 +28,7 @@ Twenty 앱을 빌드하는 과정은 세 단계로 이루어집니다. 스캐폴
npx create-twenty-app@latest my-twenty-app
```
-이름과 설명을 묻는 프롬프트가 표시됩니다 — 기본값을 사용하려면 **Enter**를 누르세요. 이 명령은 `my-twenty-app/`에 시작용 `application-config.ts`, 기본 역할, CI 워크플로, 통합 테스트가 포함된 TypeScript 프로젝트를 생성합니다.
+스캐폴더는 비대화식입니다. 디렉터리 이름이 앱 이름이 됩니다. 생성되는 메타데이터를 사용자 지정하려면 `--display-name` 및 `--description`을(를) 전달합니다(나중에 `src/constants/universal-identifiers.ts`에서 수정할 수도 있습니다). 이 명령은 `my-twenty-app/`에 시작용 `application-config.ts`, 기본 역할, CI/CD 워크플로, 통합 테스트가 포함된 TypeScript 프로젝트를 생성합니다.
**이 단계를 마치면:** 로컬 머신에 앱의 소스 코드가 준비됩니다. 아직 실행되지는 않았습니다 — 그건 2단계에서 진행합니다.
@@ -38,28 +38,14 @@ npx create-twenty-app@latest my-twenty-app
앱은 동기화할 Twenty 서버가 필요합니다. 이 서버는 Docker에서 로컬로 실행되는 완전한 Twenty 인스턴스입니다 — UI, GraphQL API, PostgreSQL을 포함합니다. 로컬 코드가 해당 서버로 정의를 업로드하면 UI에 표시됩니다.
-스캐폴더가 서버 시작 여부를 묻습니다:
+Scaffolder가 이를 대신 시작합니다. Docker가 실행 중이면 `twentycrm/twenty-app-dev` 이미지를 pull 하고, 포트 `2020`에서 시작한 다음, 미리 시드된 데모 워크스페이스(`tim@apple.dev`)에 대해 CLI를 인증합니다. 별도의 로그인은 필요하지 않습니다.
-> **로컬 Twenty 인스턴스를 설정하시겠습니까?**
-
-* **Yes(권장)** — `twentycrm/twenty-app-dev` Docker 이미지를 가져와 포트 `2020`에서 시작합니다. 먼저 Docker가 실행 중인지 확인하세요.
-* **No** — 이미 연결하려는 Twenty 서버가 있는 경우 선택하세요. `yarn twenty remote:add`로 나중에 연결할 수 있습니다.
-
-
-

-
-
-서버가 올라오면 로그인할 수 있도록 브라우저가 열립니다. 미리 준비된 데모 계정을 사용하세요:
-
-* **이메일:** `tim@apple.dev`
-* **비밀번호:** `tim@apple.dev`
+대신 기존 Twenty 서버에 연결하려면 `--url \`을 전달하세요. 원격 서버는 OAuth로 인증합니다. 브라우저가 열리면 로그인한 뒤 **Authorize**를 클릭해 워크스페이스에 대한 CLI 액세스를 허용하면 됩니다. (로컬에서도 `--authentication-method oauth`로 OAuth를 선택할 수 있습니다. `tim@apple.dev` / `tim@apple.dev`로 로그인하세요.)
-다음 화면에서 **Authorize**를 클릭하세요 — 그러면 CLI가 워크스페이스에 접근할 수 있게 됩니다.
-
@@ -117,28 +103,32 @@ yarn twenty dev
### CI 및 스크립트를 위한 1회성 동기화
-`--once`를 전달하면 한 번만 빌드 + 동기화를 실행하고 종료합니다 — 파이프라인은 동일하고, 워처는 없습니다:
+워처 없이 동일한 파이프라인을 한 번만 실행하려면 `plan`과 `apply`를 사용하세요:
```bash filename="Terminal"
-yarn twenty dev --once
+yarn twenty plan # preview the metadata changes without applying them
+yarn twenty apply # show the plan, then apply it
```
-| 명령 | 동작 | 사용 시점 |
-| ---------------------------------- | ------------------------------------------------ | -------------------------------------- |
-| `yarn twenty dev` | 소스 파일을 감시하고 변경될 때마다 다시 동기화합니다. 중지할 때까지 계속 실행됩니다. | 대화형 로컬 개발. |
-| `yarn twenty dev --once` | 한 번만 빌드 + 동기화를 수행하고, 성공 시 `0`, 실패 시 `1`로 종료합니다. | CI, pre-commit 훅, AI 에이전트, 스크립트형 워크플로. |
-| `yarn twenty dev --once --dry-run` | 메타데이터 변경 사항을 **실제로 적용하지 않고** 빌드하고 출력합니다. | 커밋하기 전에 동기화가 어떤 변경을 수행할지 살펴봅니다. |
+| 명령 | 동작 | 사용 시점 |
+| ------------------- | ------------------------------------------------------------------------------------------------- | -------------------------------------- |
+| `yarn twenty dev` | 소스 파일을 감시하고 변경될 때마다 다시 동기화합니다. 중지할 때까지 계속 실행됩니다. | 대화형 로컬 개발. |
+| `yarn twenty apply` | 한 번만 빌드 + 동기화를 수행하고, 성공 시 `0`, 실패 시 `1`로 종료합니다. 파괴적인 변경 사항에 대해 확인을 요청합니다(건너뛰려면 `--force`를 전달하세요). | CI, pre-commit 훅, AI 에이전트, 스크립트형 워크플로. |
+| `yarn twenty plan` | 메타데이터 변경 사항을 **실제로 적용하지 않고** 빌드하고 출력합니다. | 커밋하기 전에 동기화가 어떤 변경을 수행할지 살펴봅니다. |
-두 모드 모두 인증된 리모트가 필요합니다. `--dry-run`에 대한 자세한 내용은 [동기화 및 복구](/l/ko/developers/extend/apps/operations/sync-and-recovery#previewing-changes-dry-run)를 참고하세요.
+모든 모드에는 인증된 리모트가 필요합니다. `plan`에 대한 자세한 내용은 [동기화 및 복구](/l/ko/developers/extend/apps/operations/sync-and-recovery#previewing-changes-plan)를 참고하세요.
+
+
+`yarn twenty dev --once` 및 `yarn twenty dev --once --dry-run`은 `yarn twenty apply` 및 `yarn twenty plan`의 사용 중단된 별칭입니다.
+
### 개발 모드 옵션
-| 플래그 | 설명 |
-| ------------------------------------- | -------------------------------------------------------------------- |
-| `--once` | 한 번만 빌드하고 동기화한 다음 종료합니다. |
-| `--dry-run` | `--once`를 사용하면 메타데이터 변경 사항을 실제로 적용하지 않고 미리 볼 수 있습니다. 아무것도 기록하지 않습니다. |
-| `--debounceMs \` | 파일 변경 디바운스 지연 시간을 밀리초 단위로 설정합니다(기본값: `2000`). |
-| `--verbose` / `--debug` | 자세한 빌드 로그, 동기화 요청, 오류 추적을 표시합니다. |
+| 플래그 | 설명 |
+| ------------------------------------- | --------------------------------------------- |
+| `--force` | 확인 없이 파괴적인 변경 사항(삭제)을 적용합니다. |
+| `--debounceMs \` | 파일 변경 디바운스 지연 시간을 밀리초 단위로 설정합니다(기본값: `1000`). |
+| `--verbose` / `--debug` | 자세한 빌드 로그, 동기화 요청, 오류 추적을 표시합니다. |
## 만들 수 있는 것
diff --git a/packages/twenty-docs/l/ko/developers/extend/apps/getting-started/scaffolding.mdx b/packages/twenty-docs/l/ko/developers/extend/apps/getting-started/scaffolding.mdx
index 87de2f3291..8158f83ffa 100644
--- a/packages/twenty-docs/l/ko/developers/extend/apps/getting-started/scaffolding.mdx
+++ b/packages/twenty-docs/l/ko/developers/extend/apps/getting-started/scaffolding.mdx
@@ -34,6 +34,10 @@ yarn twenty dev:add frontComponent
| 뷰 | `yarn twenty dev:add view` | `src/views/\.ts` |
| 내비게이션 메뉴 항목 | `yarn twenty dev:add navigationMenuItem` | `src/navigation-menu-items/\.ts` |
| 페이지 레이아웃 | `yarn twenty dev:add pageLayout` | `src/page-layouts/\.ts` |
+| 페이지 레이아웃 탭 | `yarn twenty dev:add pageLayoutTab` | `src/page-layout-tabs/\.ts` |
+| 명령 메뉴 항목 | `yarn twenty dev:add commandMenuItem` | `src/command-menu-items/\.ts` |
+| 보기 필드 | `yarn twenty dev:add viewField` | `src/view-fields/\.ts` |
+| 연결 제공자 | `yarn twenty dev:add connectionProvider` | `src/connection-providers/\.ts` |
## 스캐폴더가 생성하는 것
diff --git a/packages/twenty-docs/l/ko/developers/extend/apps/getting-started/troubleshooting.mdx b/packages/twenty-docs/l/ko/developers/extend/apps/getting-started/troubleshooting.mdx
index 6fd1109431..c46efd019f 100644
--- a/packages/twenty-docs/l/ko/developers/extend/apps/getting-started/troubleshooting.mdx
+++ b/packages/twenty-docs/l/ko/developers/extend/apps/getting-started/troubleshooting.mdx
@@ -5,10 +5,10 @@ icon: wrench
---
* **Docker 오류** — `yarn twenty docker:start`를 실행하기 전에 Docker Desktop(또는 데몬)이 실행 중인지 확인하세요. 오류 메시지에 OS에 맞는 올바른 시작 명령이 표시됩니다.
-* **Node 버전 오류** — 24 이상 필요. `node -v`로 확인하세요.
+* **잘못된 Node 버전** — 24.5+가 필요합니다 (`engines.node: ^24.5.0`). `node -v`로 확인하세요.
* **Yarn 4 누락** — `corepack enable`을 실행하세요.
* **의존성 문제** — `rm -rf node_modules && yarn install`.
* **`twenty-sdk` v2.8.0으로 업그레이드한 후 오류 발생** — v2.8.0에서 `dependencies`에서 `devDependencies`로 이동했습니다. [프로젝트 구조 → Dependencies](/l/ko/developers/extend/apps/getting-started/project-structure#dependencies)를 참조하세요.
-* **`twenty build`는 `dependencies` 아래의 `twenty-client-sdk`에 대해 경고합니다** — 이는 실행 시 Twenty에서 제공되므로, `twenty-sdk`와 함께 `devDependencies`로 옮겨야 합니다. [프로젝트 구조 → Dependencies](/l/ko/developers/extend/apps/getting-started/project-structure#dependencies)를 참조하세요.
+* **`twenty dev:build`는 `dependencies` 아래의 `twenty-client-sdk`에 대해 경고합니다** — 이는 실행 시 Twenty에서 제공되므로, `twenty-sdk`와 함께 `devDependencies`로 옮겨야 합니다. [프로젝트 구조 → Dependencies](/l/ko/developers/extend/apps/getting-started/project-structure#dependencies)를 참조하세요.
막히셨나요? [Twenty Discord](https://discord.com/channels/1130383047699738754/1130386664812982322)에서 문의하세요.
diff --git a/packages/twenty-docs/l/ko/developers/extend/apps/layout/command-menu-items.mdx b/packages/twenty-docs/l/ko/developers/extend/apps/layout/command-menu-items.mdx
index 1c3b09f7ba..61caf1a311 100644
--- a/packages/twenty-docs/l/ko/developers/extend/apps/layout/command-menu-items.mdx
+++ b/packages/twenty-docs/l/ko/developers/extend/apps/layout/command-menu-items.mdx
@@ -13,7 +13,6 @@ export default defineCommandMenuItem({
universalIdentifier: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
label: 'Open Dashboard',
shortLabel: 'Dashboard',
- icon: 'IconLayoutDashboard',
isPinned: true,
availabilityType: 'GLOBAL',
frontComponentUniversalIdentifier: '74c526eb-cb68-4cf7-b05c-0dd8c288d948',
@@ -22,51 +21,23 @@ export default defineCommandMenuItem({
## 구성 필드
-| 필드 | 필수 | 설명 |
-| --------------------------------------- | --- | ---------------------------------------------------------------------------------------------------------- |
-| `universalIdentifier` | 예 | 명령의 안정적인 고유 ID |
-| `label` | 예 | 명령 메뉴(Cmd+K)에 표시되는 전체 레이블 |
-| `frontComponentUniversalIdentifier` | 예 | 이 명령으로 열리는 프런트 컴포넌트의 `universalIdentifier` |
-| `shortLabel` | 아니요 | 고정된 빠른 작업 버튼에 표시되는 더 짧은 레이블 |
-| `icon` | 아니요 | 레이블 옆에 표시되는 아이콘 이름(예: `'IconBolt'`, `'IconSend'`) |
-| `isPinned` | 아니요 | `true`이면 페이지 우측 상단에 빠른 작업 버튼으로 명령을 표시합니다 |
-| `availabilityType` | 아니요 | 명령이 표시되는 위치를 제어합니다: 'GLOBAL'(항상 사용 가능), 'RECORD_SELECTION'(레코드가 선택된 경우에만), 'FALLBACK'(다른 명령이 일치하지 않을 때 표시) |
-| `availabilityObjectUniversalIdentifier` | 아니요 | 명령을 특정 객체 타입의 페이지로 제한합니다(예: Company 레코드에서만) |
-| `conditionalAvailabilityExpression` | 아니요 | 표시 여부를 동적으로 제어하는 불리언 표현식(아래 참조) |
+| 필드 | 필수 | 설명 |
+| --------------------------------------- | --- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `universalIdentifier` | 예 | 명령의 안정적인 고유 ID |
+| `label` | 예 | 명령 메뉴(Cmd+K)에 표시되는 전체 레이블 |
+| `frontComponentUniversalIdentifier` | 예 | 이 명령으로 열리는 프런트 컴포넌트의 `universalIdentifier` |
+| `shortLabel` | 아니요 | 고정된 빠른 작업 버튼에 표시되는 더 짧은 레이블 |
+| `icon` | 아니요 | **사용 중단됨** — 애플리케이션 아이콘이 대신 사용되며, 설정된 경우 빌드 시 경고가 발생합니다. |
+| `isPinned` | 아니요 | `true`이면 페이지 우측 상단에 빠른 작업 버튼으로 명령을 표시합니다 |
+| `availabilityType` | 아니요 | 명령이 표시되는 위치를 제어합니다: `'GLOBAL'`(항상 사용 가능), `'GLOBAL_OBJECT_CONTEXT'`(객체 컨텍스트가 있는 페이지에서만 — 인덱스 및 레코드 페이지), `'RECORD_SELECTION'`(레코드가 선택된 경우에만), `'FALLBACK'`(다른 명령이 일치하지 않을 때 표시). |
+| `availabilityObjectUniversalIdentifier` | 아니요 | 명령을 특정 객체 타입의 페이지로 제한합니다(예: Company 레코드에서만) |
+| `conditionalAvailabilityExpression` | 아니요 | 표시 여부를 동적으로 제어하는 불리언 표현식(아래 참조) |
## 헤드리스 명령
[헤드리스 프런트 컴포넌트](/l/ko/developers/extend/apps/layout/front-components#headless-vs-non-headless)와 짝을 이룬 명령 메뉴 항목은 원클릭 작업—코드 실행, 이동, 확인 후 실행—을 제공하는 가장 전형적인 방식입니다. 프런트 컴포넌트 페이지에서는 동작 후 언마운트 패턴을 처리하는 [SDK Command 구성 요소](/l/ko/developers/extend/apps/layout/front-components#sdk-command-components) (`Command`, `CommandLink`, `CommandModal`, `CommandOpenSidePanelPage`)를 다룹니다.
-일반적인 흐름:
-
-```tsx src/front-components/run-action.tsx
-import { defineFrontComponent } from 'twenty-sdk/define';
-import { Command } from 'twenty-sdk/command';
-import { CoreApiClient } from 'twenty-sdk/clients';
-
-const RunAction = () => {
- const execute = async () => {
- const client = new CoreApiClient();
- await client.mutation({
- createTask: {
- __args: { data: { title: 'Created by my app' } },
- id: true,
- },
- });
- };
-
- return ;
-};
-
-export default defineFrontComponent({
- universalIdentifier: 'e5f6a7b8-c9d0-1234-efab-345678901234',
- name: 'run-action',
- description: 'Creates a task from the command menu',
- component: RunAction,
- isHeadless: true,
-});
-```
+일반적인 흐름: 헤드리스 컴포넌트가 ``( [전체 예제](/l/ko/developers/extend/apps/layout/front-components#sdk-command-components)를 참고)와 같이 렌더링하고, 명령 메뉴 항목이 이를 가리킵니다.
```ts src/command-menu-items/run-action.command-menu-item.ts
import { defineCommandMenuItem } from 'twenty-sdk/define';
@@ -74,7 +45,6 @@ import { defineCommandMenuItem } from 'twenty-sdk/define';
export default defineCommandMenuItem({
universalIdentifier: 'f6a7b8c9-d0e1-2345-fabc-456789012345',
label: 'Run my action',
- icon: 'IconPlayerPlay',
frontComponentUniversalIdentifier: 'e5f6a7b8-c9d0-1234-efab-345678901234',
});
```
diff --git a/packages/twenty-docs/l/ko/developers/extend/apps/layout/front-components.mdx b/packages/twenty-docs/l/ko/developers/extend/apps/layout/front-components.mdx
index 7b4f734b23..40d9bf3066 100644
--- a/packages/twenty-docs/l/ko/developers/extend/apps/layout/front-components.mdx
+++ b/packages/twenty-docs/l/ko/developers/extend/apps/layout/front-components.mdx
@@ -49,14 +49,13 @@ export default defineCommandMenuItem({
universalIdentifier: 'd4e5f6a7-b8c9-0123-defa-456789012345',
shortLabel: 'Hello',
label: 'Hello World',
- icon: 'IconBolt',
isPinned: true,
availabilityType: 'GLOBAL',
frontComponentUniversalIdentifier: '74c526eb-cb68-4cf7-b05c-0dd8c288d948',
});
```
-`yarn twenty dev`로 동기화한 후(또는 일회성으로 `yarn twenty dev --once`를 실행한 경우), 페이지 우측 상단에 빠른 작업이 표시됩니다:
+`yarn twenty dev`로 동기화한 후(또는 일회성으로 `yarn twenty apply`를 실행한 경우), 페이지 우측 상단에 빠른 작업이 표시됩니다:

@@ -88,11 +87,11 @@ export default defineCommandMenuItem({
```tsx src/front-components/sync-tracker.tsx
import { defineFrontComponent } from 'twenty-sdk/define';
-import { useRecordId, enqueueSnackbar } from 'twenty-sdk/front-component';
+import { useSelectedRecordIds, enqueueSnackbar } from 'twenty-sdk/front-component';
import { useEffect } from 'react';
const SyncTracker = () => {
- const recordId = useRecordId();
+ const [recordId] = useSelectedRecordIds();
useEffect(() => {
enqueueSnackbar({ message: `Tracking record ${recordId}`, variant: 'info' });
@@ -116,7 +115,7 @@ export default defineFrontComponent({
`twenty-sdk` 패키지는 헤드리스 프런트 컴포넌트를 위해 설계된 네 가지 Command 헬퍼 컴포넌트를 제공합니다. 각 컴포넌트는 마운트 시 동작을 실행하고, 스낵바 알림을 표시하여 오류를 처리하며, 완료되면 프런트 컴포넌트를 자동으로 언마운트합니다.
-`twenty-sdk/command`에서 임포트하세요:
+`twenty-sdk/front-component`에서 임포트하세요:
* **`Command`** — `execute` prop을 통해 비동기 콜백을 실행합니다.
* **`CommandLink`** — 앱 경로로 이동합니다. Props: `to`, `params`, `queryParams`, `options`.
@@ -127,8 +126,8 @@ export default defineFrontComponent({
```tsx src/front-components/run-action.tsx
import { defineFrontComponent } from 'twenty-sdk/define';
-import { Command } from 'twenty-sdk/command';
-import { CoreApiClient } from 'twenty-sdk/clients';
+import { Command } from 'twenty-sdk/front-component';
+import { CoreApiClient } from 'twenty-client-sdk/core';
const RunAction = () => {
const execute = async () => {
@@ -160,7 +159,6 @@ import { defineCommandMenuItem } from 'twenty-sdk/define';
export default defineCommandMenuItem({
universalIdentifier: 'f6a7b8c9-d0e1-2345-fabc-456789012345',
label: 'Run my action',
- icon: 'IconPlayerPlay',
frontComponentUniversalIdentifier: 'e5f6a7b8-c9d0-1234-efab-345678901234',
});
```
@@ -169,7 +167,7 @@ export default defineCommandMenuItem({
```tsx src/front-components/delete-draft.tsx
import { defineFrontComponent } from 'twenty-sdk/define';
-import { CommandModal } from 'twenty-sdk/command';
+import { CommandModal } from 'twenty-sdk/front-component';
const DeleteDraft = () => {
const execute = async () => {
@@ -202,7 +200,7 @@ Front 컴포넌트는 샌드박스된 Web Worker 안에서 브라우저 측에
`httpRouteTriggerSettings`로 선언된 로직 함수는 HTTP를 통해 해당 라우트 경로에서 액세스할 수 있습니다. Twenty는 워커에 함수들이 제공되는 기본 URL을 `TWENTY_FUNCTIONS_URL`로 주입하고, 호출을 인증하는 `TWENTY_APP_ACCESS_TOKEN`도 함께 주입합니다. 아직 자체 함수를 호출하기 위한 전용 SDK 클라이언트는 없으므로, 일반 `fetch`로 호출하세요:
-> **Twenty Cloud에서 HTTP로 트리거되는 로직 함수는 작업공간별 전용 도메인에서 제공됩니다**: `https://\
.twenty.com\` — 이는 `TWENTY_FUNCTIONS_URL`이 정확히 가리키는 주소입니다. 외부 호출자의 경우, 함수의 **HTTP trigger** 설정 또는 애플리케이션의 **Settings** 탭에서 정확한 URL을 복사하세요.
+> **Twenty Cloud에서 HTTP로 트리거되는 로직 함수는 작업공간별 전용 도메인에서 제공됩니다**: `https://\.withtwenty.com\` — 이는 `TWENTY_FUNCTIONS_URL`이 정확히 가리키는 주소입니다. 외부 호출자의 경우, 함수의 **HTTP trigger** 설정 또는 애플리케이션의 **Settings** 탭에서 정확한 URL을 복사하세요.
레거시 `/s/` 함수 라우트는 **사용 중단(deprecated)** 되었으며 **2026-07-24에 비활성화됩니다**. 대신 위의 `TWENTY_FUNCTIONS_URL`을 사용하고, 해당 날짜 이전에 하드 코딩된 모든 `/s/` URL을 마이그레이션하세요. `/s/` 라우트는 셀프 호스팅의 경우 계속 사용 가능합니다.
@@ -212,7 +210,7 @@ Front 컴포넌트는 샌드박스된 Web Worker 안에서 브라우저 측에
```tsx src/front-components/sync-prs.tsx
import { defineFrontComponent } from 'twenty-sdk/define';
-import { Command } from 'twenty-sdk/command';
+import { Command } from 'twenty-sdk/front-component';
const SyncPrs = () => {
const execute = async () => {
@@ -316,13 +314,13 @@ try {
import { defineFrontComponent } from 'twenty-sdk/define';
import {
useUserId,
- useRecordId,
+ useSelectedRecordIds,
useFrontComponentId,
} from 'twenty-sdk/front-component';
const RecordInfo = () => {
const userId = useUserId();
- const recordId = useRecordId();
+ const [recordId] = useSelectedRecordIds();
const componentId = useFrontComponentId();
return (
@@ -405,12 +403,11 @@ export default defineFrontComponent({
```tsx src/front-components/archive-record.tsx
import { defineFrontComponent } from 'twenty-sdk/define';
-import { useRecordId } from 'twenty-sdk/front-component';
-import { enqueueSnackbar, closeSidePanel } from 'twenty-sdk/front-component';
-import { CoreApiClient } from 'twenty-sdk/clients';
+import { enqueueSnackbar, closeSidePanel, useSelectedRecordIds } from 'twenty-sdk/front-component';
+import { CoreApiClient } from 'twenty-client-sdk/core';
const ArchiveRecord = () => {
- const recordId = useRecordId();
+ const [recordId] = useSelectedRecordIds();
const handleArchive = async () => {
const client = new CoreApiClient();
@@ -451,10 +448,10 @@ export default defineFrontComponent({
여러 개의 선택된 기록을 처리하려면 `useSelectedRecordIds()`를 사용하세요. 이는 일괄 작업에 유용합니다:
```tsx src/front-components/bulk-export.tsx
-import { defineFrontComponent, numberOfSelectedRecords } from 'twenty-sdk/define';
+import { defineFrontComponent } from 'twenty-sdk/define';
import { useSelectedRecordIds } from 'twenty-sdk/front-component';
import { enqueueSnackbar, closeSidePanel } from 'twenty-sdk/front-component';
-import { CoreApiClient } from 'twenty-sdk/clients';
+import { CoreApiClient } from 'twenty-client-sdk/core';
const BulkExport = () => {
const selectedRecordIds = useSelectedRecordIds();
@@ -492,12 +489,19 @@ export default defineFrontComponent({
name: 'bulk-export',
description: 'Export selected records',
component: BulkExport,
- command: {
- universalIdentifier: 'd0e1f2a3-b4c5-6789-defa-012345678902',
- label: 'Bulk Export',
- availabilityType: 'RECORD_SELECTION',
- conditionalAvailabilityExpression: numberOfSelectedRecords > 0,
- },
+});
+```
+
+레코드 선택으로 제한된 [명령 메뉴 항목](/l/ko/developers/extend/apps/layout/command-menu-items)으로 노출하세요:
+
+```ts src/command-menu-items/bulk-export.command-menu-item.ts
+import { defineCommandMenuItem } from 'twenty-sdk/define';
+
+export default defineCommandMenuItem({
+ universalIdentifier: 'd0e1f2a3-b4c5-6789-defa-012345678902',
+ label: 'Bulk Export',
+ availabilityType: 'RECORD_SELECTION',
+ frontComponentUniversalIdentifier: 'd0e1f2a3-b4c5-6789-defa-012345678901',
});
```
diff --git a/packages/twenty-docs/l/ko/developers/extend/apps/layout/navigation-menu-items.mdx b/packages/twenty-docs/l/ko/developers/extend/apps/layout/navigation-menu-items.mdx
index 24cf0d2871..d4a11058c4 100644
--- a/packages/twenty-docs/l/ko/developers/extend/apps/layout/navigation-menu-items.mdx
+++ b/packages/twenty-docs/l/ko/developers/extend/apps/layout/navigation-menu-items.mdx
@@ -35,6 +35,8 @@ export default defineNavigationMenuItem({
* `position`은 사이드바에서의 정렬 순서를 제어합니다.
+* 열거형에는 사용자 생성 레코드 즐겨찾기에 내부적으로 사용되는 `NavigationMenuItemType.RECORD` 도 포함되어 있습니다. 이 항목은 앱 매니페스트에서 사용할 수 없는데, 레코드를 참조할 필드가 없기 때문입니다.
+
* `icon`과 `color`는 선택 사항이며 항목의 표시 방식을 사용자 지정합니다.
* `folderUniversalIdentifier`는 모든 항목에서 사용할 수 있으며, 이를 통해 해당 항목을 `FOLDER` 유형의 상위 항목 안에 중첩할 수 있습니다.
diff --git a/packages/twenty-docs/l/ko/developers/extend/apps/layout/views.mdx b/packages/twenty-docs/l/ko/developers/extend/apps/layout/views.mdx
index 17de1c4800..5816ee9d0e 100644
--- a/packages/twenty-docs/l/ko/developers/extend/apps/layout/views.mdx
+++ b/packages/twenty-docs/l/ko/developers/extend/apps/layout/views.mdx
@@ -33,17 +33,32 @@ export default defineView({
## 핵심 요점
* `objectUniversalIdentifier`는 이 뷰가 적용되는 객체를 지정합니다. 이 객체는 사용자가 정의한 커스텀 객체일 수도 있고, 표준 Twenty 객체일 수도 있습니다.
-* `key`는 보기 유형을 결정합니다. `ViewKey.INDEX`는 해당 객체의 기본 목록 보기입니다.
+* `key: ViewKey.INDEX`는 뷰를 객체의 기본 목록 보기( `OBJECT` 내비게이션 항목이 여는 보기)로 표시합니다.
* `fields`는 어떤 열을 어떤 순서로 표시할지를 제어합니다. 각 필드는 `fieldMetadataUniversalIdentifier`를 참조합니다.
-* 또한 더 고급 구성을 위해 `filters`, `filterGroups`, `groups`, `fieldGroups`를 선언할 수 있습니다.
+* 또한 더 고급 구성을 위해 `filters`, `filterGroups`, `sorts`, `groups`, `fieldGroups`를 선언할 수 있습니다.
* `position`은 동일한 객체에 여러 뷰가 있을 때의 정렬 순서를 제어합니다.
+## 선택적 속성
+
+| 속성 | 값 | 설명 |
+| ----------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- |
+| `type` | `ViewType.TABLE`(기본값), `ViewType.KANBAN`, `ViewType.CALENDAR` | 레코드가 어떻게 배치되는지에 대한 설정입니다. (`FIELDS_WIDGET` / `TABLE_WIDGET`도 존재하지만, 페이지 레이아웃 위젯에서 내부적으로 사용됩니다.) |
+| `visibility` | `ViewVisibility.WORKSPACE`(기본값), `ViewVisibility.UNLISTED` | 워크스페이스 전체에 대해 뷰가 목록에 표시되는지, 선택기에서 숨겨지는지 여부입니다. |
+| `openRecordIn` | `ViewOpenRecordIn.SIDE_PANEL`(기본값), `ViewOpenRecordIn.RECORD_PAGE` | 레코드를 클릭했을 때 어디에서 열리는지에 대한 설정입니다. |
+| `정렬` | `{ fieldMetadataUniversalIdentifier, direction: ViewSortDirection.ASC \| DESC }[]` | 기본 정렬 순서입니다. |
+| `isCompact` | `boolean` | 행을 압축 형태로 표시합니다. |
+| `mainGroupByFieldMetadataUniversalIdentifier` + `shouldHideEmptyGroups` | — | 레코드를 필드별로 그룹화합니다(예: 칸반 열). |
+| `kanbanAggregateOperation`, `kanbanAggregateOperationFieldMetadataUniversalIdentifier`, `kanbanColumnWidth` | `AggregateOperations.*` | 칸반 열의 집계 및 크기 설정입니다. |
+| `calendarLayout`, `calendarFieldMetadataUniversalIdentifier` | `ViewCalendarLayout.DAY` / `WEEK` / `MONTH` | 캘린더 뷰: 레이아웃 및 레코드의 위치를 결정하는 날짜 필드입니다. |
+
+위의 모든 enum은 `twenty-sdk/define`에서 export됩니다.
+
## 필터
뷰는 미리 적용된 필터와 함께 제공될 수 있습니다. 각 필터에는 세 가지 요소가 있습니다: 필터링할 **필드**, **연산자**(어떻게 비교할지), 그리고 **값**(무엇과 비교할지). 세 가지가 모두 맞아야 하며, 필드 유형에 적용되지 않는 연산자를 사용하면 동기화 시 거부됩니다.
```ts
-import { ViewFilterOperand } from 'twenty-shared/types';
+import { ViewFilterOperand } from 'twenty-sdk/define';
filters: [
{
diff --git a/packages/twenty-docs/l/ko/developers/extend/apps/logic/logic-functions.mdx b/packages/twenty-docs/l/ko/developers/extend/apps/logic/logic-functions.mdx
index 01e717e69d..b4b26a8398 100644
--- a/packages/twenty-docs/l/ko/developers/extend/apps/logic/logic-functions.mdx
+++ b/packages/twenty-docs/l/ko/developers/extend/apps/logic/logic-functions.mdx
@@ -51,8 +51,12 @@ export default defineLogicFunction({
```
사용 가능한 트리거 유형:
-* **httpRoute**: **`/s/` 엔드포인트** 아래의 HTTP 경로와 메서드로 함수를 노출합니다:
-> 예: `path: '/post-card/create'`는 `https://your-twenty-server.com/s/post-card/create`에서 호출할 수 있습니다
+* **httpRoute**: 워크스페이스의 **functions base URL**(Twenty가 `TWENTY_FUNCTIONS_URL`로 주입하는 값, Twenty Cloud에서는 워크스페이스별 전용 도메인임)에서 HTTP 경로와 메서드로 함수를 노출합니다.
+> 예: `path: '/post-card/create'`는 `https://your-workspace.withtwenty.com/post-card/create`에서 호출할 수 있습니다
+
+
+레거시 `/s/` prefix 경로(`https://your-twenty-server.com/s/post-card/create`)는 **Twenty Cloud에서 사용 중단(deprecated)** 되었으며 **2026-07-24**에 비활성화됩니다. 격리된 functions 도메인을 구성하지 않는 셀프 호스팅 및 로컬 인스턴스에서는 계속 사용할 수 있습니다. `TWENTY_FUNCTIONS_URL`이 설정되어 있을 경우 해당 값을 사용하고, 그렇지 않은 경우에는 `\/s/\`를 대신 사용하십시오.
+
(헤드리스) 프런트 컴포넌트에서 라우트로 트리거되는 로직 함수를 호출하려면 [로직 함수 호출하기](/l/ko/developers/extend/apps/layout/front-components#calling-a-logic-function)를 참고하세요.
diff --git a/packages/twenty-docs/l/ko/developers/extend/apps/logic/overview.mdx b/packages/twenty-docs/l/ko/developers/extend/apps/logic/overview.mdx
index ff03921d96..ae6fc85f6b 100644
--- a/packages/twenty-docs/l/ko/developers/extend/apps/logic/overview.mdx
+++ b/packages/twenty-docs/l/ko/developers/extend/apps/logic/overview.mdx
@@ -40,13 +40,13 @@ Twenty 앱의 **로직 계층**은 *실행되는* 코드로, HTTP 요청, 크론
로직 함수는 하나 이상의 트리거를 선택합니다. 아래의 각 항목은 `defineLogicFunction()`의 개별 필드입니다:
-| 트리거 | 실행 시점 | 설정 |
-| -------------- | ---------------------------------------------- | ------------------------------- |
-| **HTTP 경로** | 요청이 `/s/\` 엔드포인트에 도달할 때 | `httpRouteTriggerSettings` |
-| **크론** | CRON 표현식이 일치할 때 | `cronTriggerSettings` |
-| **데이터베이스 이벤트** | 워크스페이스 레코드가 생성, 업데이트 또는 삭제될 때 | `databaseEventTriggerSettings` |
-| **AI 도구** | Twenty AI 기능이 사용자의 함수를 호출하기로 결정할 때 | `toolTriggerSettings` |
-| **워크플로우 액션** | 워크플로우 단계가 사용자의 함수를 호출할 때 | `workflowActionTriggerSettings` |
+| 트리거 | 실행 시점 | 설정 |
+| -------------- | ---------------------------------- | ------------------------------- |
+| **HTTP 경로** | 요청이 함수의 공개 URL에 도달합니다. | `httpRouteTriggerSettings` |
+| **크론** | CRON 표현식이 일치할 때 | `cronTriggerSettings` |
+| **데이터베이스 이벤트** | 워크스페이스 레코드가 생성, 업데이트 또는 삭제될 때 | `databaseEventTriggerSettings` |
+| **AI 도구** | Twenty AI 기능이 사용자의 함수를 호출하기로 결정할 때 | `toolTriggerSettings` |
+| **워크플로우 액션** | 워크플로우 단계가 사용자의 함수를 호출할 때 | `workflowActionTriggerSettings` |
함수는 격리된 Node.js 프로세스의 샌드박스 환경에서 실행되며, [`defineApplication()`](/l/ko/developers/extend/apps/config/application)에 선언된 역할 범위에 맞춰 지정된 타입의 API 클라이언트를 통해 워크스페이스에 접근합니다.
diff --git a/packages/twenty-docs/l/ko/developers/extend/apps/operations/cli.mdx b/packages/twenty-docs/l/ko/developers/extend/apps/operations/cli.mdx
index 06085c6511..6be10662a8 100644
--- a/packages/twenty-docs/l/ko/developers/extend/apps/operations/cli.mdx
+++ b/packages/twenty-docs/l/ko/developers/extend/apps/operations/cli.mdx
@@ -4,7 +4,25 @@ description: "`yarn twenty`는 함수를 실행하고, 로그를 스트리밍하
icon: terminal
---
-`dev`, `dev:build`, `dev:add`, `dev:typecheck` 외에도, `yarn twenty` CLI는 함수 실행, 로그 보기, 앱 설치 관리용 명령을 제공합니다.
+`yarn twenty` CLI는 앱과 관련된 모든 작업을 위한 인터페이스입니다. 전체 명령어 목록:
+
+| 명령 | 하는 일 | 문서화 위치 |
+| ----------------------------------------------- | ---------------------------------------------------------- | ---------------------------------------------------------------------------------------- |
+| `dev` | 소스 파일을 감시하고 변경 사항을 실시간으로 동기화합니다. | [빠른 시작](/l/ko/developers/extend/apps/getting-started/quick-start) |
+| `플랜` | 변경 사항을 적용하지 않고 메타데이터 변경 내용을 미리 봅니다. | [동기화 및 복구](/l/ko/developers/extend/apps/operations/sync-and-recovery#previewing-changes-plan) |
+| `적용` | 계획을 표시한 후 메타데이터 변경 사항을 적용합니다. | [동기화 및 복구](/l/ko/developers/extend/apps/operations/sync-and-recovery) |
+| `dev:build` | 앱을 컴파일하고 API 클라이언트를 생성합니다 (`--tarball`을 사용하면 `.tgz`로 패키징). | [게시하기](/l/ko/developers/extend/apps/operations/publishing) |
+| `dev:typecheck` | TypeScript 타입 검사를 실행합니다. | [테스트](/l/ko/developers/extend/apps/operations/testing) |
+| `dev:add` | 새 엔터티를 스캐폴딩합니다. | [스캐폴딩](/l/ko/developers/extend/apps/getting-started/scaffolding) |
+| `dev:generate-client` | 타입이 지정된 API 클라이언트를 다시 생성합니다. | 이 페이지 |
+| `dev:function:exec` / `dev:function:logs` | 함수를 실행하고 해당 로그를 스트리밍합니다. | 이 페이지 |
+| `dev:translations-extract` | 번역 가능한 문자열을 `locales/` 카탈로그로 추출합니다. | [번역](/l/ko/developers/extend/apps/translations/overview) |
+| `dev:catalog-sync` | 마켓플레이스 카탈로그 동기화를 트리거합니다. | [게시하기](/l/ko/developers/extend/apps/operations/publishing#how-marketplace-discovery-works) |
+| `app:publish` / `app:install` / `app:uninstall` | 릴리스 라이프사이클 | [게시하기](/l/ko/developers/extend/apps/operations/publishing) 및 이 페이지 |
+| `docker:*` | 로컬 Twenty 서버 컨테이너를 관리합니다. | [로컬 서버](/l/ko/developers/extend/apps/getting-started/local-server) |
+| `remote:*` | 서버 연결을 관리합니다. | 이 페이지 |
+
+모든 명령은 기본 원격 대신 특정 원격을 대상으로 하기 위해 `-r, --remote \`을(를) 허용합니다.
## 함수 실행(`yarn twenty dev:function:exec`)
@@ -20,8 +38,9 @@ yarn twenty dev:function:exec -u e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf
# Pass a JSON payload
yarn twenty dev:function:exec -n create-new-post-card -p '{"name": "Hello"}'
-# Execute the post-install function
+# Execute the install hooks
yarn twenty dev:function:exec --postInstall
+yarn twenty dev:function:exec --preInstall
```
## 함수 로그 보기(`yarn twenty dev:function:logs`)
@@ -100,6 +119,12 @@ yarn twenty remote:list
# Set the active remote
yarn twenty remote:use
+
+# Check that the active remote's authentication is still valid
+yarn twenty remote:status
+
+# Remove a remote
+yarn twenty remote:remove
```
자격 증명은 `~/.twenty/config.json`에 저장됩니다.
diff --git a/packages/twenty-docs/l/ko/developers/extend/apps/operations/publishing.mdx b/packages/twenty-docs/l/ko/developers/extend/apps/operations/publishing.mdx
index 9faf035d05..f31de0fa0e 100644
--- a/packages/twenty-docs/l/ko/developers/extend/apps/operations/publishing.mdx
+++ b/packages/twenty-docs/l/ko/developers/extend/apps/operations/publishing.mdx
@@ -229,7 +229,7 @@ yarn twenty dev:catalog-sync
# yarn twenty dev:catalog-sync --remote production
```
-마켓플레이스에 표시되는 메타데이터는 `defineApplication()` 구성에서 가져옵니다 — `displayName`, `description`, `author`, `category`, `logoUrl`, `screenshots`, `aboutDescription`, `websiteUrl`, `termsUrl` 같은 필드입니다.
+마켓플레이스에 표시되는 메타데이터는 `defineApplication()` 구성에서 가져옵니다. 위의 [Marketplace metadata](#marketplace-metadata)를 참고하세요.
앱에서 `defineApplication()`에 `aboutDescription`을 정의하지 않으면, 마켓플레이스는 소개 페이지 콘텐츠로 npm에 게시된 패키지의 `README.md`를 자동으로 사용합니다. 즉, npm과 Twenty 마켓플레이스 모두에서 하나의 README만 관리하면 됩니다. 마켓플레이스에서 다른 설명을 사용하려면 `aboutDescription`을 명시적으로 설정하세요.
diff --git a/packages/twenty-docs/l/ko/developers/extend/apps/operations/sync-and-recovery.mdx b/packages/twenty-docs/l/ko/developers/extend/apps/operations/sync-and-recovery.mdx
index 1322cdbeb7..731a3b1ab4 100644
--- a/packages/twenty-docs/l/ko/developers/extend/apps/operations/sync-and-recovery.mdx
+++ b/packages/twenty-docs/l/ko/developers/extend/apps/operations/sync-and-recovery.mdx
@@ -15,33 +15,44 @@ icon: compass
| 원하는 작업… | 명령 | 노트 |
| ---------------------------- | ----------------------------------- | ------------------------------------------------------------------------------------------------------------------- |
| 라이브 동기화로 로컬에서 반복 개발 | `yarn twenty dev` | 파일을 감시하고 변경될 때마다 동기화합니다. |
-| 한 번만 동기화하고 종료 (CI, 스크립트, 훅) | `yarn twenty dev --once` | 한 번 빌드 + 동기화한 뒤 종료합니다. |
-| 변경 사항을 **실제로 적용하지 않고** 미리 보기 | `yarn twenty dev --once --dry-run` | 차이를 계산해 출력만 하고, 아무것도 기록하지 않습니다. |
+| 한 번만 동기화하고 종료 (CI, 스크립트, 훅) | `yarn twenty apply` | 한 번 빌드 + 동기화한 뒤 종료합니다. 파괴적인 변경 확인을 건너뛰려면 `--force`를 추가하세요. |
+| 변경 사항을 **실제로 적용하지 않고** 미리 보기 | `yarn twenty plan` | 차이를 계산해 출력만 하고, 아무것도 기록하지 않습니다. |
| 워크스페이스에서 앱 제거 | `yarn twenty app:uninstall` | 프롬프트를 건너뛰려면 `--yes`를 추가하세요. |
| 타르볼을 서버로 전송 | `yarn twenty app:publish --private` | `package.json`의 버전이 **엄격하게 더 높아야** 합니다. 자세한 내용은 [Publishing](/l/ko/developers/extend/apps/operations/publishing)을 참고하세요. |
| 마켓플레이스(npm)에 게시 | `yarn twenty app:publish` | — |
| 배포된 버전 설치 / 업그레이드 | `yarn twenty app:install` | 현재 배포된 버전을 설치합니다. |
| 로컬 서버를 초기화하고 깨끗하게 시작 | `yarn twenty docker:reset` | 로컬 데이터 **전체**를 삭제합니다. 최후의 수단입니다. |
+
+`yarn twenty dev --once` 및 `yarn twenty dev --once --dry-run`은 여전히 `yarn twenty apply`와 `yarn twenty plan`의 더 이상 사용되지 않는 별칭으로 작동합니다.
+
+
### 로컬 동기화에는 버전 증가가 필요 없음
엄격히 증가하는 `version` 규칙(`deploy` 시 `VERSION_ALREADY_EXISTS`, `install` 시 `APP_ALREADY_INSTALLED` / `CANNOT_DOWNGRADE_APPLICATION`)은 **`app:publish` / `app:install`**, 즉 릴리스 경로에만 적용됩니다. `yarn twenty dev`는 매니페스트를 제자리에서 동기화하므로 버전을 변경할 필요가 없습니다. 따라서 반복 개발을 위해 `package.json`을 수정할 필요가 없습니다. 로컬 변경을 테스트하기 위해 버전을 올리고 있다면, 개발 루프가 아니라 릴리스 경로를 사용하고 있는 것입니다.
## 동기화 출력 읽기
-각 동기화는 적용된(또는 `--dry-run`일 경우 적용될) 메타데이터 변경 사항을 출력합니다.
+각 동기화는 적용된 메타데이터 변경 사항(또는 `plan`으로 했을 때는 적용될 변경 사항)을 Terraform 스타일로 출력합니다. 각 엔티티마다 해당 속성이 포함된 하나의 블록이 출력되고, 그 뒤에 요약 한 줄이 이어집니다:
```text filename="Terminal"
-Metadata changes: 2 created, 1 updated, 1 deleted
- created objectMetadata rocket
- created fieldMetadata timelineActivities
- updated fieldMetadata launchedAt
- deleted pageLayout legacyTab
-✓ Synced
+ # objectMetadata "rocket" will be created
+ + icon = "IconRocket"
+ + labelSingular = "Rocket"
+ + ...
+
+ # fieldMetadata "launchedAt" will be updated
+ ~ isNullable = false -> true
+
+Plan: 2 to add, 1 to change, 1 to destroy.
+
+✓ Synced My App (4 files)
```
이 출력이 1차 진단 도구입니다. 어떤 객체, 필드, 레이아웃이 변경되었는지 정확히 알려주므로, UI를 확인하기 전에 동기화가 예상대로 동작했는지 검증할 수 있습니다.
+파괴적인 변경(`to destroy`)은 무엇을 삭제하는지와 함께 나열됩니다(예: `objectMetadata "auditNote" — drops the table and all its rows`), 그리고 대화형 확인이 필요하거나, 스크립트에서는 `--force`가 필요합니다.
+
동기화가 단일 엔티티에서 실패하면, 오류 메시지에 문제의 엔티티와 그 `universalIdentifier`가 함께 표시됩니다. 예를 들면 다음과 같습니다.
```text
@@ -50,39 +61,42 @@ Migration action 'create' for 'fieldMetadata' (universalIdentifier: 2020...4337)
그 식별자를 사용해 매니페스트(필요하다면 워크스페이스)에서 해당 엔티티를 찾아, 어떤 것이 충돌하는지 추측하지 말고 정확히 확인하세요.
-## 변경 사항 미리 보기(dry run)
+## 변경 사항 미리 보기(plan)
-`yarn twenty dev --once --dry-run`은 매니페스트를 빌드하고, 서버에 마이그레이션 계획을 요청한 뒤, 이를 출력만 합니다. **아무것도 실제로 적용하지 않습니다**. "이 동기화로 무엇이 바뀔까?"에 안전하게 답할 수 있는 방법으로, 실제로 적용하기 전에 확인할 수 있습니다.
+`yarn twenty plan`은 매니페스트를 빌드하고, 서버에 마이그레이션 계획을 요청한 뒤, 이를 출력만 합니다. **아무것도 실제로 적용하지 않습니다**. "이 동기화로 무엇이 바뀔까?"에 안전하게 답할 수 있는 방법으로, 실제로 적용하기 전에 확인할 수 있습니다.
```bash filename="Terminal"
-yarn twenty dev --once --dry-run
+yarn twenty plan
```
```text filename="Terminal"
Building manifest...
-Computing metadata diff (dry run, nothing will be applied)...
-Metadata changes: 1 created, 1 updated
- created fieldMetadata timelineActivities
- updated objectMetadata rocket
-✓ Dry run complete for My App — no changes were applied
+Computing metadata plan (read-only, nothing will be applied)...
+
+ # fieldMetadata "timelineActivities" will be created
+ + ...
+
+Plan: 1 to add, 1 to change, 0 to destroy.
+
+✓ Plan complete for My App — no changes were applied
```
-dry run은 다음과 같습니다.
+플랜:
* **아무것도 기록하지 않습니다**. 메타데이터 마이그레이션, 애플리케이션 레코드 업데이트, 기본 역할/탭 변경, API 클라이언트 생성이 모두 수행되지 않습니다.
* 실제 동기화에서 적용될 **동일한 diff**를 반환하므로, 생성/업데이트/삭제되는 엔티티를 미리 검토할 수 있습니다.
* 위험한 변경 전에, AI가 생성한 변경 사항을 검토할 때, 또는 예기치 않은 변경이 적용되려 하면 실패해야 하는 스크립트에서 유용합니다.
-dry run은 **메타데이터** 변경만 미리 보여 주며, 앱이 최소 한 번 이상 동기화된 상태(워크스페이스가 이 앱을 알고 있는 상태)여야 합니다. 한 번도 동기화된 적이 없는 앱에 대해 dry run을 실행하면, 서버는 앱이 설치되지 않았다고 보고합니다. 먼저 `yarn twenty dev`를 한 번 실행하세요.
+plan은 **메타데이터** 변경만 미리 보여 주며, 앱이 최소 한 번 이상 동기화된 상태(워크스페이스가 이 앱을 알고 있는 상태)여야 합니다. 한 번도 동기화된 적이 없는 앱에 대해 dry run을 실행하면, 서버는 앱이 설치되지 않았다고 보고합니다. 먼저 `yarn twenty dev`를 한 번 실행하세요.
## 복구 단계별 절차
로컬 메타데이터가 잘못된 것처럼 보일 때는, 아래 순서대로 단계를 진행하면서 문제가 해결되는 즉시 멈추세요. 각 단계는 이전 단계보다 더 많은 영향을 미칩니다.
-1. **재동기화.** `yarn twenty dev --once`를 다시 실행하세요. 동기화는 멱등적이므로, 깨끗한 매니페스트를 다시 실행해도 안전하며 일시적인 오류가 이 방식으로 해결되는 경우가 많습니다.
-2. **계획 미리 보기.** `yarn twenty dev --once --dry-run`을 실행해, 다음 동기화가 정확히 무엇을 변경하려 하는지 실제로 적용하지 않고 확인하세요.
+1. **재동기화.** `yarn twenty apply`를 다시 실행하세요. 동기화는 멱등적이므로, 깨끗한 매니페스트를 다시 실행해도 안전하며 일시적인 오류가 이 방식으로 해결되는 경우가 많습니다.
+2. **계획 미리 보기.** `yarn twenty plan`을 실행해, 다음 동기화가 실제로 적용하지 않고 정확히 무엇을 변경하려 하는지 확인하세요.
3. **명시적인 오류 읽기.** 동기화가 실패하면, 메시지에 포함된 메타데이터 타입과 `universalIdentifier`(위 참조)를 확인한 뒤, 매니페스트에서 해당 엔티티를 찾으세요. 충돌은 보통 중복되었거나 재사용된 식별자를 가리킵니다.
4. **삭제 후 재설치.** `yarn twenty app:uninstall`을 실행한 뒤, 다시 동기화합니다(`yarn twenty dev`). 이 방법은 워크스페이스의 나머지 부분은 그대로 둔 채, 앱의 메타데이터를 깨끗한 상태에서 다시 구축합니다.
5. **전체 초기화(최후의 수단).** `yarn twenty docker:reset`을 실행한 뒤, 다시 시드하고 재동기화합니다.
diff --git a/packages/twenty-docs/l/ko/developers/extend/apps/operations/testing.mdx b/packages/twenty-docs/l/ko/developers/extend/apps/operations/testing.mdx
index c17fde0c14..71443acaf2 100644
--- a/packages/twenty-docs/l/ko/developers/extend/apps/operations/testing.mdx
+++ b/packages/twenty-docs/l/ko/developers/extend/apps/operations/testing.mdx
@@ -78,6 +78,13 @@ yarn add -D vitest vite-tsconfig-paths
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 ?? '';
+
+// 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({
@@ -88,66 +95,74 @@ export default defineConfig({
test: {
testTimeout: 120_000,
hookTimeout: 120_000,
+ fileParallelism: false,
include: ['src/**/*.integration-test.ts'],
- setupFiles: ['src/__tests__/setup-test.ts'],
+ globalSetup: ['src/__tests__/global-setup.ts'],
env: {
- TWENTY_API_URL: 'http://localhost:2020',
- TWENTY_API_KEY: 'your-api-key',
+ TWENTY_API_URL,
+ TWENTY_API_KEY,
},
},
});
```
-테스트 실행 전에 서버에 접근 가능한지 확인하는 설정 파일을 생성하세요:
+서버에 연결할 수 있는지 확인하고, SDK용 테스트 구성 파일(`~/.twenty/config.test.json`)을 작성한 다음, 테스트 실행 전에 앱을 동기화하는 글로벌 설정 파일을 생성합니다:
-```ts src/__tests__/setup-test.ts
+```ts src/__tests__/global-setup.ts
import * as fs from 'fs';
import * as os from 'os';
import * as path from 'path';
-import { beforeAll } from 'vitest';
-const TWENTY_API_URL = process.env.TWENTY_API_URL ?? 'http://localhost:2020';
-const TEST_CONFIG_DIR = path.join(os.tmpdir(), '.twenty-sdk-test');
+import { appDevOnce, appUninstall } from 'twenty-sdk/cli';
+
+const APP_PATH = process.cwd();
+const CONFIG_DIR = path.join(os.homedir(), '.twenty');
+
+export async function setup() {
+ const apiUrl = process.env.TWENTY_API_URL!;
+ const apiKey = process.env.TWENTY_API_KEY!;
-beforeAll(async () => {
// Verify the server is running
- const response = await fetch(`${TWENTY_API_URL}/healthz`);
-
+ const response = await fetch(`${apiUrl}/healthz`);
if (!response.ok) {
- throw new Error(
- `Twenty server is not reachable at ${TWENTY_API_URL}. ` +
- 'Start the server before running integration tests.',
- );
+ throw new Error(`Twenty server is not reachable at ${apiUrl}.`);
}
- // Write a temporary config for the SDK
- fs.mkdirSync(TEST_CONFIG_DIR, { recursive: true });
-
+ // Write the SDK's test config (the CLI reads config.test.json when NODE_ENV=test)
+ fs.mkdirSync(CONFIG_DIR, { recursive: true });
fs.writeFileSync(
- path.join(TEST_CONFIG_DIR, 'config.json'),
+ path.join(CONFIG_DIR, 'config.test.json'),
JSON.stringify({
- remotes: {
- local: {
- apiUrl: process.env.TWENTY_API_URL,
- apiKey: process.env.TWENTY_API_KEY,
- },
- },
+ remotes: { local: { apiUrl, apiKey } },
defaultRemote: 'local',
}, null, 2),
);
-});
+
+ // Start from a clean slate, then sync the app
+ await appUninstall({ appPath: APP_PATH }).catch(() => {});
+
+ const result = await appDevOnce({ appPath: APP_PATH });
+ if (!result.success) {
+ throw new Error(`Dev sync failed: ${result.error?.message}`);
+ }
+}
+
+export async function teardown() {
+ await appUninstall({ appPath: APP_PATH });
+}
```
## 프로그래매틱 SDK API
`twenty-sdk/cli` 서브 경로는 테스트 코드에서 직접 호출할 수 있는 함수를 내보냅니다:
-| 함수 | 설명 |
-| -------------- | --------------------- |
-| `appBuild` | 앱을 빌드하고 필요하면 타르볼로 패키징 |
-| `appDeploy` | 타르볼을 서버로 업로드 |
-| `appInstall` | 활성 워크스페이스에 앱 설치 |
-| `appUninstall` | 활성 워크스페이스에서 앱 제거 |
+| 함수 | 설명 |
+| -------------- | --------------------------------------------- |
+| `appBuild` | 앱을 빌드하고 필요하면 타르볼로 패키징 |
+| `appDeploy` | 타르볼을 서버로 업로드 |
+| `appDevOnce` | 앱을 한 번만 빌드하고 동기화합니다(`yarn twenty apply`와 동일). |
+| `appInstall` | 활성 워크스페이스에 앱 설치 |
+| `appUninstall` | 활성 워크스페이스에서 앱 제거 |
각 함수는 `success: boolean`과 `data` 또는 `error`를 포함한 결과 객체를 반환합니다.
@@ -238,64 +253,10 @@ yarn test:watch
yarn twenty dev:typecheck
```
-이는 `tsc --noEmit`를 실행하고 모든 타입 오류를 보고합니다.
+이는 앱의 `tsconfig.json`에 대해 `tsc --noEmit`를 실행하고 모든 타입 오류를 보고합니다. 스캐폴딩된 앱에는 테스트 파일까지 포함하는(`tsconfig.spec.json`) `yarn typecheck` 스크립트도 포함되어 있습니다.
## GitHub Actions로 CI
-스캐폴더가 `.github/workflows/ci.yml`에 바로 사용할 수 있는 GitHub Actions 워크플로를 생성합니다. `main`으로의 푸시와 풀 리퀘스트마다 통합 테스트를 자동으로 실행합니다.
+스캐폴더는 `.github/workflows/ci.yml`에 바로 사용할 수 있는 워크플로를 생성합니다. `main`으로의 모든 푸시와 모든 풀 리퀘스트마다, 러너에서 임시 Twenty 서버를 실행하고(`twentyhq/twenty/.github/actions/spawn-twenty-app-dev-test` 액션을 통해), 그 서버를 가리키도록 설정된 `TWENTY_API_URL` / `TWENTY_API_KEY`와 함께 `yarn lint`, `yarn typecheck`, `yarn test:unit`, `yarn test`를 실행합니다. 시크릿은 필요 없으며, 워크플로 상단의 `TWENTY_VERSION` 환경 변수로 서버 버전을 고정할 수 있습니다.
-워크플로:
-
-1. 코드를 체크아웃합니다
-2. `twentyhq/twenty/.github/actions/spawn-twenty-docker-image` 액션을 사용해 임시 Twenty 서버를 구동합니다
-3. `yarn install --immutable`로 종속성을 설치합니다
-4. 액션 출력에서 주입된 `TWENTY_API_URL` 및 `TWENTY_API_KEY`로 `yarn test`를 실행합니다
-
-```yaml .github/workflows/ci.yml
-name: CI
-
-on:
- push:
- branches:
- - main
- pull_request: {}
-
-env:
- TWENTY_VERSION: latest
-
-jobs:
- test:
- runs-on: ubuntu-latest
- steps:
- - name: Checkout
- uses: actions/checkout@v4
-
- - name: Spawn Twenty instance
- id: twenty
- uses: twentyhq/twenty/.github/actions/spawn-twenty-docker-image@main
- with:
- twenty-version: ${{ env.TWENTY_VERSION }}
- github-token: ${{ secrets.GITHUB_TOKEN }}
-
- - name: Enable Corepack
- run: corepack enable
-
- - name: Setup Node.js
- uses: actions/setup-node@v4
- with:
- node-version-file: '.nvmrc'
- cache: 'yarn'
-
- - name: Install dependencies
- run: yarn install --immutable
-
- - name: Run integration tests
- run: yarn test
- env:
- TWENTY_API_URL: ${{ steps.twenty.outputs.server-url }}
- TWENTY_API_KEY: ${{ steps.twenty.outputs.access-token }}
-```
-
-별도의 시크릿을 구성할 필요가 없습니다 — `spawn-twenty-docker-image` 액션이 러너 내에서 일시적인 Twenty 서버를 직접 시작하고 연결 정보를 출력합니다. `GITHUB_TOKEN` 시크릿은 GitHub에서 자동으로 제공됩니다.
-
-`latest` 대신 특정 Twenty 버전을 고정하려면 워크플로 상단의 `TWENTY_VERSION` 환경 변수를 변경하세요.
+스캐폴딩된 두 워크플로(`ci.yml` 및 `cd.yml` 배포 파이프라인)에 대한 전체 단계별 안내는 [Publishing → Automated CI/CD](/l/ko/developers/extend/apps/operations/publishing#automated-cicd-scaffolded-workflows)를 참고하세요.
diff --git a/packages/twenty-docs/l/ko/developers/extend/apps/tutorials/document-generator/building-the-ui.mdx b/packages/twenty-docs/l/ko/developers/extend/apps/tutorials/document-generator/building-the-ui.mdx
index dfd91a1e6b..f4af4aa841 100644
--- a/packages/twenty-docs/l/ko/developers/extend/apps/tutorials/document-generator/building-the-ui.mdx
+++ b/packages/twenty-docs/l/ko/developers/extend/apps/tutorials/document-generator/building-the-ui.mdx
@@ -84,9 +84,11 @@ const GenerateDocumentForm = () => {
}, []);
const generate = async () => {
- const apiBaseUrl = process.env.TWENTY_API_URL;
+ // Prefer the injected functions URL; fall back to the legacy /s prefix (self-hosted/local)
+ const functionsBaseUrl =
+ process.env.TWENTY_FUNCTIONS_URL || `${process.env.TWENTY_API_URL}/s`;
const token = process.env.TWENTY_APP_ACCESS_TOKEN ?? process.env.TWENTY_API_KEY;
- const res = await fetch(`${apiBaseUrl}/s/documents/generate`, {
+ const res = await fetch(`${functionsBaseUrl}/documents/generate`, {
method: 'POST',
headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` },
body: JSON.stringify({ templateId, recordId }),
@@ -167,7 +169,9 @@ const DocumentViewer = () => {
const recordId = useFrontComponentExecutionContext((c) => c.recordId ?? null);
// ...load { content, file } for recordId, then derive the links:
const pdfUrl = document.file?.[0]?.url;
- const webUrl = `${process.env.TWENTY_API_URL ?? ''}/s/documents/view?id=${recordId}`;
+ const functionsBaseUrl =
+ process.env.TWENTY_FUNCTIONS_URL || `${process.env.TWENTY_API_URL ?? ''}/s`;
+ const webUrl = `${functionsBaseUrl}/documents/view?id=${recordId}`;
// Render the template body, plus quick links to the web page and the PDF.
// Links open in a new tab so they don't navigate the embedded component.
diff --git a/packages/twenty-docs/l/ko/developers/extend/apps/tutorials/document-generator/http-routes.mdx b/packages/twenty-docs/l/ko/developers/extend/apps/tutorials/document-generator/http-routes.mdx
index e47b9b4380..76f2297f94 100644
--- a/packages/twenty-docs/l/ko/developers/extend/apps/tutorials/document-generator/http-routes.mdx
+++ b/packages/twenty-docs/l/ko/developers/extend/apps/tutorials/document-generator/http-routes.mdx
@@ -9,7 +9,12 @@ description: HTTP를 통해 함수를 트리거하고 문서를 웹 페이지로
* UI가 문서를 생성하기 위해 호출하는 **POST** 엔드포인트, 그리고
* 문서를 인쇄 가능한 웹 페이지로 렌더링하는 공개 **GET** 엔드포인트입니다.
-둘 다 `httpRouteTriggerSettings`를 사용합니다. 앱 경로는 Twenty 서버의 `/s` 아래에서 제공됩니다 (예: `http://localhost:2020/s/documents/generate`).
+둘 다 `httpRouteTriggerSettings`를 사용합니다. 로컬 개발 서버에서는 앱 라우트가 `/s` 프리픽스 아래에서 제공됩니다(예: `http://localhost:2020/s/documents/generate`).
+
+
+Twenty Cloud에서는 워크스페이스 전용 Functions 도메인에서 라우트가 제공됩니다. 이 도메인은 Twenty가 `/s` 프리픽스 없이 `TWENTY_FUNCTIONS_URL`로 주입하는 URL입니다. 해당 환경에서 `/s` 프리픽스는 더 이상 사용되지(deprecated) 않으며, 셀프 호스팅 및 로컬 인스턴스에서만 유지됩니다.
+[로직 함수 호출하기](/l/ko/developers/extend/apps/layout/front-components#calling-a-logic-function)를 참고하세요.
+
## POST 경로 — 온디맨드로 생성하기
diff --git a/packages/twenty-docs/l/ko/developers/extend/apps/tutorials/document-generator/publishing.mdx b/packages/twenty-docs/l/ko/developers/extend/apps/tutorials/document-generator/publishing.mdx
index 2886ac5242..8d11830dda 100644
--- a/packages/twenty-docs/l/ko/developers/extend/apps/tutorials/document-generator/publishing.mdx
+++ b/packages/twenty-docs/l/ko/developers/extend/apps/tutorials/document-generator/publishing.mdx
@@ -69,10 +69,11 @@ CI에서 수행하는 것과 동일한 검증 단계를 실행하세요:
yarn lint # oxlint
yarn typecheck # tsgo
yarn test:unit # unit tests
-yarn twenty dev --once --dry-run # preview the metadata diff
+yarn twenty plan # preview the metadata diff
```
-드라이 런은 서버에서 실제로 적용하지 않고 무엇이 변경될지를 그대로 출력합니다. 마지막으로 확인하기에 좋은 방법입니다. [테스트](/l/ko/developers/extend/apps/operations/testing)와
+플랜은 서버에 실제로 적용하지 않고 무엇이 변경될지를 그대로 출력합니다.
+마지막으로 확인하기에 좋은 방법입니다. [테스트](/l/ko/developers/extend/apps/operations/testing)와
[동기화 및 복구](/l/ko/developers/extend/apps/operations/sync-and-recovery)를 참조하세요.
## 게시
diff --git a/packages/twenty-docs/l/pt/developers/extend/apps/config/install-hooks.mdx b/packages/twenty-docs/l/pt/developers/extend/apps/config/install-hooks.mdx
index 63082fb8d6..7b194dfd87 100644
--- a/packages/twenty-docs/l/pt/developers/extend/apps/config/install-hooks.mdx
+++ b/packages/twenty-docs/l/pt/developers/extend/apps/config/install-hooks.mdx
@@ -4,7 +4,7 @@ description: Execute lógica antes ou depois da instalação — para popular da
icon: wrench
---
-Hooks de instalação são funções de lógica especiais que são executadas durante o ciclo de vida de instalação ou atualização. Elas compartilham o mesmo runtime de handler que as [logic functions](/l/pt/developers/extend/apps/logic/logic-functions) normais e recebem um `InstallPayload`, mas são declaradas com suas próprias funções de definição — `definePostInstallLogicFunction()` e `definePreInstallLogicFunction()` — e ficam fora do modelo de gatilhos normal (HTTP, cron, eventos de banco de dados).
+Hooks de instalação são funções de lógica especiais que são executadas durante o ciclo de vida de instalação ou atualização. Elas compartilham o mesmo runtime de handler que as [logic functions](/l/pt/developers/extend/apps/logic/logic-functions) normais e recebem um `InstallPayload` (`{ previousVersion?: string; newVersion: string }` — `previousVersion` é `undefined` em uma instalação nova), mas são declaradas com suas próprias funções de definição e ficam fora do modelo de gatilhos normal (HTTP, cron, eventos de banco de dados).
Cada aplicativo pode definir no máximo uma função de pré-instalação e no máximo uma função de pós-instalação. A geração do manifesto apresentará erro se mais de uma de cada for detectada.
@@ -19,111 +19,59 @@ Cada aplicativo pode definir no máximo uma função de pré-instalação e no m
└─────────────────────────────────────────────────────────────┘
```
-
-
+## Visão geral
-Uma função de pós-instalação é executada automaticamente assim que seu aplicativo termina de ser instalado em um workspace. O servidor a executa **depois** que os metadados do aplicativo forem sincronizados e o cliente do SDK for gerado, para que o espaço de trabalho esteja totalmente pronto para uso e o novo esquema esteja disponível. Casos de uso típicos incluem popular dados padrão, criar registros iniciais, configurar as definições do espaço de trabalho ou provisionar recursos em serviços de terceiros.
+| | `definePreInstallLogicFunction` | `definePostInstallLogicFunction` |
+| ---------------- | ---------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
+| Execuções | Antes da migração de metadados — o esquema e os dados **anteriores** ainda estão intactos | Após a migração e a geração do SDK — o **novo** esquema está em vigor |
+| Execução | Sempre síncrona; bloqueia a instalação | Assíncrona por padrão (em fila, 3 novas tentativas); modo síncrono por opt-in via `shouldRunSynchronously: true` |
+| Em caso de falha | A instalação é **abortada** antes de qualquer alteração de esquema | Assíncrono: novas tentativas até 3 vezes. Síncrono: o chamador recebe `POST_INSTALL_ERROR` (as alterações de esquema **não** são revertidas) |
+| Uso típico | Fazer backup ou corrigir dados que uma migração poderia perder; recusar uma atualização arriscada lançando uma exceção | Popular dados padrão, configurar o workspace, registrar recursos externos |
-```ts src/logic-functions/post-install.ts
-import { definePostInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define';
+**Regra geral:** use post-install como padrão. Recurra à pré-instalação somente quando a própria migração for destrutiva e você precisar interceptar o estado anterior antes que ele desapareça.
-const handler = async (payload: InstallPayload): Promise => {
- console.log('Post install logic function executed successfully!', payload.previousVersion);
-};
+| Você quer... | Usar |
+| ----------------------------------------------------------------------------------------- | ----------------------------------------------------------------------- |
+| Popular dados, configurar o workspace, registrar recursos externos | `post-install` |
+| Trabalho de longa duração que não deve bloquear a resposta da instalação | `post-install` (modo assíncrono padrão, com novas tentativas do worker) |
+| Configuração rápida da qual o chamador depende imediatamente após o retorno da instalação | `post-install` com `shouldRunSynchronously: true` |
+| Ler ou fazer backup de dados que a próxima migração perderia | `pre-install` |
+| Rejeitar uma atualização que corromperia dados existentes | `pre-install` (lançar uma exceção no manipulador) |
+| Reconciliação em cada atualização | Qualquer um dos hooks com `shouldRunOnVersionUpgrade: true` |
-export default definePostInstallLogicFunction({
- universalIdentifier: 'f7a2b9c1-3d4e-5678-abcd-ef9876543210',
- name: 'post-install',
- description: 'Runs after installation to set up the application.',
- timeoutSeconds: 300,
- shouldRunOnVersionUpgrade: false,
- shouldRunSynchronously: false,
- handler,
-});
-```
+## Comportamento compartilhado por ambos os hooks
-Você também pode executar manualmente a função de pós-instalação a qualquer momento usando a CLI:
+* A configuração é uma config de `defineLogicFunction` menos as configurações de gatilho, mais `shouldRunOnVersionUpgrade`.
+* **Quando é executado**: apenas em instalações novas, por padrão. Defina `shouldRunOnVersionUpgrade: true` para também executar em atualizações. Use `previousVersion` / `newVersion` para ramificar com base no caminho de atualização.
+* **Idempotência é importante**: o post-install assíncrono pode ser executado novamente, e qualquer um dos hooks é reexecutado em atualizações quando `shouldRunOnVersionUpgrade` está ativado.
+* O ambiente usual de logic-function (`APPLICATION_ID`, `APP_ACCESS_TOKEN`, `API_URL`) é injetado, para que você possa chamar a Twenty API com o token do seu app.
+* O hook é anexado automaticamente ao manifesto da aplicação em tempo de build (`preInstallLogicFunction` / `postInstallLogicFunction`) — nada para referenciar em [`defineApplication()`](/l/pt/developers/extend/apps/config/application).
+* O `timeoutSeconds` padrão é 300 para permitir tarefas de configuração mais longas, como o pré-carregamento de dados.
+* **Não é executado em modo de desenvolvimento**: `yarn twenty dev` ignora o fluxo de instalação e sincroniza os arquivos diretamente, portanto os hooks nunca são executados ali. Em vez disso, acione-os manualmente:
```bash filename="Terminal"
yarn twenty dev:function:exec --postInstall
-```
-
-Pontos-chave:
-* As funções de pós-instalação usam `definePostInstallLogicFunction()` — uma variante especializada que omite as configurações de gatilho (`cronTriggerSettings`, `databaseEventTriggerSettings`, `httpRouteTriggerSettings`, `toolTriggerSettings`, `workflowActionTriggerSettings`).
-* O manipulador recebe um `InstallPayload` com `{ previousVersion?: string; newVersion: string }` — `newVersion` é a versão que está sendo instalada, e `previousVersion` é a versão que foi instalada anteriormente (ou `undefined` em uma instalação nova). Use esses valores para distinguir instalações novas de atualizações e para executar lógica de migração específica da versão.
-* **Quando o hook é executado**: apenas em instalações novas, por padrão. Passe `shouldRunOnVersionUpgrade: true` se você também quiser que ele seja executado quando o app for atualizado a partir de uma versão anterior. Quando omitida, a flag tem valor padrão `false` e as atualizações ignoram o hook.
-* **Modelo de execução — assíncrono por padrão, síncrono opcional**: a flag `shouldRunSynchronously` controla *como* a pós-instalação é executada.
- * `shouldRunSynchronously: false` *(padrão)* — o hook é **enfileirado na fila de mensagens** com `retryLimit: 3` e é executado de forma assíncrona em um worker. A resposta da instalação retorna assim que o job é enfileirado, então um manipulador lento ou com falha não bloqueia quem chamou. O worker tentará novamente até três vezes. **Use isto para jobs de longa duração** — popular grandes conjuntos de dados, chamar APIs de terceiros lentas, provisionar recursos externos, qualquer coisa que possa exceder uma janela razoável de resposta HTTP.
- * `shouldRunSynchronously: true` — o hook é executado **inline durante o fluxo de instalação** (mesmo executor da pré-instalação). A requisição de instalação bloqueia até o manipulador terminar e, se ele lançar uma exceção, quem chamou a instalação recebe um `POST_INSTALL_ERROR`. Sem novas tentativas automáticas. **Use isto para trabalhos rápidos que precisam ser concluídos antes da resposta** — por exemplo, emitir um erro de validação para o usuário ou fazer uma configuração rápida da qual o cliente dependerá imediatamente após a chamada de instalação retornar. Tenha em mente que a migração de metadados já foi aplicada quando a pós-instalação é executada, então uma falha no modo síncrono **não** reverte as alterações de esquema — ela apenas expõe o erro.
-* Garanta que seu manipulador seja idempotente. No modo assíncrono, a fila pode tentar novamente até três vezes; em qualquer modo, o hook pode ser executado novamente em atualizações quando `shouldRunOnVersionUpgrade: true`.
-* As variáveis de ambiente `APPLICATION_ID`, `APP_ACCESS_TOKEN` e `API_URL` estão disponíveis dentro do manipulador (assim como em qualquer outra função de lógica), então você pode chamar a API da Twenty com um token de acesso de aplicativo com escopo para o seu app.
-* É permitida apenas uma função de pós-instalação por app. A geração do manifesto apresentará erro se mais de uma for detectada.
-* O `universalIdentifier`, `shouldRunOnVersionUpgrade` e `shouldRunSynchronously` da função são anexados automaticamente ao manifesto do aplicativo no campo `postInstallLogicFunction` durante o build — você não precisa referenciá-los em [`defineApplication()`](/l/pt/developers/extend/apps/config/application).
-* O tempo limite padrão é definido como 300 segundos (5 minutos) para permitir tarefas de configuração mais longas, como o pré-carregamento de dados.
-* **Não executado no modo de desenvolvimento**: quando um app é registrado localmente (via `yarn twenty dev`), o servidor pula completamente o fluxo de instalação e sincroniza arquivos diretamente pelo watcher da CLI — portanto, a pós-instalação nunca é executada no modo de desenvolvimento, independentemente de `shouldRunSynchronously`. Use `yarn twenty dev:function:exec --postInstall` para acioná-lo manualmente em um workspace em execução.
-
-
-
-
-Uma função de pré-instalação é executada automaticamente durante a instalação, **antes que a migração de metadados do workspace seja aplicada**. Ela compartilha o mesmo formato de payload que a pós-instalação (`InstallPayload`), mas está posicionada mais cedo no fluxo de instalação para poder preparar o estado do qual a próxima migração depende — usos típicos incluem fazer backup de dados, validar a compatibilidade com o novo esquema ou arquivar registros que estão prestes a ser reestruturados ou removidos.
-
-```ts src/logic-functions/pre-install.ts
-import { definePreInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define';
-
-const handler = async (payload: InstallPayload): Promise => {
- console.log('Pre install logic function executed successfully!', payload.previousVersion);
-};
-
-export default definePreInstallLogicFunction({
- universalIdentifier: 'a1b2c3d4-5678-90ab-cdef-1234567890ab',
- name: 'pre-install',
- description: 'Runs before installation to prepare the application.',
- timeoutSeconds: 300,
- shouldRunOnVersionUpgrade: true,
- handler,
-});
-```
-
-Você também pode executar manualmente a função de pré-instalação a qualquer momento usando a CLI:
-
-```bash filename="Terminal"
yarn twenty dev:function:exec --preInstall
```
-Pontos-chave:
-* Funções de pré-instalação usam `definePreInstallLogicFunction()` — a mesma configuração especializada da pós-instalação, apenas anexada a um ponto diferente do ciclo de vida.
-* Os manipuladores de pré e pós-instalação recebem o mesmo tipo `InstallPayload`: `{ previousVersion?: string; newVersion: string }`. Importe-o uma vez e reutilize-o para ambos os hooks.
-* **Quando o hook é executado**: posicionado imediatamente antes da migração de metadados do workspace (`synchronizeFromManifest`). Antes de executar, o servidor realiza uma "sincronização simplificada" puramente aditiva que registra a função de pré-instalação da **nova** versão nos metadados do workspace — nada mais é alterado — e então a executa. Como essa sincronização é apenas aditiva, os objetos, campos e dados da versão anterior ainda estão intactos quando seu manipulador é executado: você pode ler e fazer backup com segurança do estado pré-migração.
-* **Modelo de execução**: a pré-instalação é executada **de forma síncrona** e **bloqueia a instalação**. Se o manipulador lançar uma exceção, a instalação é abortada antes que quaisquer alterações de esquema sejam aplicadas — o workspace permanece na versão anterior em um estado consistente. Isto é intencional: a pré-instalação é sua última chance de recusar uma atualização arriscada.
-* Assim como na pós-instalação, é permitida apenas uma função de pré-instalação por app. Ela é anexada ao manifesto do aplicativo sob `preInstallLogicFunction` automaticamente durante o build.
-* **Não é executada no modo de desenvolvimento**: igual à pós-instalação — o fluxo de instalação é totalmente ignorado para apps registrados localmente, portanto a pré-instalação nunca é executada com `yarn twenty dev`. Use `yarn twenty dev:function:exec --preInstall` para acioná-lo manualmente.
+
+
-
-
-
-Ambos os hooks fazem parte do mesmo fluxo de instalação e recebem o mesmo `InstallPayload`. A diferença é **quando** eles são executados em relação à migração de metadados do workspace, e isso muda quais dados eles podem manipular com segurança.
-
-A pré-instalação é sempre **síncrona** (ela bloqueia a instalação e pode abortá-la). A pós-instalação é **assíncrona por padrão** — enfileirada em um worker com novas tentativas automáticas — mas pode optar por execução síncrona com `shouldRunSynchronously: true`. Veja o acordeão `definePostInstallLogicFunction` acima para saber quando usar cada modo.
-
-**Use `post-install` para qualquer coisa que precise que o novo esquema exista.** Este é o caso mais comum:
-
-* Popular dados padrão (criando registros iniciais, visualizações padrão, conteúdo de demonstração) em objetos e campos recém-adicionados.
-* Registrar webhooks com serviços de terceiros agora que o app tem suas credenciais.
-* Chamar sua própria API para finalizar a configuração que depende dos metadados sincronizados.
-* Lógica idempotente de "garantir que isso exista" que deve reconciliar o estado em cada atualização — combine com `shouldRunOnVersionUpgrade: true`.
-
-Exemplo — popular um registro `PostCard` padrão após a instalação:
+É executado depois que seu app termina de ser instalado: metadados sincronizados, cliente SDK gerado, novo esquema disponível para consulta. Exemplo — popular um registro padrão em instalações novas:
```ts src/logic-functions/post-install.ts
import { definePostInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define';
-import { createClient } from './generated/client';
+import { CoreApiClient } from 'twenty-client-sdk/core';
const handler = async ({ previousVersion }: InstallPayload): Promise => {
if (previousVersion) return; // fresh installs only
- const client = createClient();
- await client.postCard.create({
- data: { title: 'Welcome to Postcard', content: 'Your first card!' },
+ const client = new CoreApiClient();
+ await client.mutation({
+ createPostCard: {
+ __args: { data: { name: 'Welcome to Postcard', content: 'Your first card!' } },
+ id: true,
+ },
});
};
@@ -133,22 +81,28 @@ export default definePostInstallLogicFunction({
description: 'Seeds a welcome post card after install.',
timeoutSeconds: 300,
shouldRunOnVersionUpgrade: false,
+ shouldRunSynchronously: false,
handler,
});
```
-**Use `pre-install` quando uma migração, de outra forma, destruiria ou corromperia dados existentes.** Como a pré-instalação roda contra o esquema *anterior* e sua falha reverte a atualização, é o lugar certo para qualquer coisa arriscada:
+A flag `shouldRunSynchronously` controla o modelo de execução:
-* **Fazer backup de dados que estão prestes a ser removidos ou reestruturados** — por exemplo, você está removendo um campo na v2 e precisa copiar seus valores para outro campo ou exportá-los para um armazenamento antes que a migração seja executada.
-* **Arquivar registros que uma nova restrição invalidaria** — por exemplo, um campo está se tornando `NOT NULL` e você precisa excluir ou corrigir linhas com valores nulos primeiro.
-* **Validar a compatibilidade e recusar a atualização se os dados atuais não puderem ser migrados de forma limpa** — lance uma exceção no manipulador e a instalação é abortada sem alterações aplicadas. Isto é mais seguro do que descobrir a incompatibilidade no meio da migração.
-* **Renomear ou reatribuir chaves de dados** antes de uma alteração de esquema que perderia a associação.
+* `false` *(padrão)* — colocado em fila na message queue (`retryLimit: 3`) e executado por um worker. A resposta da instalação retorna assim que o job é colocado na fila. **Use para trabalhos de longa duração** — popular grandes conjuntos de dados, APIs lentas de terceiros.
+* `true` — executado inline durante o fluxo de instalação. A requisição de instalação fica bloqueada até que o handler termine; um erro lançado aparece como `POST_INSTALL_ERROR` para o chamador (sem novas tentativas). **Use para trabalhos rápidos que precisam ser concluídos antes da resposta.** A migração já foi aplicada neste ponto, portanto uma falha não reverte as alterações de esquema — ela apenas expõe o erro.
-Exemplo — arquivar registros antes de uma migração destrutiva:
+
+
+
+É executado antes da migração de metadados, contra o esquema **anterior** — o lugar certo para fazer backup de dados que uma migração poderia perder ou para recusar uma atualização arriscada. Antes de executar, o servidor realiza uma "sincronização simplificada" puramente aditiva que registra apenas a função de pré-instalação da nova versão; todo o resto — objetos, campos e dados da versão anterior — permanece intocado quando seu handler é executado.
+
+A pré-instalação é sempre **síncrona** e bloqueia a instalação. Se o handler lançar uma exceção, a instalação é abortada antes de qualquer alteração de esquema — o workspace permanece na versão anterior em um estado consistente. Isto é intencional: a pré-instalação é sua última chance de recusar uma atualização arriscada.
+
+Exemplo — copiar os valores de um campo legado antes que a migração o remova:
```ts src/logic-functions/pre-install.ts
import { definePreInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define';
-import { createClient } from './generated/client';
+import { CoreApiClient } from 'twenty-client-sdk/core';
const handler = async ({ previousVersion, newVersion }: InstallPayload): Promise => {
// Only the 1.x → 2.x upgrade drops the legacy `notes` field.
@@ -156,24 +110,24 @@ const handler = async ({ previousVersion, newVersion }: InstallPayload): Promise
return;
}
- const client = createClient();
- const legacyRecords = await client.postCard.findMany({
- where: { notes: { isNotNull: true } },
+ const client = new CoreApiClient();
+ const { postCards } = await client.query({
+ postCards: {
+ __args: { filter: { notes: { isNot: null } } },
+ edges: { node: { id: true, notes: true } },
+ },
});
- if (legacyRecords.length === 0) return;
-
- // Copy legacy `notes` into the new `description` field before the migration
- // drops the `notes` column. If this fails, the upgrade is aborted and the
- // workspace stays on v1 with all data intact.
- await Promise.all(
- legacyRecords.map((record) =>
- client.postCard.update({
- where: { id: record.id },
- data: { description: record.notes },
- }),
- ),
- );
+ // Copy legacy `notes` into `description` before the migration drops the
+ // column. If this fails, the upgrade aborts and the workspace stays on v1.
+ for (const { node } of postCards.edges) {
+ await client.mutation({
+ updatePostCard: {
+ __args: { id: node.id, data: { description: node.notes } },
+ id: true,
+ },
+ });
+ }
};
export default definePreInstallLogicFunction({
@@ -186,21 +140,5 @@ export default definePreInstallLogicFunction({
});
```
-**Regra geral:**
-
-| Você quer... | Usar |
-| ------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- |
-| Popular dados padrão, configurar o workspace, registrar recursos externos | `post-install` |
-| Executar processos longos de popular dados ou chamadas a terceiros que não devem bloquear a resposta da instalação | `post-install` (padrão — `shouldRunSynchronously: false`, com novas tentativas do worker) |
-| Executar uma configuração rápida da qual o chamador dependerá imediatamente após o retorno da chamada de instalação | `post-install` com `shouldRunSynchronously: true` |
-| Ler ou fazer backup de dados que a próxima migração perderia | `pre-install` |
-| Rejeitar uma atualização que corromperia dados existentes | `pre-install` (lançar uma exceção no manipulador) |
-| Executar reconciliação em cada atualização | `post-install` com `shouldRunOnVersionUpgrade: true` |
-| Fazer uma configuração única apenas na primeira instalação | `post-install` com `shouldRunOnVersionUpgrade: false` (padrão) |
-
-
-Em caso de dúvida, use **post-install** como padrão. Recurra à pré-instalação somente quando a própria migração for destrutiva e você precisar interceptar o estado anterior antes que ele desapareça.
-
-
diff --git a/packages/twenty-docs/l/pt/developers/extend/apps/data/objects.mdx b/packages/twenty-docs/l/pt/developers/extend/apps/data/objects.mdx
index bbb3f557b8..036dce8824 100644
--- a/packages/twenty-docs/l/pt/developers/extend/apps/data/objects.mdx
+++ b/packages/twenty-docs/l/pt/developers/extend/apps/data/objects.mdx
@@ -86,6 +86,22 @@ export default defineObject({
**Os campos base são adicionados automaticamente.** Quando você define um objeto personalizado, o Twenty cria campos padrão como `id`, `name`, `createdAt`, `updatedAt`, `createdBy`, `updatedBy` e `deletedAt` para você. Você não precisa declará‑los no seu array `fields` — apenas seus campos personalizados. Você pode substituir um campo padrão declarando um com o mesmo nome, mas isso raramente é uma boa ideia.
+## Tipos de campo
+
+O conjunto completo de valores de `FieldType`, exportados de `twenty-sdk/define`:
+
+| Categoria | Tipos |
+| -------------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
+| Texto | `TEXT`, `RICH_TEXT`, `ARRAY` (de strings), `RAW_JSON` |
+| Numérico | `NUMBER` (`universalSettings.dataType`: `'float'` / `'int'` / `'bigint'`), `NUMERIC` (precisão arbitrária), `RATING`, `POSITION` |
+| Datas | `DATE`, `DATE_TIME` |
+| Escolha | `BOOLEAN`, `SELECT`, `MULTI_SELECT` |
+| Composto | `FULL_NAME`, `ADDRESS`, `EMAILS`, `PHONES`, `LINKS`, `CURRENCY`, `ACTOR`, `FILES` |
+| Identificadores e relações | `UUID`, `RELATION`, `MORPH_RELATION` (veja [Relações](/l/pt/developers/extend/apps/data/relations)) |
+| Sistema | `TS_VECTOR` (vetor de pesquisa de texto completo, gerenciado pelo servidor) |
+
+Tipos compostos armazenam vários subcampos (por exemplo, `FULL_NAME` = primeiro + último nome; `CURRENCY` = `amountMicros` + `currencyCode`). `SELECT` e `MULTI_SELECT` exigem um array `options`, como no exemplo acima.
+
## Valores padrão
Valores padrão de strings literais devem ser colocados entre aspas simples **dentro** da string — `defaultValue: "'Draft'"`, não `defaultValue: "Draft"`. É por isso que o campo `status` acima usa `` `'${PostCardStatus.DRAFT}'` ``.
diff --git a/packages/twenty-docs/l/pt/developers/extend/apps/getting-started/project-structure.mdx b/packages/twenty-docs/l/pt/developers/extend/apps/getting-started/project-structure.mdx
index bef909168d..c2a0f4b80e 100644
--- a/packages/twenty-docs/l/pt/developers/extend/apps/getting-started/project-structure.mdx
+++ b/packages/twenty-docs/l/pt/developers/extend/apps/getting-started/project-structure.mdx
@@ -14,26 +14,39 @@ my-twenty-app/
default-role.ts # Permissions for logic functions
constants/
universal-identifiers.ts # Auto-generated UUIDs and metadata
+ front-components/
+ main-page.tsx # Welcome page component
+ navigation-menu-items/
+ main-page.navigation-menu-item.ts # Sidebar entry for the welcome page
+ page-layouts/
+ main-page.page-layout.ts # Standalone page hosting the component
__tests__/
- setup-test.ts
- app-install.integration-test.ts
- .github/workflows/ci.yml # GitHub Actions
- public/ # Static assets
- vitest.config.ts # Test runner config
+ application-config.test.ts # Unit test
+ global-setup.ts # Integration test setup (sync + uninstall)
+ schema.integration-test.ts # Integration test against a live server
+ .github/workflows/
+ ci.yml # Lint, typecheck, unit + integration tests
+ cd.yml # Deploy + install on push to main
+ public/
+ logo.svg # Static assets
+ vitest.config.ts # Integration test runner config
+ vitest.unit.config.ts # Unit test runner config
tsconfig.json, tsconfig.spec.json
.nvmrc, .yarnrc.yml, .oxlintrc.json
- README.md, LLMS.md
+ README.md, AGENTS.md, CLAUDE.md
```
## Arquivos principais
-| Arquivo / Pasta | Finalidade |
-| ---------------------------------------- | ------------------------------------------------------------------------ |
-| `src/application-config.ts` | **Obrigatório.** O principal arquivo de configuração do seu aplicativo. |
-| `src/default-role.ts` | Papel padrão que controla o que suas funções de lógica podem acessar. |
-| `src/constants/universal-identifiers.ts` | UUIDs gerados automaticamente e metadados (nome de exibição, descrição). |
-| `src/__tests__/` | Testes de integração (configuração + teste de exemplo). |
-| `public/` | Recursos estáticos (imagens, fontes) servidos com seu aplicativo. |
+| Arquivo / Pasta | Finalidade |
+| -------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
+| `src/application-config.ts` | **Obrigatório.** O principal arquivo de configuração do seu aplicativo. |
+| `src/default-role.ts` | Papel padrão que controla o que suas funções de lógica podem acessar. |
+| `src/constants/universal-identifiers.ts` | UUIDs gerados automaticamente e metadados (nome de exibição, descrição). |
+| `src/front-components/`, `src/navigation-menu-items/`, `src/page-layouts/` | Uma página de boas-vindas inicial: um front component renderizado por um page layout autônomo, acessível a partir da barra lateral. |
+| `src/__tests__/` | Um teste de unidade mais um teste de integração (com sua configuração global) que sincroniza o app com um servidor real. |
+| `public/` | Recursos estáticos (imagens, fontes) servidos com seu aplicativo. |
+| `AGENTS.md` / `CLAUDE.md` | Orientação para agentes de codificação de IA que trabalham no app. |
**A organização de arquivos fica a seu critério.** As pastas acima são convenções — o SDK detecta entidades por meio de análise de AST em chamadas a `export default defineEntity(...)`, independentemente de onde o arquivo esteja.
@@ -47,15 +60,18 @@ Ambos os pacotes Twenty SDK pertencem a `devDependencies`, não a `dependencies`
{
"dependencies": {},
"devDependencies": {
- "twenty-client-sdk": "^2.13.0",
- "twenty-sdk": "^2.13.0"
+ "twenty-client-sdk": "2.20.0",
+ "twenty-sdk": "2.20.0",
+ "twenty-ui": "1.0.0-alpha.1"
}
}
```
+O scaffolder fixa `twenty-sdk` e `twenty-client-sdk` para a sua própria versão — mantenha os dois sincronizados ao atualizar.
+
* **`twenty-sdk`** inclui a CLI `twenty` e as ferramentas de build/scaffolding. Ele é executado apenas durante o desenvolvimento e o build e nunca é importado pelo runtime do aplicativo publicado.
* **`twenty-client-sdk`** *é* importado pelo código do seu aplicativo (`CoreApiClient`, `MetadataApiClient`, `RestApiClient`), mas a Twenty o fornece em tempo de execução — as funções de lógica o obtêm de uma camada SDK gerada, e os componentes de front o resolvem a partir de módulos servidos pelo servidor. A cópia instalada é usada apenas para verificação de tipos e para o build no momento do deploy, então ela nunca precisa ser incluída no bundle implantado.
-Manter qualquer um dos pacotes em `dependencies` o inclui no bundle de runtime do aplicativo instalado, onde ele é peso morto. `twenty build` emite um aviso quando qualquer um deles ainda está listado em `dependencies`.
+Manter qualquer um dos pacotes em `dependencies` o inclui no bundle de runtime do aplicativo instalado, onde ele é peso morto. `twenty dev:build` emite um aviso quando qualquer um deles ainda está listado em `dependencies`.
Adicione as dependências de runtime do próprio aplicativo (bibliotecas que as suas funções de lógica realmente importam em tempo de execução) em `dependencies`, como de costume.
diff --git a/packages/twenty-docs/l/pt/developers/extend/apps/getting-started/quick-start.mdx b/packages/twenty-docs/l/pt/developers/extend/apps/getting-started/quick-start.mdx
index 5a451c47a2..ac1aa5da37 100644
--- a/packages/twenty-docs/l/pt/developers/extend/apps/getting-started/quick-start.mdx
+++ b/packages/twenty-docs/l/pt/developers/extend/apps/getting-started/quick-start.mdx
@@ -6,17 +6,17 @@ description: Crie seu primeiro app do Twenty em minutos.
## Pré-requisitos
-* **Node.js 24+** — [Baixar](https://nodejs.org/)
+* **Node.js 24.5+** — [Baixar](https://nodejs.org/)
* **Yarn 4** — Vem com o Node.js via Corepack. Ative-o: `corepack enable`
* **Docker** — [Baixar](https://www.docker.com/products/docker-desktop/). Necessário para executar um servidor Twenty local. Ignore se você já tiver o Twenty em execução em outro lugar.
A criação de um aplicativo Twenty tem três fases. A ferramenta de scaffolding as reúne em um único comando do fluxo ideal, mas cada fase é um conceito separado — quando algo falha, saber em que fase você está indica o que corrigir.
-| Fase | O que você faz | Ferramenta | Resultado |
-| --------------------------- | -------------------------------------------------- | ----------------------------- | ------------------------------------- |
-| **1. Criar scaffolding** | Gerar o código-fonte do aplicativo | `npx create-twenty-app` | Um projeto TypeScript em disco |
-| **2. Executar um servidor** | Iniciar um servidor Twenty para o qual sincronizar | Docker + `yarn twenty server` | Uma instância Twenty em execução |
-| **3. Sincronizar** | Sincronize seu código em tempo real com o servidor | `yarn twenty dev` | Suas alterações aparecem na interface |
+| Fase | O que você faz | Ferramenta | Resultado |
+| --------------------------- | -------------------------------------------------- | ----------------------------------- | ------------------------------------- |
+| **1. Criar scaffolding** | Gerar o código-fonte do aplicativo | `npx create-twenty-app` | Um projeto TypeScript em disco |
+| **2. Executar um servidor** | Iniciar um servidor Twenty para o qual sincronizar | Docker + `yarn twenty docker:start` | Uma instância Twenty em execução |
+| **3. Sincronizar** | Sincronize seu código em tempo real com o servidor | `yarn twenty dev` | Suas alterações aparecem na interface |
---
@@ -28,7 +28,7 @@ Crie um novo aplicativo a partir do modelo:
npx create-twenty-app@latest my-twenty-app
```
-Você será solicitado a informar um nome e uma descrição — pressione **Enter** para aceitar os valores padrão. Isso gera um projeto TypeScript em `my-twenty-app/` com um `application-config.ts` inicial, um papel padrão, um fluxo de trabalho de CI e um teste de integração.
+O gerador não é interativo: o nome do diretório se torna o nome do app. Passe `--display-name` e `--description` para personalizar os metadados gerados (você também pode editá-los depois em `src/constants/universal-identifiers.ts`). Isso gera um projeto TypeScript em `my-twenty-app/` com um `application-config.ts` inicial, um papel padrão, fluxos de trabalho de CI/CD e um teste de integração.
**Após esta fase:** você tem o código-fonte de um aplicativo na sua máquina. Ele ainda não está em execução — isso é a Fase 2.
@@ -38,28 +38,14 @@ Você será solicitado a informar um nome e uma descrição — pressione **Ente
Seu aplicativo precisa de um servidor Twenty para o qual sincronizar. O servidor é uma instância completa do Twenty — interface, API GraphQL, PostgreSQL — executando localmente no Docker. Seu código local envia suas definições para esse servidor, o que faz com que elas apareçam na interface.
-A ferramenta de scaffolding oferece iniciar um para você:
+O scaffolder inicia uma instância para você: com o Docker em execução, ele baixa a imagem `twentycrm/twenty-app-dev`, inicia-a na porta `2020` e autentica a CLI no workspace de demonstração pré-preenchido (`tim@apple.dev`) — sem necessidade de login.
-> **Você gostaria de configurar uma instância local do Twenty?**
-
-* **Sim (recomendado)** — baixa a imagem Docker `twentycrm/twenty-app-dev` e a inicia na porta `2020`. Certifique-se de que o Docker esteja em execução primeiro.
-* **Não** — escolha isto se você já tiver um servidor Twenty ao qual deseja se conectar. Você pode conectá-lo depois com `yarn twenty remote:add`.
-
-
-

-
-
-Quando o servidor estiver ativo, um navegador será aberto para login. Use a conta de demonstração pré-configurada:
-
-* **E-mail:** `tim@apple.dev`
-* **Senha:** `tim@apple.dev`
+Para se conectar a um servidor Twenty existente em vez disso, passe `--url \`. Servidores remotos se autenticam com OAuth: um navegador é aberto para que você faça login e clique em **Authorize**, o que dá à CLI acesso ao seu workspace. (Você também pode optar por usar OAuth localmente com `--authentication-method oauth` — faça login com `tim@apple.dev` / `tim@apple.dev`.)
-Clique em **Authorize** na próxima tela — isso dá à CLI acesso ao seu espaço de trabalho.
-
@@ -117,27 +103,31 @@ Clique em **View installed app** para ver a instalação no espaço de trabalho.
### Sincronização única para CI e scripts
-Passe `--once` para executar uma única compilação + sincronização e sair — mesmo pipeline, sem watcher:
+Use `plan` e `apply` para executar o mesmo pipeline uma vez, sem watcher:
```bash filename="Terminal"
-yarn twenty dev --once
+yarn twenty plan # preview the metadata changes without applying them
+yarn twenty apply # show the plan, then apply it
```
-| Comando | Comportamento | Quando usar |
-| ---------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- |
-| `yarn twenty dev` | Monitora e ressincroniza a cada alteração. Fica em execução até você interrompê-lo. | Desenvolvimento local interativo. |
-| `yarn twenty dev --once` | Executa uma única compilação + sincronização e, em seguida, encerra com o código `0` em caso de sucesso ou `1` em caso de falha. | Scripts, CI, hooks de pre-commit, agentes de IA e fluxos de trabalho com script. |
-| `yarn twenty dev --once --dry-run` | Compila e imprime as alterações de metadados **sem aplicá-las**. | Inspecionar o que uma sincronização mudaria antes de confirmá-la. |
+| Comando | Comportamento | Quando usar |
+| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- |
+| `yarn twenty dev` | Monitora e ressincroniza a cada alteração. Fica em execução até você interrompê-lo. | Desenvolvimento local interativo. |
+| `yarn twenty apply` | Executa uma única compilação + sincronização e, em seguida, encerra com o código `0` em caso de sucesso ou `1` em caso de falha. Pede confirmação para alterações destrutivas (passe `--force` para pular). | Scripts, CI, hooks de pre-commit, agentes de IA e fluxos de trabalho com script. |
+| `yarn twenty plan` | Compila e imprime as alterações de metadados **sem aplicá-las**. | Inspecionar o que uma sincronização mudaria antes de confirmá-la. |
-Ambos os modos precisam de um remoto autenticado. Veja [Sincronização e recuperação](/l/pt/developers/extend/apps/operations/sync-and-recovery#previewing-changes-dry-run) para mais detalhes sobre `--dry-run`.
+Todos os modos precisam de um remoto autenticado. Veja [Sincronização e recuperação](/l/pt/developers/extend/apps/operations/sync-and-recovery#previewing-changes-plan) para mais detalhes sobre `plan`.
+
+
+`yarn twenty dev --once` e `yarn twenty dev --once --dry-run` são aliases obsoletos para `yarn twenty apply` e `yarn twenty plan`.
+
### Opções do modo de desenvolvimento
| Opção | Descrição |
| ------------------------------------- | ------------------------------------------------------------------------------------------------- |
-| `--once` | Compila e sincroniza uma vez e, em seguida, sai. |
-| `--dry-run` | Com `--once`, visualize as alterações de metadados sem aplicá‑las. Não grava nada. |
-| `--debounceMs \` | Define o atraso de debounce para alterações de arquivo em milissegundos (padrão: `2000`). |
+| `--force` | Aplicar alterações destrutivas (exclusões) sem confirmação. |
+| `--debounceMs \` | Define o atraso de debounce para alterações de arquivo em milissegundos (padrão: `1000`). |
| `--verbose` / `--debug` | Mostra registros detalhados de compilação, solicitações de sincronização e rastreamentos de erro. |
## O que você pode criar
diff --git a/packages/twenty-docs/l/pt/developers/extend/apps/getting-started/scaffolding.mdx b/packages/twenty-docs/l/pt/developers/extend/apps/getting-started/scaffolding.mdx
index 31116c7600..f71c71df00 100644
--- a/packages/twenty-docs/l/pt/developers/extend/apps/getting-started/scaffolding.mdx
+++ b/packages/twenty-docs/l/pt/developers/extend/apps/getting-started/scaffolding.mdx
@@ -34,6 +34,10 @@ yarn twenty dev:add frontComponent
| Vista | `yarn twenty dev:add view` | `src/views/\.ts` |
| Item do menu de navegação | `yarn twenty dev:add navigationMenuItem` | `src/navigation-menu-items/\.ts` |
| Layout da página | `yarn twenty dev:add pageLayout` | `src/page-layouts/\.ts` |
+| Aba Layout da Página | `yarn twenty dev:add pageLayoutTab` | `src/page-layout-tabs/\.ts` |
+| Item do menu de comandos | `yarn twenty dev:add commandMenuItem` | `src/command-menu-items/\.ts` |
+| Campo da Vista | `yarn twenty dev:add viewField` | `src/view-fields/\.ts` |
+| Provedor de conexão | `yarn twenty dev:add connectionProvider` | `src/connection-providers/\.ts` |
## O que o scaffolder gera
diff --git a/packages/twenty-docs/l/pt/developers/extend/apps/getting-started/troubleshooting.mdx b/packages/twenty-docs/l/pt/developers/extend/apps/getting-started/troubleshooting.mdx
index c0b1fbe3e2..fa862466a9 100644
--- a/packages/twenty-docs/l/pt/developers/extend/apps/getting-started/troubleshooting.mdx
+++ b/packages/twenty-docs/l/pt/developers/extend/apps/getting-started/troubleshooting.mdx
@@ -5,10 +5,10 @@ icon: wrench
---
* **Erros do Docker** — Certifique-se de que o Docker Desktop (ou o daemon) esteja em execução antes de `yarn twenty docker:start`. A mensagem de erro mostrará o comando de inicialização correto para o seu sistema operacional.
-* **Versão errada do Node** — É necessário 24 ou superior. Verifique com `node -v`.
+* **Versão errada do Node** — É necessário 24.5+ (`engines.node: ^24.5.0`). Verifique com `node -v`.
* **Falta o Yarn 4** — Execute `corepack enable`.
* **Dependências com problemas** — `rm -rf node_modules && yarn install`.
* **Erros do `twenty-sdk` após a atualização para a v2.8.0** — Ele foi movido de `dependencies` para `devDependencies` na v2.8.0. Veja [Estrutura do projeto → Dependências](/l/pt/developers/extend/apps/getting-started/project-structure#dependencies).
-* **`twenty build` emite um aviso sobre `twenty-client-sdk` em `dependencies`** — Ele é fornecido em tempo de execução pela Twenty, então deve ser movido para `devDependencies` junto com `twenty-sdk`. Veja [Estrutura do projeto → Dependências](/l/pt/developers/extend/apps/getting-started/project-structure#dependencies).
+* **`twenty dev:build` emite um aviso sobre `twenty-client-sdk` em `dependencies`** — Ele é fornecido em tempo de execução pela Twenty, então deve ser movido para `devDependencies` junto com `twenty-sdk`. Veja [Estrutura do projeto → Dependências](/l/pt/developers/extend/apps/getting-started/project-structure#dependencies).
Travou? Peça ajuda no [Discord da Twenty](https://discord.com/channels/1130383047699738754/1130386664812982322).
diff --git a/packages/twenty-docs/l/pt/developers/extend/apps/layout/command-menu-items.mdx b/packages/twenty-docs/l/pt/developers/extend/apps/layout/command-menu-items.mdx
index 41b36d8418..95769a0ea1 100644
--- a/packages/twenty-docs/l/pt/developers/extend/apps/layout/command-menu-items.mdx
+++ b/packages/twenty-docs/l/pt/developers/extend/apps/layout/command-menu-items.mdx
@@ -13,7 +13,6 @@ export default defineCommandMenuItem({
universalIdentifier: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
label: 'Open Dashboard',
shortLabel: 'Dashboard',
- icon: 'IconLayoutDashboard',
isPinned: true,
availabilityType: 'GLOBAL',
frontComponentUniversalIdentifier: '74c526eb-cb68-4cf7-b05c-0dd8c288d948',
@@ -22,51 +21,23 @@ export default defineCommandMenuItem({
## Campos de configuração
-| Campo | Obrigatório | Descrição |
-| --------------------------------------- | ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| `universalIdentifier` | Sim | ID exclusivo e estável para o comando |
-| `label` | Sim | Rótulo completo exibido no menu de comandos (Cmd+K) |
-| `frontComponentUniversalIdentifier` | Sim | O `universalIdentifier` do componente de front-end que este comando abre |
-| `shortLabel` | Não | Rótulo mais curto exibido no botão fixado de ação rápida |
-| `icon` | Não | Nome do ícone exibido ao lado do rótulo (por exemplo, `'IconBolt'`, `'IconSend'`) |
-| `isPinned` | Não | Quando `true`, mostra o comando como um botão de ação rápida no canto superior direito da página |
-| `availabilityType` | Não | Controla onde o comando aparece: `'GLOBAL'` (sempre disponível), `'RECORD_SELECTION'` (apenas quando registros estão selecionados) ou `'FALLBACK'` (exibido quando nenhum outro comando corresponde) |
-| `availabilityObjectUniversalIdentifier` | Não | Restringe o comando a páginas de um tipo específico de objeto (por exemplo, somente em registros de Company) |
-| `conditionalAvailabilityExpression` | Não | Uma expressão booleana que controla dinamicamente a visibilidade (veja abaixo) |
+| Campo | Obrigatório | Descrição |
+| --------------------------------------- | ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `universalIdentifier` | Sim | ID exclusivo e estável para o comando |
+| `label` | Sim | Rótulo completo exibido no menu de comandos (Cmd+K) |
+| `frontComponentUniversalIdentifier` | Sim | O `universalIdentifier` do componente de front-end que este comando abre |
+| `shortLabel` | Não | Rótulo mais curto exibido no botão fixado de ação rápida |
+| `icon` | Não | **Obsoleto** — ignorado em favor do ícone da aplicação; a compilação emite um aviso se definido |
+| `isPinned` | Não | Quando `true`, mostra o comando como um botão de ação rápida no canto superior direito da página |
+| `availabilityType` | Não | Controla onde o comando aparece: `'GLOBAL'` (sempre disponível), `'GLOBAL_OBJECT_CONTEXT'` (apenas em páginas com um contexto de objeto — páginas de índice e de registro), `'RECORD_SELECTION'` (apenas quando registros estão selecionados) ou `'FALLBACK'` (exibido quando nenhum outro comando corresponde) |
+| `availabilityObjectUniversalIdentifier` | Não | Restringe o comando a páginas de um tipo específico de objeto (por exemplo, somente em registros de Company) |
+| `conditionalAvailabilityExpression` | Não | Uma expressão booleana que controla dinamicamente a visibilidade (veja abaixo) |
## Comandos sem interface
Um item do menu de comandos emparelhado com um [componente de front-end sem interface](/l/pt/developers/extend/apps/layout/front-components#headless-vs-non-headless) é a forma idiomática de disponibilizar uma ação de um clique — executar código, navegar ou confirmar e executar. A página de Front Components aborda os [SDK Command components](/l/pt/developers/extend/apps/layout/front-components#sdk-command-components) (`Command`, `CommandLink`, `CommandModal`, `CommandOpenSidePanelPage`) que lidam com o padrão de ação e desmontagem.
-Um fluxo típico:
-
-```tsx src/front-components/run-action.tsx
-import { defineFrontComponent } from 'twenty-sdk/define';
-import { Command } from 'twenty-sdk/command';
-import { CoreApiClient } from 'twenty-sdk/clients';
-
-const RunAction = () => {
- const execute = async () => {
- const client = new CoreApiClient();
- await client.mutation({
- createTask: {
- __args: { data: { title: 'Created by my app' } },
- id: true,
- },
- });
- };
-
- return ;
-};
-
-export default defineFrontComponent({
- universalIdentifier: 'e5f6a7b8-c9d0-1234-efab-345678901234',
- name: 'run-action',
- description: 'Creates a task from the command menu',
- component: RunAction,
- isHeadless: true,
-});
-```
+Um fluxo típico: um componente headless renderiza `` (veja o [exemplo completo](/l/pt/developers/extend/apps/layout/front-components#sdk-command-components)), e o item de menu de comando aponta para ele:
```ts src/command-menu-items/run-action.command-menu-item.ts
import { defineCommandMenuItem } from 'twenty-sdk/define';
@@ -74,7 +45,6 @@ import { defineCommandMenuItem } from 'twenty-sdk/define';
export default defineCommandMenuItem({
universalIdentifier: 'f6a7b8c9-d0e1-2345-fabc-456789012345',
label: 'Run my action',
- icon: 'IconPlayerPlay',
frontComponentUniversalIdentifier: 'e5f6a7b8-c9d0-1234-efab-345678901234',
});
```
diff --git a/packages/twenty-docs/l/pt/developers/extend/apps/layout/front-components.mdx b/packages/twenty-docs/l/pt/developers/extend/apps/layout/front-components.mdx
index 5fe0d22e89..5871e7d3d2 100644
--- a/packages/twenty-docs/l/pt/developers/extend/apps/layout/front-components.mdx
+++ b/packages/twenty-docs/l/pt/developers/extend/apps/layout/front-components.mdx
@@ -49,14 +49,13 @@ export default defineCommandMenuItem({
universalIdentifier: 'd4e5f6a7-b8c9-0123-defa-456789012345',
shortLabel: 'Hello',
label: 'Hello World',
- icon: 'IconBolt',
isPinned: true,
availabilityType: 'GLOBAL',
frontComponentUniversalIdentifier: '74c526eb-cb68-4cf7-b05c-0dd8c288d948',
});
```
-Após sincronizar com `yarn twenty dev` (ou executando uma única vez o `yarn twenty dev --once`), a ação rápida aparece no canto superior direito da página:
+Após sincronizar com `yarn twenty dev` (ou executando uma única vez o `yarn twenty apply`), a ação rápida aparece no canto superior direito da página:

@@ -88,11 +87,11 @@ Os componentes de front-end têm dois modos de renderização controlados pela o
```tsx src/front-components/sync-tracker.tsx
import { defineFrontComponent } from 'twenty-sdk/define';
-import { useRecordId, enqueueSnackbar } from 'twenty-sdk/front-component';
+import { useSelectedRecordIds, enqueueSnackbar } from 'twenty-sdk/front-component';
import { useEffect } from 'react';
const SyncTracker = () => {
- const recordId = useRecordId();
+ const [recordId] = useSelectedRecordIds();
useEffect(() => {
enqueueSnackbar({ message: `Tracking record ${recordId}`, variant: 'info' });
@@ -116,7 +115,7 @@ Como o componente retorna `null`, o Twenty ignora renderizar um contêiner para
O pacote `twenty-sdk` fornece quatro componentes auxiliares Command projetados para componentes de front-end headless. Cada componente executa uma ação ao montar, trata erros exibindo uma notificação de snackbar e desmonta automaticamente o componente de front-end ao concluir.
-Importe-os de `twenty-sdk/command`:
+Importe-os de `twenty-sdk/front-component`:
* **`Command`** — Executa um callback assíncrono via a prop `execute`.
* **`CommandLink`** — Navega para um caminho do app. Props: `to`, `params`, `queryParams`, `options`.
@@ -127,8 +126,8 @@ Aqui está um exemplo completo de um componente de front-end headless usando `Co
```tsx src/front-components/run-action.tsx
import { defineFrontComponent } from 'twenty-sdk/define';
-import { Command } from 'twenty-sdk/command';
-import { CoreApiClient } from 'twenty-sdk/clients';
+import { Command } from 'twenty-sdk/front-component';
+import { CoreApiClient } from 'twenty-client-sdk/core';
const RunAction = () => {
const execute = async () => {
@@ -160,7 +159,6 @@ import { defineCommandMenuItem } from 'twenty-sdk/define';
export default defineCommandMenuItem({
universalIdentifier: 'f6a7b8c9-d0e1-2345-fabc-456789012345',
label: 'Run my action',
- icon: 'IconPlayerPlay',
frontComponentUniversalIdentifier: 'e5f6a7b8-c9d0-1234-efab-345678901234',
});
```
@@ -169,7 +167,7 @@ E um exemplo usando `CommandModal` para solicitar confirmação antes de executa
```tsx src/front-components/delete-draft.tsx
import { defineFrontComponent } from 'twenty-sdk/define';
-import { CommandModal } from 'twenty-sdk/command';
+import { CommandModal } from 'twenty-sdk/front-component';
const DeleteDraft = () => {
const execute = async () => {
@@ -202,7 +200,7 @@ Os componentes de front são executados no navegador em um Web Worker isolado, e
Uma função lógica declarada com `httpRouteTriggerSettings` é acessível por HTTP em seu caminho de rota. Twenty injeta no worker a URL base a partir da qual suas funções são servidas como `TWENTY_FUNCTIONS_URL`, juntamente com o `TWENTY_APP_ACCESS_TOKEN` que autentica a chamada. Ainda não há um cliente SDK dedicado para invocar suas próprias funções, portanto chame-as com um simples `fetch`:
-> **No Twenty Cloud, funções lógicas acionadas por HTTP são servidas em um domínio dedicado por workspace** em `https://\
.twenty.com\` — que é exatamente para onde `TWENTY_FUNCTIONS_URL` aponta. Para chamadores externos, copie a URL exata das configurações de **HTTP trigger** da função ou da guia **Settings** do aplicativo.
+> **No Twenty Cloud, funções lógicas acionadas por HTTP são servidas em um domínio dedicado por workspace** em `https://\.withtwenty.com\` — que é exatamente para onde `TWENTY_FUNCTIONS_URL` aponta. Para chamadores externos, copie a URL exata das configurações de **HTTP trigger** da função ou da guia **Settings** do aplicativo.
A rota legada da função `/s/` está **obsoleta** e será **desativada em 2026-07-24**. Use `TWENTY_FUNCTIONS_URL` (acima) em vez disso e migre quaisquer URLs de `/s/` fixas no código antes dessa data. A rota `/s/` continua disponível para auto-hospedagem.
@@ -212,7 +210,7 @@ Um componente de front headless pode executar a chamada ao montar via o componen
```tsx src/front-components/sync-prs.tsx
import { defineFrontComponent } from 'twenty-sdk/define';
-import { Command } from 'twenty-sdk/command';
+import { Command } from 'twenty-sdk/front-component';
const SyncPrs = () => {
const execute = async () => {
@@ -316,13 +314,13 @@ Dentro do seu componente, use hooks do SDK para acessar o usuário atual, o regi
import { defineFrontComponent } from 'twenty-sdk/define';
import {
useUserId,
- useRecordId,
+ useSelectedRecordIds,
useFrontComponentId,
} from 'twenty-sdk/front-component';
const RecordInfo = () => {
const userId = useUserId();
- const recordId = useRecordId();
+ const [recordId] = useSelectedRecordIds();
const componentId = useFrontComponentId();
return (
@@ -405,12 +403,11 @@ Aqui está um exemplo que usa a API do host para exibir um snackbar e fechar o p
```tsx src/front-components/archive-record.tsx
import { defineFrontComponent } from 'twenty-sdk/define';
-import { useRecordId } from 'twenty-sdk/front-component';
-import { enqueueSnackbar, closeSidePanel } from 'twenty-sdk/front-component';
-import { CoreApiClient } from 'twenty-sdk/clients';
+import { enqueueSnackbar, closeSidePanel, useSelectedRecordIds } from 'twenty-sdk/front-component';
+import { CoreApiClient } from 'twenty-client-sdk/core';
const ArchiveRecord = () => {
- const recordId = useRecordId();
+ const [recordId] = useSelectedRecordIds();
const handleArchive = async () => {
const client = new CoreApiClient();
@@ -451,10 +448,10 @@ export default defineFrontComponent({
Use `useSelectedRecordIds()` para lidar com vários registros selecionados. Isso é útil para operações em lote:
```tsx src/front-components/bulk-export.tsx
-import { defineFrontComponent, numberOfSelectedRecords } from 'twenty-sdk/define';
+import { defineFrontComponent } from 'twenty-sdk/define';
import { useSelectedRecordIds } from 'twenty-sdk/front-component';
import { enqueueSnackbar, closeSidePanel } from 'twenty-sdk/front-component';
-import { CoreApiClient } from 'twenty-sdk/clients';
+import { CoreApiClient } from 'twenty-client-sdk/core';
const BulkExport = () => {
const selectedRecordIds = useSelectedRecordIds();
@@ -492,12 +489,19 @@ export default defineFrontComponent({
name: 'bulk-export',
description: 'Export selected records',
component: BulkExport,
- command: {
- universalIdentifier: 'd0e1f2a3-b4c5-6789-defa-012345678902',
- label: 'Bulk Export',
- availabilityType: 'RECORD_SELECTION',
- conditionalAvailabilityExpression: numberOfSelectedRecords > 0,
- },
+});
+```
+
+Exiba-o com um [item de menu de comando](/l/pt/developers/extend/apps/layout/command-menu-items) restrito a seleções de registros:
+
+```ts src/command-menu-items/bulk-export.command-menu-item.ts
+import { defineCommandMenuItem } from 'twenty-sdk/define';
+
+export default defineCommandMenuItem({
+ universalIdentifier: 'd0e1f2a3-b4c5-6789-defa-012345678902',
+ label: 'Bulk Export',
+ availabilityType: 'RECORD_SELECTION',
+ frontComponentUniversalIdentifier: 'd0e1f2a3-b4c5-6789-defa-012345678901',
});
```
diff --git a/packages/twenty-docs/l/pt/developers/extend/apps/layout/navigation-menu-items.mdx b/packages/twenty-docs/l/pt/developers/extend/apps/layout/navigation-menu-items.mdx
index c2d40f771d..6a5c77cd68 100644
--- a/packages/twenty-docs/l/pt/developers/extend/apps/layout/navigation-menu-items.mdx
+++ b/packages/twenty-docs/l/pt/developers/extend/apps/layout/navigation-menu-items.mdx
@@ -35,6 +35,8 @@ export default defineNavigationMenuItem({
* `position` controla a ordenação na barra lateral.
+* O enum também contém `NavigationMenuItemType.RECORD`, usado internamente para favoritos de registros criados pelo usuário — não pode ser usado a partir de um manifesto de app (não há nenhum campo para fazer referência a um registro).
+
* `icon` e `color` são opcionais e personalizam a aparência da entrada.
* `folderUniversalIdentifier` também está disponível em qualquer item para aninhá-lo dentro de um pai do tipo `FOLDER`.
diff --git a/packages/twenty-docs/l/pt/developers/extend/apps/layout/views.mdx b/packages/twenty-docs/l/pt/developers/extend/apps/layout/views.mdx
index 4377dcef56..4e398719da 100644
--- a/packages/twenty-docs/l/pt/developers/extend/apps/layout/views.mdx
+++ b/packages/twenty-docs/l/pt/developers/extend/apps/layout/views.mdx
@@ -33,17 +33,32 @@ export default defineView({
## Pontos-chave
* `objectUniversalIdentifier` especifica a qual objeto esta visualização se aplica. Pode ser um objeto personalizado que você definiu ou um objeto padrão do Twenty.
-* `key` determina o tipo de visualização — `ViewKey.INDEX` é a visualização de lista principal do objeto.
+* `key: ViewKey.INDEX` marca a visualização como a visualização principal de lista do objeto (aquela que um item de navegação `OBJECT` abre).
* `fields` controla quais colunas aparecem e em que ordem. Cada campo referencia um `fieldMetadataUniversalIdentifier`.
-* Você também pode declarar `filters`, `filterGroups`, `groups` e `fieldGroups` para configurações avançadas.
+* Você também pode declarar `filters`, `filterGroups`, `sorts`, `groups` e `fieldGroups` para configurações avançadas.
* `position` controla a ordenação quando existem várias visualizações para o mesmo objeto.
+## Propriedades opcionais
+
+| Propriedade | Valores | Descrição |
+| ----------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
+| `type` | `ViewType.TABLE` (padrão), `ViewType.KANBAN`, `ViewType.CALENDAR` | Como os registros são dispostos. (`FIELDS_WIDGET` / `TABLE_WIDGET` também existem, mas são usados internamente por widgets de layout de página.) |
+| `visibility` | `ViewVisibility.WORKSPACE` (padrão), `ViewVisibility.UNLISTED` | Se a visualização é listada para todo o workspace ou ocultada dos seletores. |
+| `openRecordIn` | `ViewOpenRecordIn.SIDE_PANEL` (padrão), `ViewOpenRecordIn.RECORD_PAGE` | Onde clicar em um registro o abre. |
+| `ordenações` | `{ fieldMetadataUniversalIdentifier, direction: ViewSortDirection.ASC \| DESC }[]` | Ordem de classificação padrão. |
+| `isCompact` | `boolean` | Exibição compacta de linhas. |
+| `mainGroupByFieldMetadataUniversalIdentifier` + `shouldHideEmptyGroups` | — | Agrupar registros (por exemplo, colunas kanban) por um campo. |
+| `kanbanAggregateOperation`, `kanbanAggregateOperationFieldMetadataUniversalIdentifier`, `kanbanColumnWidth` | `AggregateOperations.*` | Agregações e dimensionamento de colunas kanban. |
+| `calendarLayout`, `calendarFieldMetadataUniversalIdentifier` | `ViewCalendarLayout.DAY` / `WEEK` / `MONTH` | Visualizações de calendário: layout e o campo de data que posiciona os registros. |
+
+Todos os enums acima são exportados de `twenty-sdk/define`.
+
## Filtros
Uma visualização pode vir com filtros pré-aplicados. Cada filtro tem três coordenadas: o **campo** a ser filtrado, o **operador** (como comparar) e o **valor** (com o que comparar). As três precisam estar alinhadas — usar um operador que não se aplica a um tipo de campo será rejeitado no momento da sincronização.
```ts
-import { ViewFilterOperand } from 'twenty-shared/types';
+import { ViewFilterOperand } from 'twenty-sdk/define';
filters: [
{
diff --git a/packages/twenty-docs/l/pt/developers/extend/apps/logic/logic-functions.mdx b/packages/twenty-docs/l/pt/developers/extend/apps/logic/logic-functions.mdx
index c8b0ab5835..5dc1bed301 100644
--- a/packages/twenty-docs/l/pt/developers/extend/apps/logic/logic-functions.mdx
+++ b/packages/twenty-docs/l/pt/developers/extend/apps/logic/logic-functions.mdx
@@ -51,8 +51,12 @@ export default defineLogicFunction({
```
Tipos de gatilho disponíveis:
-* **httpRoute**: Expõe sua função em um caminho e método HTTP **no endpoint `/s/`**:
-> por exemplo, `path: '/post-card/create'` é acessível em `https://your-twenty-server.com/s/post-card/create`
+* **httpRoute**: expõe sua função em um caminho HTTP e método na **URL base do seu espaço de trabalho** — o valor de 20 injeções como `TWENTY_FUNCTIONS_URL` (em Vinte nuvens, um domínio dedicado por espaço de trabalho):
+> por exemplo, `path: '/post-card/create'` é acessível em `https://your-workspace.withtwenty.com/post-card/create`
+
+
+A rota de prefixo `/s/` do legado (`https://your-twenty-server.com/s/post-card/create`) está **obsoleta em 20 Cloud** e será desativada em **2026-07-24**. Persiste disponível para instâncias auto-hospedadas e locais que não configuram um domínio de funções isoladas — use `TWENTY_FUNCTIONS_URL` quando estiver definido, e cair de volta para `\/s/\` caso contrário.
+
Para invocar uma função de lógica acionada por rota a partir de um componente de front-end (headless), consulte [Chamando uma função de lógica](/l/pt/developers/extend/apps/layout/front-components#calling-a-logic-function).
diff --git a/packages/twenty-docs/l/pt/developers/extend/apps/logic/overview.mdx b/packages/twenty-docs/l/pt/developers/extend/apps/logic/overview.mdx
index cacdedbf33..94842a0abb 100644
--- a/packages/twenty-docs/l/pt/developers/extend/apps/logic/overview.mdx
+++ b/packages/twenty-docs/l/pt/developers/extend/apps/logic/overview.mdx
@@ -40,13 +40,13 @@ A **camada de lógica** de um app do Twenty é o código que *é executado* —
Uma função de lógica escolhe um ou mais gatilhos — cada entrada abaixo é um campo separado em `defineLogicFunction()`:
-| Disparador | Quando é executado | Configuração |
-| ----------------------------- | ----------------------------------------------------------------- | ------------------------------- |
-| **Rota HTTP** | Uma solicitação atinge seu endpoint `/s/\` | `httpRouteTriggerSettings` |
-| **Cron** | Uma expressão CRON corresponde | `cronTriggerSettings` |
-| **Evento de banco de dados** | Um registro do workspace é criado, atualizado ou excluído | `databaseEventTriggerSettings` |
-| **Ferramenta de IA** | Um recurso de IA do Twenty decide chamar sua função | `toolTriggerSettings` |
-| **Ação de fluxo de trabalho** | Uma etapa de fluxo de trabalho invoca sua função | `workflowActionTriggerSettings` |
+| Disparador | Quando é executado | Configuração |
+| ----------------------------- | --------------------------------------------------------- | ------------------------------- |
+| **Rota HTTP** | Uma solicitação atinge a URL pública da sua função | `httpRouteTriggerSettings` |
+| **Cron** | Uma expressão CRON corresponde | `cronTriggerSettings` |
+| **Evento de banco de dados** | Um registro do workspace é criado, atualizado ou excluído | `databaseEventTriggerSettings` |
+| **Ferramenta de IA** | Um recurso de IA do Twenty decide chamar sua função | `toolTriggerSettings` |
+| **Ação de fluxo de trabalho** | Uma etapa de fluxo de trabalho invoca sua função | `workflowActionTriggerSettings` |
As funções são executadas em sandbox, em processos Node.js isolados, e acessam o workspace por meio de um cliente de API tipado, com escopo definido pelo papel declarado em [`defineApplication()`](/l/pt/developers/extend/apps/config/application).
diff --git a/packages/twenty-docs/l/pt/developers/extend/apps/operations/cli.mdx b/packages/twenty-docs/l/pt/developers/extend/apps/operations/cli.mdx
index d923768fa9..f42a69d28c 100644
--- a/packages/twenty-docs/l/pt/developers/extend/apps/operations/cli.mdx
+++ b/packages/twenty-docs/l/pt/developers/extend/apps/operations/cli.mdx
@@ -4,7 +4,25 @@ description: comandos `yarn twenty` para executar funções, transmitir logs, ge
icon: terminal
---
-Além de `dev`, `dev:build`, `dev:add` e `dev:typecheck`, a CLI `yarn twenty` fornece comandos para executar funções, visualizar logs e gerenciar instalações de aplicativos.
+A CLI `yarn twenty` é sua interface para tudo relacionado a apps. Lista completa de comandos:
+
+| Comando | O que faz | Documentado em |
+| ----------------------------------------------- | ---------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- |
+| `dev` | Monitora arquivos-fonte e sincroniza alterações em tempo real | [Início rápido](/l/pt/developers/extend/apps/getting-started/quick-start) |
+| `plan` | Visualize as alterações de metadados sem aplicá-las | [Sincronização e recuperação](/l/pt/developers/extend/apps/operations/sync-and-recovery#previewing-changes-plan) |
+| `apply` | Aplicar alterações de metadados após exibir o plano | [Sincronização e recuperação](/l/pt/developers/extend/apps/operations/sync-and-recovery) |
+| `dev:build` | Compile o app e gere o cliente de API (`--tarball` para empacotar um `.tgz`) | [Publicação](/l/pt/developers/extend/apps/operations/publishing) |
+| `dev:typecheck` | Executar verificação de tipos TypeScript | [Testes](/l/pt/developers/extend/apps/operations/testing) |
+| `dev:add` | Criar o esqueleto de uma nova entidade | [Scaffolding](/l/pt/developers/extend/apps/getting-started/scaffolding) |
+| `dev:generate-client` | Regenerar o cliente de API tipado | esta página |
+| `dev:function:exec` / `dev:function:logs` | Executar funções e transmitir seus logs | esta página |
+| `dev:translations-extract` | Extrair strings traduzíveis para catálogos em `locales/` | [Traduções](/l/pt/developers/extend/apps/translations/overview) |
+| `dev:catalog-sync` | Acionar a sincronização do catálogo do marketplace | [Publicação](/l/pt/developers/extend/apps/operations/publishing#how-marketplace-discovery-works) |
+| `app:publish` / `app:install` / `app:uninstall` | Ciclo de vida de lançamento | [Publicação](/l/pt/developers/extend/apps/operations/publishing) e esta página |
+| `docker:*` | Gerenciar o contêiner do servidor local Twenty | [Servidor local](/l/pt/developers/extend/apps/getting-started/local-server) |
+| `remote:*` | Gerenciar conexões de servidor | esta página |
+
+Todo comando aceita `-r, --remote \` para direcionar a um remoto específico em vez do padrão.
## Executando funções (`yarn twenty dev:function:exec`)
@@ -20,8 +38,9 @@ yarn twenty dev:function:exec -u e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf
# Pass a JSON payload
yarn twenty dev:function:exec -n create-new-post-card -p '{"name": "Hello"}'
-# Execute the post-install function
+# Execute the install hooks
yarn twenty dev:function:exec --postInstall
+yarn twenty dev:function:exec --preInstall
```
## Visualizando logs de funções (`yarn twenty dev:function:logs`)
@@ -100,6 +119,12 @@ yarn twenty remote:list
# Set the active remote
yarn twenty remote:use
+
+# Check that the active remote's authentication is still valid
+yarn twenty remote:status
+
+# Remove a remote
+yarn twenty remote:remove
```
Suas credenciais são armazenadas em `~/.twenty/config.json`.
diff --git a/packages/twenty-docs/l/pt/developers/extend/apps/operations/publishing.mdx b/packages/twenty-docs/l/pt/developers/extend/apps/operations/publishing.mdx
index a3e24a0251..0c037ff0ae 100644
--- a/packages/twenty-docs/l/pt/developers/extend/apps/operations/publishing.mdx
+++ b/packages/twenty-docs/l/pt/developers/extend/apps/operations/publishing.mdx
@@ -229,7 +229,7 @@ yarn twenty dev:catalog-sync
# yarn twenty dev:catalog-sync --remote production
```
-Os metadados exibidos no marketplace vêm da sua configuração `defineApplication()` — campos como `displayName`, `description`, `author`, `category`, `logoUrl`, `screenshots`, `aboutDescription`, `websiteUrl` e `termsUrl`.
+Os metadados exibidos no marketplace vêm da sua configuração de `defineApplication()` — consulte [Metadados do marketplace](#marketplace-metadata) acima.
Se o seu aplicativo não definir um `aboutDescription` em `defineApplication()`, o marketplace usará automaticamente o `README.md` do seu pacote no npm como conteúdo da página Sobre. Isso significa que você pode manter um único README tanto para o npm quanto para o marketplace da Twenty. Se quiser uma descrição diferente no marketplace, defina explicitamente `aboutDescription`.
diff --git a/packages/twenty-docs/l/pt/developers/extend/apps/operations/sync-and-recovery.mdx b/packages/twenty-docs/l/pt/developers/extend/apps/operations/sync-and-recovery.mdx
index 9edad4303e..00e392ec0b 100644
--- a/packages/twenty-docs/l/pt/developers/extend/apps/operations/sync-and-recovery.mdx
+++ b/packages/twenty-docs/l/pt/developers/extend/apps/operations/sync-and-recovery.mdx
@@ -15,33 +15,44 @@ Para a iteração local do dia a dia, quase sempre você vai querer `yarn twenty
| Você quer… | Comando | Notas |
| ------------------------------------------------- | ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
| Iterar localmente com sincronização em tempo real | `yarn twenty dev` | Monitora seus arquivos e sincroniza a cada alteração. |
-| Sincronizar uma vez e sair (CI, scripts, hooks) | `yarn twenty dev --once` | Um build + sincronização, depois encerra. |
-| Prever mudanças **sem aplicá-las** | `yarn twenty dev --once --dry-run` | Calcula e imprime o diff; não grava nada. |
+| Sincronizar uma vez e sair (CI, scripts, hooks) | `yarn twenty apply` | Um build + sincronização, depois encerra. Adicione `--force` para pular a confirmação de mudança destrutiva. |
+| Prever mudanças **sem aplicá-las** | `yarn twenty plan` | Calcula e imprime o diff; não grava nada. |
| Remover o app do workspace | `yarn twenty app:uninstall` | Adicione `--yes` para pular o prompt. |
| Enviar um tarball para um servidor | `yarn twenty app:publish --private` | Requer uma versão **estritamente maior** em `package.json` — veja [Publicação](/l/pt/developers/extend/apps/operations/publishing). |
| Publicar no marketplace (npm) | `yarn twenty app:publish` | — |
| Instalar / atualizar uma versão implantada | `yarn twenty app:install` | Instala a versão atualmente implantada. |
| Limpar o servidor local e começar do zero | `yarn twenty docker:reset` | Exclui **todos** os dados locais — último recurso. |
+
+`yarn twenty dev --once` e `yarn twenty dev --once --dry-run` ainda funcionam como aliases obsoletos para `yarn twenty apply` e `yarn twenty plan`.
+
+
### A sincronização local não precisa de incremento de versão
A regra de `version` estritamente crescente (`VERSION_ALREADY_EXISTS` no deploy, `APP_ALREADY_INSTALLED` / `CANNOT_DOWNGRADE_APPLICATION` na instalação) se aplica a **`app:publish` / `app:install`** — o caminho de release. `yarn twenty dev` sincroniza seu manifesto no lugar e nunca exige mudança de versão, então você não precisa mexer em `package.json` para iterar. Se você se pegar aumentando a versão para testar uma mudança local, está usando o caminho de release quando o que quer é o ciclo de desenvolvimento.
## Lendo a saída da sincronização
-Cada sincronização imprime as mudanças de metadados que aplicou (ou aplicaria, com `--dry-run`):
+Cada sincronização imprime as alterações de metadados que aplicou (ou aplicaria, com `plan`), no estilo do Terraform — um bloco por entidade com seus atributos, depois uma linha de resumo:
```text filename="Terminal"
-Metadata changes: 2 created, 1 updated, 1 deleted
- created objectMetadata rocket
- created fieldMetadata timelineActivities
- updated fieldMetadata launchedAt
- deleted pageLayout legacyTab
-✓ Synced
+ # objectMetadata "rocket" will be created
+ + icon = "IconRocket"
+ + labelSingular = "Rocket"
+ + ...
+
+ # fieldMetadata "launchedAt" will be updated
+ ~ isNullable = false -> true
+
+Plan: 2 to add, 1 to change, 1 to destroy.
+
+✓ Synced My App (4 files)
```
Este é seu primeiro diagnóstico: ele mostra exatamente quais objetos, campos e layouts mudaram, para que você possa confirmar que uma sincronização fez o que esperava antes de conferir na interface.
+Mudanças destrutivas (`to destroy`) são listadas com o que elas removem (por exemplo, `objectMetadata "auditNote" — drops the table and all its rows`) e exigem confirmação interativa, ou `--force` em scripts.
+
Quando uma sincronização falha em uma única entidade, o erro nomeia a entidade com problema e seu `universalIdentifier`, por exemplo:
```text
@@ -50,39 +61,42 @@ Migration action 'create' for 'fieldMetadata' (universalIdentifier: 2020...4337)
Use esse identificador para encontrar a entidade no seu manifesto (e, se necessário, no workspace), em vez de adivinhar qual está em conflito.
-## Visualizando mudanças (dry run)
+## Visualizando mudanças (plan)
-`yarn twenty dev --once --dry-run` compila seu manifesto, pede ao servidor o plano de migração e o imprime — **sem aplicar nada**. É a forma segura de responder "o que esta sincronização mudaria?" antes de se comprometer com ela.
+`yarn twenty plan` compila seu manifesto, pede ao servidor o plano de migração e o imprime — **sem aplicar nada**. É a forma segura de responder "o que esta sincronização mudaria?" antes de se comprometer com ela.
```bash filename="Terminal"
-yarn twenty dev --once --dry-run
+yarn twenty plan
```
```text filename="Terminal"
Building manifest...
-Computing metadata diff (dry run, nothing will be applied)...
-Metadata changes: 1 created, 1 updated
- created fieldMetadata timelineActivities
- updated objectMetadata rocket
-✓ Dry run complete for My App — no changes were applied
+Computing metadata plan (read-only, nothing will be applied)...
+
+ # fieldMetadata "timelineActivities" will be created
+ + ...
+
+Plan: 1 to add, 1 to change, 0 to destroy.
+
+✓ Plan complete for My App — no changes were applied
```
-Um dry run:
+Um plano:
* **Não grava nada** — nenhuma migração de metadados, nenhuma atualização de registro de aplicativo, nenhuma mudança de função/aba padrão e nenhuma geração de cliente de API.
* Retorna o **mesmo diff** que uma sincronização real aplicaria, para que você possa revisar previamente as entidades criadas/atualizadas/excluídas.
* É útil antes de uma mudança arriscada, ao revisar uma mudança gerada por IA ou em um script que deve falhar se uma mudança inesperada estiver prestes a ser aplicada.
-Um dry run só antevê mudanças de **metadados**, e exige que o app tenha sido sincronizado ao menos uma vez (para que o workspace o conheça). Se você rodar isso em um app que nunca foi sincronizado, o servidor informa que o app não está instalado — rode `yarn twenty dev` uma vez antes.
+Um plano só antevê mudanças de **metadados**, e exige que o app tenha sido sincronizado ao menos uma vez (para que o workspace o conheça). Se você rodar isso em um app que nunca foi sincronizado, o servidor informa que o app não está instalado — rode `yarn twenty dev` uma vez antes.
## Escada de recuperação
Quando os metadados locais parecerem errados, aumente o nível nesta ordem e pare assim que estiver desbloqueado. Cada etapa é mais disruptiva que a anterior.
-1. **Ressincronizar.** Rode `yarn twenty dev --once` novamente. Sincronizações são idempotentes — rodar novamente um manifesto limpo é seguro e frequentemente resolve um problema transitório.
-2. **Prever o plano.** Rode `yarn twenty dev --once --dry-run` para ver exatamente o que a próxima sincronização pretende mudar, sem aplicá-la.
+1. **Ressincronizar.** Rode `yarn twenty apply` novamente. Sincronizações são idempotentes — rodar novamente um manifesto limpo é seguro e frequentemente resolve um problema transitório.
+2. **Prever o plano.** Rode `yarn twenty plan` para ver exatamente o que a próxima sincronização pretende mudar, sem aplicá-la.
3. **Leia o erro nomeado.** Se uma sincronização falhar, anote o tipo de metadado e o `universalIdentifier` na mensagem (veja acima) e localize essa entidade no seu manifesto. Um conflito geralmente aponta para um identificador duplicado ou reutilizado.
4. **Desinstalar e reinstalar.** `yarn twenty app:uninstall`, depois sincronize novamente (`yarn twenty dev`). Isso reconstrói os metadados do app a partir do zero, mantendo o restante do seu workspace intacto.
5. **Reset completo (último recurso).** `yarn twenty docker:reset`, depois faça o seeding e a sincronização novamente.
diff --git a/packages/twenty-docs/l/pt/developers/extend/apps/operations/testing.mdx b/packages/twenty-docs/l/pt/developers/extend/apps/operations/testing.mdx
index a9284a006d..ec08bc236d 100644
--- a/packages/twenty-docs/l/pt/developers/extend/apps/operations/testing.mdx
+++ b/packages/twenty-docs/l/pt/developers/extend/apps/operations/testing.mdx
@@ -78,6 +78,13 @@ Crie um `vitest.config.ts` na raiz do seu aplicativo:
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 ?? '';
+
+// 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({
@@ -88,66 +95,74 @@ export default defineConfig({
test: {
testTimeout: 120_000,
hookTimeout: 120_000,
+ fileParallelism: false,
include: ['src/**/*.integration-test.ts'],
- setupFiles: ['src/__tests__/setup-test.ts'],
+ globalSetup: ['src/__tests__/global-setup.ts'],
env: {
- TWENTY_API_URL: 'http://localhost:2020',
- TWENTY_API_KEY: 'your-api-key',
+ TWENTY_API_URL,
+ TWENTY_API_KEY,
},
},
});
```
-Crie um arquivo de configuração que verifique se o servidor está acessível antes da execução dos testes:
+Crie um arquivo de configuração global que verifique se o servidor está acessível, escreva uma configuração de teste para o SDK (`~/.twenty/config.test.json`) e sincronize o app antes da execução dos testes:
-```ts src/__tests__/setup-test.ts
+```ts src/__tests__/global-setup.ts
import * as fs from 'fs';
import * as os from 'os';
import * as path from 'path';
-import { beforeAll } from 'vitest';
-const TWENTY_API_URL = process.env.TWENTY_API_URL ?? 'http://localhost:2020';
-const TEST_CONFIG_DIR = path.join(os.tmpdir(), '.twenty-sdk-test');
+import { appDevOnce, appUninstall } from 'twenty-sdk/cli';
+
+const APP_PATH = process.cwd();
+const CONFIG_DIR = path.join(os.homedir(), '.twenty');
+
+export async function setup() {
+ const apiUrl = process.env.TWENTY_API_URL!;
+ const apiKey = process.env.TWENTY_API_KEY!;
-beforeAll(async () => {
// Verify the server is running
- const response = await fetch(`${TWENTY_API_URL}/healthz`);
-
+ const response = await fetch(`${apiUrl}/healthz`);
if (!response.ok) {
- throw new Error(
- `Twenty server is not reachable at ${TWENTY_API_URL}. ` +
- 'Start the server before running integration tests.',
- );
+ throw new Error(`Twenty server is not reachable at ${apiUrl}.`);
}
- // Write a temporary config for the SDK
- fs.mkdirSync(TEST_CONFIG_DIR, { recursive: true });
-
+ // Write the SDK's test config (the CLI reads config.test.json when NODE_ENV=test)
+ fs.mkdirSync(CONFIG_DIR, { recursive: true });
fs.writeFileSync(
- path.join(TEST_CONFIG_DIR, 'config.json'),
+ path.join(CONFIG_DIR, 'config.test.json'),
JSON.stringify({
- remotes: {
- local: {
- apiUrl: process.env.TWENTY_API_URL,
- apiKey: process.env.TWENTY_API_KEY,
- },
- },
+ remotes: { local: { apiUrl, apiKey } },
defaultRemote: 'local',
}, null, 2),
);
-});
+
+ // Start from a clean slate, then sync the app
+ await appUninstall({ appPath: APP_PATH }).catch(() => {});
+
+ const result = await appDevOnce({ appPath: APP_PATH });
+ if (!result.success) {
+ throw new Error(`Dev sync failed: ${result.error?.message}`);
+ }
+}
+
+export async function teardown() {
+ await appUninstall({ appPath: APP_PATH });
+}
```
## APIs programáticas do SDK
O subcaminho `twenty-sdk/cli` exporta funções que você pode chamar diretamente a partir do código de teste:
-| Função | Descrição |
-| -------------- | ------------------------------------------------------------ |
-| `appBuild` | Compilar o aplicativo e, opcionalmente, empacotar um tarball |
-| `appDeploy` | Enviar um tarball para o servidor |
-| `appInstall` | Instalar o aplicativo no espaço de trabalho ativo |
-| `appUninstall` | Desinstalar o aplicativo do espaço de trabalho ativo |
+| Função | Descrição |
+| -------------- | ---------------------------------------------------------------- |
+| `appBuild` | Compilar o aplicativo e, opcionalmente, empacotar um tarball |
+| `appDeploy` | Enviar um tarball para o servidor |
+| `appDevOnce` | Compila e sincroniza o app uma vez (igual a `yarn twenty apply`) |
+| `appInstall` | Instalar o aplicativo no espaço de trabalho ativo |
+| `appUninstall` | Desinstalar o aplicativo do espaço de trabalho ativo |
Cada função retorna um objeto de resultado com `success: boolean` e `data` ou `error`.
@@ -238,64 +253,10 @@ Você também pode executar a verificação de tipos no seu aplicativo sem execu
yarn twenty dev:typecheck
```
-Isso executa `tsc --noEmit` e informa quaisquer erros de tipo.
+Isso executa `tsc --noEmit` no `tsconfig.json` do seu app e informa quaisquer erros de tipo. Os apps criados pelo scaffold também incluem um script `yarn typecheck` que cobre arquivos de teste também (`tsconfig.spec.json`).
## CI com GitHub Actions
-O gerador de scaffold cria um workflow do GitHub Actions pronto para uso em `.github/workflows/ci.yml`. Ele executa seus testes de integração automaticamente a cada push para `main` e em pull requests.
+O gerador de scaffold cria um workflow pronto para uso em `.github/workflows/ci.yml`. A cada push para `main` e a cada pull request, ele inicia um servidor Twenty efêmero no runner (por meio da action `twentyhq/twenty/.github/actions/spawn-twenty-app-dev-test`) e então executa `yarn lint`, `yarn typecheck`, `yarn test:unit` e `yarn test` com `TWENTY_API_URL` / `TWENTY_API_KEY` apontando para esse servidor. Nenhum secret é necessário e você pode fixar a versão do servidor por meio da variável de ambiente `TWENTY_VERSION` no topo do workflow.
-O workflow:
-
-1. Faz checkout do seu código
-2. Inicializa um servidor Twenty temporário usando a ação `twentyhq/twenty/.github/actions/spawn-twenty-docker-image`
-3. Instala as dependências com `yarn install --immutable`
-4. Executa `yarn test` com `TWENTY_API_URL` e `TWENTY_API_KEY` injetados a partir das saídas da ação
-
-```yaml .github/workflows/ci.yml
-name: CI
-
-on:
- push:
- branches:
- - main
- pull_request: {}
-
-env:
- TWENTY_VERSION: latest
-
-jobs:
- test:
- runs-on: ubuntu-latest
- steps:
- - name: Checkout
- uses: actions/checkout@v4
-
- - name: Spawn Twenty instance
- id: twenty
- uses: twentyhq/twenty/.github/actions/spawn-twenty-docker-image@main
- with:
- twenty-version: ${{ env.TWENTY_VERSION }}
- github-token: ${{ secrets.GITHUB_TOKEN }}
-
- - name: Enable Corepack
- run: corepack enable
-
- - name: Setup Node.js
- uses: actions/setup-node@v4
- with:
- node-version-file: '.nvmrc'
- cache: 'yarn'
-
- - name: Install dependencies
- run: yarn install --immutable
-
- - name: Run integration tests
- run: yarn test
- env:
- TWENTY_API_URL: ${{ steps.twenty.outputs.server-url }}
- TWENTY_API_KEY: ${{ steps.twenty.outputs.access-token }}
-```
-
-Você não precisa configurar nenhum segredo — a ação `spawn-twenty-docker-image` inicia um servidor Twenty efêmero diretamente no runner e fornece os detalhes de conexão. O segredo `GITHUB_TOKEN` é fornecido automaticamente pelo GitHub.
-
-Para fixar uma versão específica do Twenty em vez de `latest`, altere a variável de ambiente `TWENTY_VERSION` no topo do workflow.
+Consulte [Publicação → CI/CD automatizado](/l/pt/developers/extend/apps/operations/publishing#automated-cicd-scaffolded-workflows) para um passo a passo completo de ambos os workflows criados pelo scaffold (`ci.yml` e o pipeline de deploy `cd.yml`).
diff --git a/packages/twenty-docs/l/pt/developers/extend/apps/tutorials/document-generator/building-the-ui.mdx b/packages/twenty-docs/l/pt/developers/extend/apps/tutorials/document-generator/building-the-ui.mdx
index a6e388abf2..39d2279065 100644
--- a/packages/twenty-docs/l/pt/developers/extend/apps/tutorials/document-generator/building-the-ui.mdx
+++ b/packages/twenty-docs/l/pt/developers/extend/apps/tutorials/document-generator/building-the-ui.mdx
@@ -91,9 +91,11 @@ const GenerateDocumentForm = () => {
}, []);
const generate = async () => {
- const apiBaseUrl = process.env.TWENTY_API_URL;
+ // Prefer the injected functions URL; fall back to the legacy /s prefix (self-hosted/local)
+ const functionsBaseUrl =
+ process.env.TWENTY_FUNCTIONS_URL || `${process.env.TWENTY_API_URL}/s`;
const token = process.env.TWENTY_APP_ACCESS_TOKEN ?? process.env.TWENTY_API_KEY;
- const res = await fetch(`${apiBaseUrl}/s/documents/generate`, {
+ const res = await fetch(`${functionsBaseUrl}/documents/generate`, {
method: 'POST',
headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` },
body: JSON.stringify({ templateId, recordId }),
@@ -186,7 +188,9 @@ const DocumentViewer = () => {
const recordId = useFrontComponentExecutionContext((c) => c.recordId ?? null);
// ...load { content, file } for recordId, then derive the links:
const pdfUrl = document.file?.[0]?.url;
- const webUrl = `${process.env.TWENTY_API_URL ?? ''}/s/documents/view?id=${recordId}`;
+ const functionsBaseUrl =
+ process.env.TWENTY_FUNCTIONS_URL || `${process.env.TWENTY_API_URL ?? ''}/s`;
+ const webUrl = `${functionsBaseUrl}/documents/view?id=${recordId}`;
// Render the template body, plus quick links to the web page and the PDF.
// Links open in a new tab so they don't navigate the embedded component.
diff --git a/packages/twenty-docs/l/pt/developers/extend/apps/tutorials/document-generator/http-routes.mdx b/packages/twenty-docs/l/pt/developers/extend/apps/tutorials/document-generator/http-routes.mdx
index 7c98a00980..f4eaefab57 100644
--- a/packages/twenty-docs/l/pt/developers/extend/apps/tutorials/document-generator/http-routes.mdx
+++ b/packages/twenty-docs/l/pt/developers/extend/apps/tutorials/document-generator/http-routes.mdx
@@ -9,8 +9,15 @@ O mesmo manipulador também pode responder solicitações HTTP. Vamos adicionar
* um terminal **POST** aponta as chamadas da UI para gerar um documento e
* um endpoint de **GET** público que renderiza um documento como uma página web impressa.
-Ambos usam `httpRouteTriggerSettings`. As rotas de aplicativos são servidas em `/s` no seu servidor
-Vinte (por exemplo, `http://localhost:2020/s/documents/generate`).
+Ambos usam `httpRouteTriggerSettings`. No servidor local de desenvolvimento, as rotas de aplicativos são
+servidas sob o prefixo `/s` (por exemplo, `http://localhost:2020/s/documents/generate`).
+
+
+Em Vinte nuvens, as rotas são servidas no domínio de funções dedicadas do espaço de trabalho
+— a URL de 20 injeções como `TWENTY_FUNCTIONS_URL`, sem prefixo `/s`. O prefixo `/s`
+está obsoleto e só permanece para instâncias auto-hospedadas e locais.
+Ver [Chamando uma função lógica](/l/pt/developers/extend/apps/layout/front-components#calling-a-logic-function).
+
## Rota POST - gerar sob demanda
diff --git a/packages/twenty-docs/l/pt/developers/extend/apps/tutorials/document-generator/publishing.mdx b/packages/twenty-docs/l/pt/developers/extend/apps/tutorials/document-generator/publishing.mdx
index 6c3957bd3a..dbf435150c 100644
--- a/packages/twenty-docs/l/pt/developers/extend/apps/tutorials/document-generator/publishing.mdx
+++ b/packages/twenty-docs/l/pt/developers/extend/apps/tutorials/document-generator/publishing.mdx
@@ -77,11 +77,11 @@ Executar os mesmos portões CI do portão:
yarn lint # oxlint
yarn typecheck # tsgo
yarn test:unit # unit tests
-yarn twenty dev --once --dry-run # preview the metadata diff
+yarn twenty plan # preview the metadata diff
```
-A corrida seca imprime exatamente o que mudaria no servidor sem aplicá-lo —
-uma boa verificação de sanidade final. Veja
+O plano mostra exatamente o que mudaria no servidor sem aplicar as alterações —
+um bom último teste de sanidade. Veja
[Testing](/l/pt/developers/extend/apps/operations/testing) e
[Sincronização e recuperação](/l/pt/developers/extend/apps/operations/sync-and-recovery).
diff --git a/packages/twenty-docs/l/ro/developers/extend/apps/config/install-hooks.mdx b/packages/twenty-docs/l/ro/developers/extend/apps/config/install-hooks.mdx
index 96ba0e29aa..d4cb463f4e 100644
--- a/packages/twenty-docs/l/ro/developers/extend/apps/config/install-hooks.mdx
+++ b/packages/twenty-docs/l/ro/developers/extend/apps/config/install-hooks.mdx
@@ -4,7 +4,7 @@ description: Rulați logică înainte sau după instalare — pentru a popula cu
icon: wrench
---
-Hook-urile de instalare sunt funcții logice speciale care rulează în timpul ciclului de viață al instalării sau actualizării. Acestea folosesc același runtime de handler ca și [funcțiile logice](/l/ro/developers/extend/apps/logic/logic-functions) obișnuite și primesc un `InstallPayload`, dar sunt declarate cu propriile lor funcții de definire — `definePostInstallLogicFunction()` și `definePreInstallLogicFunction()` — și există în afara modelului obișnuit de declanșatori (HTTP, cron, evenimente de bază de date).
+Hook-urile de instalare sunt funcții logice speciale care rulează în timpul ciclului de viață al instalării sau actualizării. Acestea folosesc același runtime de handler ca și [funcțiile logice](/l/ro/developers/extend/apps/logic/logic-functions) obișnuite și primesc un `InstallPayload` (`{ previousVersion?: string; newVersion: string }` — `previousVersion` este `undefined` la o instalare nouă), dar sunt declarate cu propriile lor funcții de definire și există în afara modelului obișnuit de declanșatori (HTTP, cron, evenimente de bază de date).
Fiecare aplicație poate defini **cel mult o funcție de pre-instalare** și **cel mult o funcție de post-instalare**. Construirea manifestului va genera o eroare dacă se detectează mai mult de una din oricare dintre ele.
@@ -19,111 +19,59 @@ Fiecare aplicație poate defini **cel mult o funcție de pre-instalare** și **c
└─────────────────────────────────────────────────────────────┘
```
-
-
+## Dintr-o privire
-O funcție de post-instalare rulează automat după ce aplicația a terminat de instalat într-un spațiu de lucru. Serverul o execută **după** ce metadatele aplicației au fost sincronizate și clientul SDK a fost generat, astfel încât spațiul de lucru este complet pregătit pentru utilizare, iar noua schemă este disponibilă. Cazuri tipice de utilizare includ popularea cu date implicite, crearea de înregistrări inițiale, configurarea setărilor spațiului de lucru sau provizionarea resurselor în cadrul serviciilor terților.
+| | `definePreInstallLogicFunction` | `definePostInstallLogicFunction` |
+| ---------------- | ------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
+| Rulări | Înainte de migrarea metadatelor — schema și datele **anterioare** sunt încă intacte | După migrare și generarea SDK — schema **nouă** este aplicată |
+| Execuție | Întotdeauna sincronă; blochează instalarea | Async în mod implicit (pus în coadă, 3 reîncercări); execuție sincronă opțională prin `shouldRunSynchronously: true` |
+| La eșec | Instalarea este **întreruptă** înainte de orice modificare a schemei | Async: reîncercată de până la 3 ori. Sync: apelantul primește `POST_INSTALL_ERROR` (modificările de schemă **nu** sunt anulate) |
+| Utilizare tipică | Faceți backup sau reparați date pe care o migrare le-ar pierde; refuzați un upgrade riscant aruncând o eroare | Populați date implicite, configurați workspace-ul, înregistrați resurse externe |
-```ts src/logic-functions/post-install.ts
-import { definePostInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define';
+**Regulă generală:** folosiți implicit post-install. Apelați la pre-install doar când migrarea în sine este distructivă și trebuie să interceptați starea anterioară înainte să dispară.
-const handler = async (payload: InstallPayload): Promise => {
- console.log('Post install logic function executed successfully!', payload.previousVersion);
-};
+| Doriți să... | Folosiți |
+| ------------------------------------------------------------------------------------- | ------------------------------------------------------------------ |
+| Populați date, configurați workspace-ul, înregistrați resurse externe | `post-install` |
+| Muncă de durată care nu ar trebui să blocheze răspunsul la instalare | `post-install` (mod async implicit, cu reîncercări ale workerului) |
+| Configurare rapidă de care apelantul are nevoie imediat după ce instalarea se încheie | `post-install` cu `shouldRunSynchronously: true` |
+| Citiți sau faceți backup datelor pe care migrarea iminentă le-ar pierde | `pre-install` |
+| Respingeți o actualizare care ar corupe datele existente | `pre-install` (aruncați din handler) |
+| Reconciliere la fiecare upgrade | Oricare hook cu `shouldRunOnVersionUpgrade: true` |
-export default definePostInstallLogicFunction({
- universalIdentifier: 'f7a2b9c1-3d4e-5678-abcd-ef9876543210',
- name: 'post-install',
- description: 'Runs after installation to set up the application.',
- timeoutSeconds: 300,
- shouldRunOnVersionUpgrade: false,
- shouldRunSynchronously: false,
- handler,
-});
-```
+## Comportament partajat de ambele hook-uri
-Puteți, de asemenea, să executați manual funcția post-instalare oricând folosind CLI:
+* Configurația este o configurație `defineLogicFunction` minus setările de declanșare, plus `shouldRunOnVersionUpgrade`.
+* **Când rulează**: doar la instalări noi, în mod implicit. Setați `shouldRunOnVersionUpgrade: true` pentru a rula și la upgrade-uri. Folosiți `previousVersion` / `newVersion` pentru a ramifica în funcție de calea de upgrade.
+* **Idempotența contează**: post-install async poate fi reîncercat, iar oricare hook rulează din nou la upgrade-uri când `shouldRunOnVersionUpgrade` este activat.
+* Mediul obișnuit de logic-function (`APPLICATION_ID`, `APP_ACCESS_TOKEN`, `API_URL`) este injectat, astfel încât puteți apela Twenty API cu tokenul aplicației voastre.
+* Hook-ul este atașat automat la manifestul aplicației la build (`preInstallLogicFunction` / `postInstallLogicFunction`) — nu este nevoie să fie referențiat în [`defineApplication()`](/l/ro/developers/extend/apps/config/application).
+* Valoarea implicită pentru `timeoutSeconds` este 300 pentru a permite sarcini de configurare mai lungi, cum ar fi popularea datelor.
+* **Nu este executat în modul dev**: `yarn twenty dev` sare peste fluxul de instalare și sincronizează fișierele direct, astfel încât hook-urile nu rulează acolo. Declanșați-le manual în schimb:
```bash filename="Terminal"
yarn twenty dev:function:exec --postInstall
-```
-
-Puncte cheie:
-* Funcțiile de post-instalare folosesc `definePostInstallLogicFunction()` — o variantă specializată care omite setările de declanșare (`cronTriggerSettings`, `databaseEventTriggerSettings`, `httpRouteTriggerSettings`, `toolTriggerSettings`, `workflowActionTriggerSettings`).
-* Handlerul primește un `InstallPayload` cu `{ previousVersion?: string; newVersion: string }` — `newVersion` este versiunea care este instalată, iar `previousVersion` este versiunea instalată anterior (sau `undefined` la o instalare nouă). Folosiți aceste valori pentru a distinge instalările noi de actualizări și pentru a rula logică de migrare specifică versiunii.
-* **Când rulează hook-ul**: doar la instalări noi, în mod implicit. Transmiteți `shouldRunOnVersionUpgrade: true` dacă doriți să ruleze și atunci când aplicația este actualizată de la o versiune anterioară. Când este omis, indicatorul are implicit valoarea `false`, iar actualizările sar peste hook.
-* **Model de execuție — implicit asincron, sincron opțional**: indicatorul `shouldRunSynchronously` controlează *modul în care* este executat post-install.
- * `shouldRunSynchronously: false` *(implicit)* — hook-ul este **pus în coadă în message queue** cu `retryLimit: 3` și rulează asincron într-un worker. Răspunsul la instalare revine imediat ce jobul este pus în coadă, astfel încât un handler lent sau care eșuează nu blochează apelantul. Workerul va reîncerca de până la trei ori. **Folosiți acest mod pentru joburi de lungă durată** — popularea unor seturi mari de date, apelarea API-urilor lente ale terților, provizionarea resurselor externe, orice ar putea depăși o fereastră rezonabilă de răspuns HTTP.
- * `shouldRunSynchronously: true` — hook-ul este executat **inline în timpul fluxului de instalare** (același executor ca pre-install). Cererea de instalare blochează până când handlerul se termină, iar dacă acesta aruncă o eroare, apelantul instalării primește un `POST_INSTALL_ERROR`. Fără reîncercări automate. **Folosiți acest mod pentru sarcini rapide, care trebuie să se finalizeze înainte de răspuns** — de exemplu, emiterea unei erori de validare către utilizator sau o configurare rapidă de care clientul va depinde imediat după ce apelul de instalare revine. Reține că migrarea metadatelor a fost deja aplicată până când rulează post-install, astfel încât un eșec în modul sincron **nu** anulează modificările de schemă — doar expune eroarea.
-* Asigurați-vă că handlerul dvs. este idempotent. În modul asincron, coada poate reîncerca de până la trei ori; în oricare mod, hook-ul poate rula din nou la actualizări când `shouldRunOnVersionUpgrade: true`.
-* Variabilele de mediu `APPLICATION_ID`, `APP_ACCESS_TOKEN` și `API_URL` sunt disponibile în interiorul handlerului (la fel ca în orice altă funcție logică), astfel încât puteți apela API-ul Twenty cu un token de acces al aplicației limitat la aplicația dvs.
-* Este permisă o singură funcție de post-instalare per aplicație. Construirea manifestului va genera o eroare dacă este detectată mai mult de una.
-* `universalIdentifier`, `shouldRunOnVersionUpgrade` și `shouldRunSynchronously` ale funcției sunt atașate automat la manifestul aplicației în câmpul `postInstallLogicFunction` în timpul build-ului — nu este nevoie să le referiți în [`defineApplication()`](/l/ro/developers/extend/apps/config/application).
-* Timpul de expirare implicit este setat la 300 de secunde (5 minute) pentru a permite sarcini de configurare mai lungi, cum ar fi popularea datelor.
-* **Nu se execută în modul dev**: când o aplicație este înregistrată local (prin `yarn twenty dev`), serverul sare complet peste fluxul de instalare și sincronizează fișierele direct prin watcher-ul CLI — astfel încât post-install nu rulează niciodată în modul dev, indiferent de `shouldRunSynchronously`. Folosiți `yarn twenty dev:function:exec --postInstall` pentru a-l declanșa manual într-un workspace care rulează.
-
-
-
-
-O funcție de pre-instalare rulează automat în timpul instalării, **înainte ca migrarea metadatelor workspace-ului să fie aplicată**. Are aceeași structură a payload-ului ca post-install (`InstallPayload`), dar este plasată mai devreme în fluxul de instalare, astfel încât poate pregăti starea de care depinde migrarea iminentă — utilizări tipice includ realizarea unui backup al datelor, validarea compatibilității cu noua schemă sau arhivarea înregistrărilor care urmează să fie restructurate sau eliminate.
-
-```ts src/logic-functions/pre-install.ts
-import { definePreInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define';
-
-const handler = async (payload: InstallPayload): Promise => {
- console.log('Pre install logic function executed successfully!', payload.previousVersion);
-};
-
-export default definePreInstallLogicFunction({
- universalIdentifier: 'a1b2c3d4-5678-90ab-cdef-1234567890ab',
- name: 'pre-install',
- description: 'Runs before installation to prepare the application.',
- timeoutSeconds: 300,
- shouldRunOnVersionUpgrade: true,
- handler,
-});
-```
-
-Puteți, de asemenea, să executați manual funcția de pre-instalare oricând folosind CLI:
-
-```bash filename="Terminal"
yarn twenty dev:function:exec --preInstall
```
-Puncte cheie:
-* Funcțiile de pre-instalare folosesc `definePreInstallLogicFunction()` — aceeași configurare specializată ca pentru post-install, doar că atașată la un alt punct din ciclul de viață.
-* Atât handlerele de pre-install, cât și cele de post-install primesc același tip `InstallPayload`: `{ previousVersion?: string; newVersion: string }`. Importați-l o singură dată și reutilizați-l pentru ambele hook-uri.
-* **Când rulează hook-ul**: poziționat chiar înainte de migrarea metadatelor workspace-ului (`synchronizeFromManifest`). Înainte de execuție, serverul rulează un "sync redus", pur aditiv, care înregistrează funcția de pre-instalare a versiunii **noi** în metadatele workspace-ului — nimic altceva nu este atins — și apoi o execută. Deoarece acest sync este doar aditiv, obiectele, câmpurile și datele versiunii precedente sunt încă intacte când rulează handlerul dvs.: puteți citi și face backup în siguranță stării pre-migrare.
-* **Model de execuție**: pre-install este executat **sincron** și **blochează instalarea**. Dacă handlerul aruncă o eroare, instalarea este întreruptă înainte ca orice modificări de schemă să fie aplicate — workspace-ul rămâne la versiunea anterioară într-o stare consistentă. Acest lucru este intenționat: pre-install este ultima dvs. șansă de a refuza o actualizare riscantă.
-* La fel ca la post-install, este permisă o singură funcție de pre-instalare per aplicație. Este atașată automat la manifestul aplicației sub `preInstallLogicFunction` în timpul build-ului.
-* **Nu se execută în modul dev**: la fel ca post-install — fluxul de instalare este sărit complet pentru aplicațiile înregistrate local, astfel încât pre-install nu rulează niciodată sub `yarn twenty dev`. Folosiți `yarn twenty dev:function:exec --preInstall` pentru a-l declanșa manual.
+
+
-
-
-
-Ambele hook-uri fac parte din același flux de instalare și primesc același `InstallPayload`. Diferența constă în **momentul** în care rulează în raport cu migrarea metadatelor workspace-ului, iar asta schimbă ce date pot atinge în siguranță.
-
-Pre-install este întotdeauna **sincron** (blochează instalarea și o poate întrerupe). Post-install este **implicit asincron** — pus în coadă pe un worker cu reîncercări automate — dar poate opta pentru execuție sincronă cu `shouldRunSynchronously: true`. Consultați acordeonul `definePostInstallLogicFunction` de mai sus pentru când să folosiți fiecare mod.
-
-**Folosiți `post-install` pentru orice are nevoie ca noua schemă să existe.** Acesta este cazul obișnuit:
-
-* Popularea datelor implicite (crearea înregistrărilor inițiale, a vizualizărilor implicite, a conținutului demo) pentru obiectele și câmpurile adăugate recent.
-* Înregistrarea webhook-urilor la servicii terțe, acum că aplicația are acreditările sale.
-* Apelarea propriului dvs. API pentru a finaliza configurarea care depinde de metadatele sincronizate.
-* Logică idempotentă de tipul "asigurați-vă că acest lucru există" care ar trebui să reconcilieze starea la fiecare actualizare — combină cu `shouldRunOnVersionUpgrade: true`.
-
-Exemplu — populează o înregistrare `PostCard` implicită după instalare:
+Rulează după ce aplicația voastră a terminat instalarea: metadate sincronizate, clientul SDK generat, noua schemă poate fi interogată. Exemplu — populează o înregistrare implicită la instalări noi:
```ts src/logic-functions/post-install.ts
import { definePostInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define';
-import { createClient } from './generated/client';
+import { CoreApiClient } from 'twenty-client-sdk/core';
const handler = async ({ previousVersion }: InstallPayload): Promise => {
if (previousVersion) return; // fresh installs only
- const client = createClient();
- await client.postCard.create({
- data: { title: 'Welcome to Postcard', content: 'Your first card!' },
+ const client = new CoreApiClient();
+ await client.mutation({
+ createPostCard: {
+ __args: { data: { name: 'Welcome to Postcard', content: 'Your first card!' } },
+ id: true,
+ },
});
};
@@ -133,22 +81,28 @@ export default definePostInstallLogicFunction({
description: 'Seeds a welcome post card after install.',
timeoutSeconds: 300,
shouldRunOnVersionUpgrade: false,
+ shouldRunSynchronously: false,
handler,
});
```
-**Folosiți `pre-install` atunci când o migrare altfel ar distruge sau ar corupe datele existente.** Deoarece pre-install rulează pe schema *anterioară* și eșecul său anulează actualizarea, acesta este locul potrivit pentru orice este riscant:
+Flag-ul `shouldRunSynchronously` controlează modelul de execuție:
-* **Crearea unui backup al datelor care urmează să fie eliminate sau restructurate** — de exemplu, eliminați un câmp în v2 și trebuie să-i copiați valorile într-un alt câmp sau să le exportați în stocare înainte de rularea migrării.
-* **Arhivarea înregistrărilor pe care o nouă constrângere le-ar invalida** — de exemplu, un câmp devine `NOT NULL` și trebuie mai întâi să ștergeți sau să corectați rândurile cu valori nule.
-* **Validarea compatibilității și refuzarea actualizării dacă datele curente nu pot fi migrate fără probleme** — aruncați din handler și instalarea se oprește fără ca modificări să fie aplicate. Aceasta este mai sigur decât să descoperi incompatibilitatea în mijlocul migrării.
-* **Redenumirea sau schimbarea cheilor datelor** înaintea unei modificări de schemă care ar pierde asocierile.
+* `false` *(implicit)* — pus în coada de mesaje (`retryLimit: 3`) și rulat de un worker. Răspunsul la instalare este returnat imediat ce jobul este pus în coadă. **Folosiți pentru muncă de durată** — popularea unor seturi mari de date, API-uri lente ale terților.
+* `true` — executat inline în timpul fluxului de instalare. Requestul de instalare este blocat până când handlerul se termină; o eroare aruncată este expusă apelantului ca `POST_INSTALL_ERROR` (fără reîncercări). **Folosiți pentru muncă rapidă, care trebuie să fie finalizată înainte de răspuns.** Migrarea a fost deja aplicată în acest punct, astfel încât un eșec nu anulează modificările de schemă — doar expune eroarea.
-Exemplu — arhivează înregistrări înainte de o migrare distructivă:
+
+
+
+Rulează înainte de migrarea metadatelor, pe schema **anterioară** — locul potrivit pentru a face backup datelor pe care o migrare le-ar pierde sau pentru a refuza un upgrade riscant. Înainte de execuție, serverul rulează un „sync redus”, pur aditiv, care înregistrează doar funcția de pre-instalare a versiunii noi; tot restul — obiectele, câmpurile și datele versiunii anterioare — rămâne neatins atunci când rulează handlerul.
+
+Pre-install este întotdeauna **sincron** și blochează instalarea. Dacă handlerul aruncă o eroare, instalarea este întreruptă înainte de orice modificare a schemei — workspace-ul rămâne la versiunea anterioară într-o stare consistentă. Acest lucru este intenționat: pre-install este ultima dvs. șansă de a refuza o actualizare riscantă.
+
+Exemplu — copiați valorile unui câmp vechi înainte ca migrarea să îl elimine:
```ts src/logic-functions/pre-install.ts
import { definePreInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define';
-import { createClient } from './generated/client';
+import { CoreApiClient } from 'twenty-client-sdk/core';
const handler = async ({ previousVersion, newVersion }: InstallPayload): Promise => {
// Only the 1.x → 2.x upgrade drops the legacy `notes` field.
@@ -156,24 +110,24 @@ const handler = async ({ previousVersion, newVersion }: InstallPayload): Promise
return;
}
- const client = createClient();
- const legacyRecords = await client.postCard.findMany({
- where: { notes: { isNotNull: true } },
+ const client = new CoreApiClient();
+ const { postCards } = await client.query({
+ postCards: {
+ __args: { filter: { notes: { isNot: null } } },
+ edges: { node: { id: true, notes: true } },
+ },
});
- if (legacyRecords.length === 0) return;
-
- // Copy legacy `notes` into the new `description` field before the migration
- // drops the `notes` column. If this fails, the upgrade is aborted and the
- // workspace stays on v1 with all data intact.
- await Promise.all(
- legacyRecords.map((record) =>
- client.postCard.update({
- where: { id: record.id },
- data: { description: record.notes },
- }),
- ),
- );
+ // Copy legacy `notes` into `description` before the migration drops the
+ // column. If this fails, the upgrade aborts and the workspace stays on v1.
+ for (const { node } of postCards.edges) {
+ await client.mutation({
+ updatePostCard: {
+ __args: { id: node.id, data: { description: node.notes } },
+ id: true,
+ },
+ });
+ }
};
export default definePreInstallLogicFunction({
@@ -186,21 +140,5 @@ export default definePreInstallLogicFunction({
});
```
-**Regulă practică:**
-
-| Doriți să... | Folosiți |
-| ------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------ |
-| Populați date implicite, configurați workspace-ul, înregistrați resurse externe | `post-install` |
-| Rulați populări de durată sau apeluri către terți care nu ar trebui să blocheze răspunsul la instalare | `post-install` (implicit — `shouldRunSynchronously: false`, cu reîncercări ale workerului) |
-| Rulați o configurare rapidă de care apelantul va depinde imediat după ce apelul de instalare revine | `post-install` cu `shouldRunSynchronously: true` |
-| Citiți sau faceți backup datelor pe care migrarea iminentă le-ar pierde | `pre-install` |
-| Respingeți o actualizare care ar corupe datele existente | `pre-install` (aruncați din handler) |
-| Rulați o reconciliere la fiecare actualizare | `post-install` cu `shouldRunOnVersionUpgrade: true` |
-| Faceți o configurare unică doar la prima instalare | `post-install` cu `shouldRunOnVersionUpgrade: false` (implicit) |
-
-
-Dacă aveți dubii, alegeți implicit **post-install**. Apelați la pre-install doar când migrarea în sine este distructivă și trebuie să interceptați starea anterioară înainte să dispară.
-
-
diff --git a/packages/twenty-docs/l/ro/developers/extend/apps/data/objects.mdx b/packages/twenty-docs/l/ro/developers/extend/apps/data/objects.mdx
index ad568266cb..4c61b9d296 100644
--- a/packages/twenty-docs/l/ro/developers/extend/apps/data/objects.mdx
+++ b/packages/twenty-docs/l/ro/developers/extend/apps/data/objects.mdx
@@ -86,6 +86,22 @@ export default defineObject({
**Câmpurile de bază sunt adăugate automat.** Când definiți un obiect personalizat, Twenty creează pentru dvs. câmpuri standard precum `id`, `name`, `createdAt`, `updatedAt`, `createdBy`, `updatedBy` și `deletedAt`. Nu trebuie să le declarați în tabloul `fields` — doar câmpurile dvs. personalizate. Puteți suprascrie un câmp implicit declarând unul cu același nume, dar acest lucru este rareori o idee bună.
+## Tipuri de câmpuri
+
+Setul complet de valori `FieldType`, exportate din `twenty-sdk/define`:
+
+| Categorie | Tipuri |
+| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
+| Text | `TEXT`, `RICH_TEXT`, `ARRAY` (de șiruri), `RAW_JSON` |
+| Numerice | `NUMBER` (`universalSettings.dataType`: `'float'` / `'int'` / `'bigint'`), `NUMERIC` (precizie arbitrară), `RATING`, `POSITION` |
+| Date calendaristice | `DATE`, `DATE_TIME` |
+| Alegere | `BOOLEAN`, `SELECT`, `MULTI_SELECT` |
+| Compuse | `FULL_NAME`, `ADDRESS`, `EMAILS`, `PHONES`, `LINKS`, `CURRENCY`, `ACTOR`, `FILES` |
+| Identificatori și relații | `UUID`, `RELATION`, `MORPH_RELATION` (vezi [Relații](/l/ro/developers/extend/apps/data/relations)) |
+| Sistem | `TS_VECTOR` (vector de căutare full-text, gestionat de server) |
+
+Tipurile compuse stochează mai multe sub‑câmpuri (de ex. `FULL_NAME` = prenume + nume de familie; `CURRENCY` = `amountMicros` + `currencyCode`). `SELECT` și `MULTI_SELECT` necesită un tablou `options`, ca în exemplul de mai sus.
+
## Valori implicite
Valorile implicite de tip șir literal trebuie să fie încadrate în ghilimele simple **în interiorul** șirului — `defaultValue: "'Draft'"`, nu `defaultValue: "Draft"`. De aceea câmpul `status` de mai sus folosește `` `'${PostCardStatus.DRAFT}'` ``.
diff --git a/packages/twenty-docs/l/ro/developers/extend/apps/getting-started/project-structure.mdx b/packages/twenty-docs/l/ro/developers/extend/apps/getting-started/project-structure.mdx
index d790de419e..0f9f90e9a8 100644
--- a/packages/twenty-docs/l/ro/developers/extend/apps/getting-started/project-structure.mdx
+++ b/packages/twenty-docs/l/ro/developers/extend/apps/getting-started/project-structure.mdx
@@ -14,26 +14,39 @@ my-twenty-app/
default-role.ts # Permissions for logic functions
constants/
universal-identifiers.ts # Auto-generated UUIDs and metadata
+ front-components/
+ main-page.tsx # Welcome page component
+ navigation-menu-items/
+ main-page.navigation-menu-item.ts # Sidebar entry for the welcome page
+ page-layouts/
+ main-page.page-layout.ts # Standalone page hosting the component
__tests__/
- setup-test.ts
- app-install.integration-test.ts
- .github/workflows/ci.yml # GitHub Actions
- public/ # Static assets
- vitest.config.ts # Test runner config
+ application-config.test.ts # Unit test
+ global-setup.ts # Integration test setup (sync + uninstall)
+ schema.integration-test.ts # Integration test against a live server
+ .github/workflows/
+ ci.yml # Lint, typecheck, unit + integration tests
+ cd.yml # Deploy + install on push to main
+ public/
+ logo.svg # Static assets
+ vitest.config.ts # Integration test runner config
+ vitest.unit.config.ts # Unit test runner config
tsconfig.json, tsconfig.spec.json
.nvmrc, .yarnrc.yml, .oxlintrc.json
- README.md, LLMS.md
+ README.md, AGENTS.md, CLAUDE.md
```
## Fișiere cheie
-| Fișier / Folder | Scop |
-| ---------------------------------------- | -------------------------------------------------------------------- |
-| `src/application-config.ts` | **Necesar.** Fișierul principal de configurare pentru aplicație. |
-| `src/default-role.ts` | Rol implicit care controlează la ce pot avea acces funcțiile logice. |
-| `src/constants/universal-identifiers.ts` | UUID-uri generate automat și metadate (nume afișat, descriere). |
-| `src/__tests__/` | Teste de integrare (configurare + test de exemplu). |
-| `public/` | Resurse statice (imagini, fonturi) servite împreună cu aplicația. |
+| Fișier / Folder | Scop |
+| -------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- |
+| `src/application-config.ts` | **Necesar.** Fișierul principal de configurare pentru aplicație. |
+| `src/default-role.ts` | Rol implicit care controlează la ce pot avea acces funcțiile logice. |
+| `src/constants/universal-identifiers.ts` | UUID-uri generate automat și metadate (nume afișat, descriere). |
+| `src/front-components/`, `src/navigation-menu-items/`, `src/page-layouts/` | O pagină de bun venit de pornire: un front component redat de un page layout autonom, accesibilă din bara laterală. |
+| `src/__tests__/` | Un test unitar plus un test de integrare (cu configurarea sa globală) care sincronizează aplicația cu un server real. |
+| `public/` | Resurse statice (imagini, fonturi) servite împreună cu aplicația. |
+| `AGENTS.md` / `CLAUDE.md` | Ghid pentru agenții AI de programare care lucrează la aplicație. |
**Organizarea fișierelor ține de dvs.** Folderele de mai sus sunt convenții — SDK-ul detectează entitățile prin analiză AST pe apelurile `export default defineEntity(...)`, indiferent unde se află fișierul.
@@ -47,15 +60,18 @@ Ambele pachete Twenty SDK trebuie plasate sub `devDependencies`, nu sub `depende
{
"dependencies": {},
"devDependencies": {
- "twenty-client-sdk": "^2.13.0",
- "twenty-sdk": "^2.13.0"
+ "twenty-client-sdk": "2.20.0",
+ "twenty-sdk": "2.20.0",
+ "twenty-ui": "1.0.0-alpha.1"
}
}
```
+Generatorul de schelete fixează versiunile `twenty-sdk` și `twenty-client-sdk` la propria sa versiune — păstrează-le sincronizate când faci upgrade.
+
* **`twenty-sdk`** livrează CLI-ul `twenty` și uneltele de build/scaffolding. Acesta rulează doar în timpul dezvoltării și al build-ului și nu este niciodată importat de runtime-ul aplicației tale publicate.
* **`twenty-client-sdk`** este importat de codul aplicației tale (`CoreApiClient`, `MetadataApiClient`, `RestApiClient`), dar Twenty îl furnizează la runtime — funcțiile de logică îl obțin dintr-un strat SDK generat, iar componentele de interfață îl rezolvă din module livrate de server. Copia instalată local este folosită doar pentru verificarea tipurilor și pentru build-ul la momentul de deploy, astfel că nu trebuie niciodată inclusă în bundle-ul livrat.
-Păstrarea oricărui pachet sub `dependencies` îl include în bundle-ul de runtime al aplicației instalate, unde reprezintă o încărcătură inutilă. `twenty build` emite un avertisment atunci când oricare dintre ele este încă listat sub `dependencies`.
+Păstrarea oricărui pachet sub `dependencies` îl include în bundle-ul de runtime al aplicației instalate, unde reprezintă o încărcătură inutilă. `twenty dev:build` emite un avertisment atunci când oricare dintre ele este încă listat sub `dependencies`.
Adaugă dependențele de runtime proprii ale aplicației tale (bibliotecile pe care funcțiile tale de logică chiar le importă la runtime) sub `dependencies`, ca de obicei.
diff --git a/packages/twenty-docs/l/ro/developers/extend/apps/getting-started/quick-start.mdx b/packages/twenty-docs/l/ro/developers/extend/apps/getting-started/quick-start.mdx
index 7815182286..0e524e23df 100644
--- a/packages/twenty-docs/l/ro/developers/extend/apps/getting-started/quick-start.mdx
+++ b/packages/twenty-docs/l/ro/developers/extend/apps/getting-started/quick-start.mdx
@@ -6,17 +6,17 @@ description: Creați prima dvs. aplicație Twenty în câteva minute.
## Cerințe
-* **Node.js 24+** — [Descărcați](https://nodejs.org/)
+* **Node.js 24.5+** — [Descărcați](https://nodejs.org/)
* **Yarn 4** — vine împreună cu Node prin Corepack. Activați-l: `corepack enable`
* **Docker** — [Descărcați](https://www.docker.com/products/docker-desktop/). Necesar pentru a rula un server Twenty local. Omiteți dacă rulați deja Twenty în altă parte.
Crearea unei aplicații Twenty are trei faze. Generatorul le reunește într-o singură comandă pe calea optimă, dar fiecare fază este un concept separat — când ceva eșuează, dacă știți în ce fază sunteți, știți ce trebuie să corectați.
-| Fază | Ce faceți | Instrument | Rezultat |
-| ----------------------- | ------------------------------------------------ | ----------------------------- | ------------------------------ |
-| **1. Creați scheletul** | Generați codul sursă al aplicației | `npx create-twenty-app` | Un proiect TypeScript pe disc |
-| **2. Rulați un server** | Porniți un server Twenty cu care să sincronizați | Docker + `yarn twenty server` | O instanță Twenty care rulează |
-| **3. Sincronizați** | Sincronizați în timp real codul cu serverul | `yarn twenty dev` | Modificările apar în UI |
+| Fază | Ce faceți | Instrument | Rezultat |
+| ----------------------- | ------------------------------------------------ | ----------------------------------- | ------------------------------ |
+| **1. Creați scheletul** | Generați codul sursă al aplicației | `npx create-twenty-app` | Un proiect TypeScript pe disc |
+| **2. Rulați un server** | Porniți un server Twenty cu care să sincronizați | Docker + `yarn twenty docker:start` | O instanță Twenty care rulează |
+| **3. Sincronizați** | Sincronizați în timp real codul cu serverul | `yarn twenty dev` | Modificările apar în UI |
---
@@ -28,7 +28,7 @@ Creați o nouă aplicație din șablon:
npx create-twenty-app@latest my-twenty-app
```
-Vi se va cere un nume și o descriere — apăsați **Enter** pentru valorile implicite. Aceasta generează un proiect TypeScript în `my-twenty-app/` cu un fișier inițial `application-config.ts`, un rol implicit, un flux de lucru CI și un test de integrare.
+Generatorul este neinteractiv: numele directorului devine numele aplicației. Transmite `--display-name` și `--description` pentru a personaliza metadatele generate (le poți edita și mai târziu în `src/constants/universal-identifiers.ts`). Aceasta generează un proiect TypeScript în `my-twenty-app/` cu un fișier inițial `application-config.ts`, un rol implicit, fluxuri de lucru CI/CD și un test de integrare.
**După această fază:** aveți codul sursă al aplicației pe mașina dvs. Încă nu rulează — aceasta este Faza 2.
@@ -38,28 +38,14 @@ Vi se va cere un nume și o descriere — apăsați **Enter** pentru valorile im
Aplicația are nevoie de un server Twenty cu care să se sincronizeze. Serverul este o instanță Twenty completă — UI, API GraphQL, PostgreSQL — care rulează local în Docker. Codul local încarcă definițiile pe acel server, făcându-le să apară în UI.
-Generatorul de schelet vă propune să pornească unul pentru dvs.:
+Generatorul de proiecte pornește unul pentru tine: cu Docker rulând, descarcă imaginea `twentycrm/twenty-app-dev`, o pornește pe portul `2020` și autentifică CLI-ul față de spațiul de lucru demo preconfigurat (`tim@apple.dev`) — fără a fi necesară autentificarea.
-> **Doriți să configurați o instanță Twenty locală?**
-
-* **Yes (recomandat)** — descarcă imaginea Docker `twentycrm/twenty-app-dev` și o pornește pe portul `2020`. Asigurați-vă mai întâi că Docker rulează.
-* **No** — alegeți această opțiune dacă aveți deja un server Twenty la care doriți să vă conectați. Îl puteți conecta ulterior cu `yarn twenty remote:add`.
-
-
-

-
-
-După ce serverul pornește, se deschide un browser pentru autentificare. Folosiți contul demo preconfigurat:
-
-* **E-mail:** `tim@apple.dev`
-* **Parolă:** `tim@apple.dev`
+Pentru a te conecta în schimb la un server Twenty existent, treci argumentul `--url \`. Serverele la distanță se autentifică prin OAuth: se deschide un browser astfel încât să te poți autentifica și să dai clic pe **Authorize**, ceea ce oferă CLI-ului acces la spațiul tău de lucru. (Poți opta pentru OAuth și local cu `--authentication-method oauth` — autentifică-te cu `tim@apple.dev` / `tim@apple.dev`.)
-Faceți clic pe **Authorize** pe ecranul următor — aceasta oferă CLI-ului acces la spațiul dvs. de lucru.
-
@@ -117,27 +103,31 @@ Faceți clic pe **View installed app** pentru a vedea instalarea în spațiul de
### Sincronizare unică pentru CI și scripturi
-Adăugați `--once` pentru a rula un singur build + sync și a ieși — același flux, fără watcher:
+Folosește `plan` și `apply` pentru a rula același pipeline o singură dată, fără watcher:
```bash filename="Terminal"
-yarn twenty dev --once
+yarn twenty plan # preview the metadata changes without applying them
+yarn twenty apply # show the plan, then apply it
```
-| Comandă | Comportament | Când se folosește |
-| ---------------------------------- | ------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------- |
-| `yarn twenty dev` | Monitorizează și resincronizează la fiecare modificare. Rulează până când îl opriți. | Dezvoltare locală interactivă. |
-| `yarn twenty dev --once` | Un singur build + sync, iese cu `0` la succes, `1` la eșec. | CI, hook-uri pre-commit, agenți AI, fluxuri de lucru scriptate. |
-| `yarn twenty dev --once --dry-run` | Construiește și afișează modificările de metadate **fără a le aplica**. | Inspectarea modificărilor pe care le-ar face o sincronizare înainte de a le confirma. |
+| Comandă | Comportament | Când se folosește |
+| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- |
+| `yarn twenty dev` | Monitorizează și resincronizează la fiecare modificare. Rulează până când îl opriți. | Dezvoltare locală interactivă. |
+| `yarn twenty apply` | Un singur build + sync, iese cu `0` la succes, `1` la eșec. Solicită confirmare pentru modificările distructive (treci argumentul `--force` pentru a sări peste aceasta). | CI, hook-uri pre-commit, agenți AI, fluxuri de lucru scriptate. |
+| `yarn twenty plan` | Construiește și afișează modificările de metadate **fără a le aplica**. | Inspectarea modificărilor pe care le-ar face o sincronizare înainte de a le confirma. |
-Ambele moduri necesită o conexiune la distanță autentificată. Vezi [Sincronizare și recuperare](/l/ro/developers/extend/apps/operations/sync-and-recovery#previewing-changes-dry-run) pentru mai multe detalii despre `--dry-run`.
+Toate modurile necesită o conexiune la distanță autentificată. Vezi [Sincronizare și recuperare](/l/ro/developers/extend/apps/operations/sync-and-recovery#previewing-changes-plan) pentru mai multe detalii despre `plan`.
+
+
+`yarn twenty dev --once` și `yarn twenty dev --once --dry-run` sunt aliasuri depreciate pentru `yarn twenty apply` și `yarn twenty plan`.
+
### Opțiuni pentru modul de dezvoltare
| Opțiune | Descriere |
| ------------------------------------- | ------------------------------------------------------------------------------------------------ |
-| `--once` | Construiește și sincronizează o singură dată, apoi iese. |
-| `--dry-run` | Cu `--once`, poți previzualiza modificările de metadate fără a le aplica. Nu scrie nimic. |
-| `--debounceMs \` | Setează întârzierea de debounce pentru modificarea fișierului în milisecunde (implicit: `2000`). |
+| `--force` | Aplică modificările distructive (ștergeri) fără confirmare. |
+| `--debounceMs \` | Setează întârzierea de debounce pentru modificarea fișierului în milisecunde (implicit: `1000`). |
| `--verbose` / `--debug` | Afișează jurnale detaliate de construire, cereri de sincronizare și urme ale erorilor. |
## Ce puteți construi
diff --git a/packages/twenty-docs/l/ro/developers/extend/apps/getting-started/scaffolding.mdx b/packages/twenty-docs/l/ro/developers/extend/apps/getting-started/scaffolding.mdx
index 3175524ac1..20d14d06fb 100644
--- a/packages/twenty-docs/l/ro/developers/extend/apps/getting-started/scaffolding.mdx
+++ b/packages/twenty-docs/l/ro/developers/extend/apps/getting-started/scaffolding.mdx
@@ -22,18 +22,22 @@ yarn twenty dev:add frontComponent
## Tipuri de entități disponibile
-| Tipul entității | Comandă | Fișier generat |
-| ---------------------------- | ---------------------------------------- | ------------------------------------------------------- |
-| Obiect | `yarn twenty dev:add object` | `src/objects/\.ts` |
-| Câmp | `yarn twenty dev:add field` | `src/fields/\.ts` |
-| Funcție logică | `yarn twenty dev:add logicFunction` | `src/logic-functions/\.ts` |
-| Componentă frontend | `yarn twenty dev:add frontComponent` | `src/front-components/\.tsx` |
-| Rol | `yarn twenty dev:add role` | `src/roles/\.ts` |
-| Abilitate | `yarn twenty dev:add skill` | `src/skills/\.ts` |
-| Agent | `yarn twenty dev:add agent` | `src/agents/\.ts` |
-| Vizualizare | `yarn twenty dev:add view` | `src/views/\.ts` |
-| Element de meniu de navigare | `yarn twenty dev:add navigationMenuItem` | `src/navigation-menu-items/\.ts` |
-| Machetă de pagină | `yarn twenty dev:add pageLayout` | `src/page-layouts/\.ts` |
+| Tipul entității | Comandă | Fișier generat |
+| ----------------------------- | ---------------------------------------- | ------------------------------------------------------- |
+| Obiect | `yarn twenty dev:add object` | `src/objects/\.ts` |
+| Câmp | `yarn twenty dev:add field` | `src/fields/\.ts` |
+| Funcție logică | `yarn twenty dev:add logicFunction` | `src/logic-functions/\.ts` |
+| Componentă frontend | `yarn twenty dev:add frontComponent` | `src/front-components/\.tsx` |
+| Rol | `yarn twenty dev:add role` | `src/roles/\.ts` |
+| Abilitate | `yarn twenty dev:add skill` | `src/skills/\.ts` |
+| Agent | `yarn twenty dev:add agent` | `src/agents/\.ts` |
+| Vizualizare | `yarn twenty dev:add view` | `src/views/\.ts` |
+| Element de meniu de navigare | `yarn twenty dev:add navigationMenuItem` | `src/navigation-menu-items/\.ts` |
+| Machetă de pagină | `yarn twenty dev:add pageLayout` | `src/page-layouts/\.ts` |
+| Fila "Aspect pagină" | `yarn twenty dev:add pageLayoutTab` | `src/page-layout-tabs/\.ts` |
+| Element din meniul de comenzi | `yarn twenty dev:add commandMenuItem` | `src/command-menu-items/\.ts` |
+| Câmpul vizualizării | `yarn twenty dev:add viewField` | `src/view-fields/\.ts` |
+| Furnizor de conexiune | `yarn twenty dev:add connectionProvider` | `src/connection-providers/\.ts` |
## Ce generează scaffolder-ul
diff --git a/packages/twenty-docs/l/ro/developers/extend/apps/getting-started/troubleshooting.mdx b/packages/twenty-docs/l/ro/developers/extend/apps/getting-started/troubleshooting.mdx
index dbcdf81378..ba79e6e3a1 100644
--- a/packages/twenty-docs/l/ro/developers/extend/apps/getting-started/troubleshooting.mdx
+++ b/packages/twenty-docs/l/ro/developers/extend/apps/getting-started/troubleshooting.mdx
@@ -5,10 +5,10 @@ icon: wrench
---
* **Erori Docker** — Asigurați-vă că Docker Desktop (sau daemonul) rulează înainte de `yarn twenty docker:start`. Mesajul de eroare va afișa comanda corectă de pornire pentru sistemul dvs. de operare.
-* **Versiune Node greșită** — Aveți nevoie de 24+. Verificați cu `node -v`.
+* **Versiune Node greșită** — Este nevoie de 24.5+ (`engines.node: ^24.5.0`). Verificați cu `node -v`.
* **Lipsește Yarn 4** — Rulați `corepack enable`.
* **Dependențe nefuncționale** — `rm -rf node_modules && yarn install`.
* **Erori ale `twenty-sdk` după actualizarea la v2.8.0** — A fost mutat din `dependencies` în `devDependencies` în v2.8.0. Vezi [Structura proiectului → Dependințe](/l/ro/developers/extend/apps/getting-started/project-structure#dependencies).
-* **`twenty build` afișează un avertisment despre `twenty-client-sdk` aflat în `dependencies`** — Este furnizat în timpul execuției de către Twenty, așa că ar trebui mutat în `devDependencies` alături de `twenty-sdk`. Vezi [Structura proiectului → Dependințe](/l/ro/developers/extend/apps/getting-started/project-structure#dependencies).
+* **`twenty dev:build` afișează un avertisment despre `twenty-client-sdk` aflat în `dependencies`** — Este furnizat în timpul execuției de către Twenty, așa că ar trebui mutat în `devDependencies` alături de `twenty-sdk`. Vezi [Structura proiectului → Dependințe](/l/ro/developers/extend/apps/getting-started/project-structure#dependencies).
Blocat? Întrebați pe [Discordul Twenty](https://discord.com/channels/1130383047699738754/1130386664812982322).
diff --git a/packages/twenty-docs/l/ro/developers/extend/apps/layout/command-menu-items.mdx b/packages/twenty-docs/l/ro/developers/extend/apps/layout/command-menu-items.mdx
index c2d888dbfd..362b8419f1 100644
--- a/packages/twenty-docs/l/ro/developers/extend/apps/layout/command-menu-items.mdx
+++ b/packages/twenty-docs/l/ro/developers/extend/apps/layout/command-menu-items.mdx
@@ -13,7 +13,6 @@ export default defineCommandMenuItem({
universalIdentifier: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
label: 'Open Dashboard',
shortLabel: 'Dashboard',
- icon: 'IconLayoutDashboard',
isPinned: true,
availabilityType: 'GLOBAL',
frontComponentUniversalIdentifier: '74c526eb-cb68-4cf7-b05c-0dd8c288d948',
@@ -22,51 +21,23 @@ export default defineCommandMenuItem({
## Câmpuri de configurare
-| Câmp | Obligatoriu | Descriere |
-| --------------------------------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
-| `universalIdentifier` | Da | ID unic stabil pentru comandă |
-| `label` | Da | Etichetă completă afișată în meniul de comenzi (Cmd+K) |
-| `frontComponentUniversalIdentifier` | Da | `universalIdentifier` al componentei front-end pe care această comandă o deschide |
-| `shortLabel` | Nu | Etichetă mai scurtă afișată pe butonul de acțiune rapidă fixat |
-| `icon` | Nu | Numele pictogramei afișat lângă etichetă (de ex. `'IconBolt'`, `'IconSend'`) |
-| `isPinned` | Nu | Când este `true`, afișează comanda ca buton de acțiune rapidă în colțul din dreapta sus al paginii |
-| `availabilityType` | Nu | Controlează unde apare comanda: `'GLOBAL'` (mereu disponibilă), `'RECORD_SELECTION'` (doar când sunt selectate înregistrări) sau `'FALLBACK'` (afișată când nicio altă comandă nu se potrivește) |
-| `availabilityObjectUniversalIdentifier` | Nu | Restricționați comanda la paginile unui anumit tip de obiect (de ex., doar pe înregistrările Company) |
-| `conditionalAvailabilityExpression` | Nu | O expresie booleană care controlează dinamic vizibilitatea (vezi mai jos) |
+| Câmp | Obligatoriu | Descriere |
+| --------------------------------------- | ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `universalIdentifier` | Da | ID unic stabil pentru comandă |
+| `label` | Da | Etichetă completă afișată în meniul de comenzi (Cmd+K) |
+| `frontComponentUniversalIdentifier` | Da | `universalIdentifier` al componentei front-end pe care această comandă o deschide |
+| `shortLabel` | Nu | Etichetă mai scurtă afișată pe butonul de acțiune rapidă fixat |
+| `icon` | Nu | **Învechit** — ignorat în favoarea pictogramei aplicației; build‑ul emite un avertisment dacă este setat |
+| `isPinned` | Nu | Când este `true`, afișează comanda ca buton de acțiune rapidă în colțul din dreapta sus al paginii |
+| `availabilityType` | Nu | Controlează unde apare comanda: `'GLOBAL'` (mereu disponibilă), `'GLOBAL_OBJECT_CONTEXT'` (doar în paginile cu context de obiect — pagini de index și de înregistrare), `'RECORD_SELECTION'` (doar când sunt selectate înregistrări) sau `'FALLBACK'` (afișată când nicio altă comandă nu se potrivește) |
+| `availabilityObjectUniversalIdentifier` | Nu | Restricționați comanda la paginile unui anumit tip de obiect (de ex., doar pe înregistrările Company) |
+| `conditionalAvailabilityExpression` | Nu | O expresie booleană care controlează dinamic vizibilitatea (vezi mai jos) |
## Comenzi headless
Un element din meniul de comenzi asociat cu un [headless front component](/l/ro/developers/extend/apps/layout/front-components#headless-vs-non-headless) este modalitatea standard de a oferi o acțiune cu un singur clic — de a rula cod, de a naviga sau de a confirma și executa. Pagina Front Components acoperă [SDK Command components](/l/ro/developers/extend/apps/layout/front-components#sdk-command-components) (`Command`, `CommandLink`, `CommandModal`, `CommandOpenSidePanelPage`) care gestionează modelul acțiune-și-demontare.
-Un flux tipic:
-
-```tsx src/front-components/run-action.tsx
-import { defineFrontComponent } from 'twenty-sdk/define';
-import { Command } from 'twenty-sdk/command';
-import { CoreApiClient } from 'twenty-sdk/clients';
-
-const RunAction = () => {
- const execute = async () => {
- const client = new CoreApiClient();
- await client.mutation({
- createTask: {
- __args: { data: { title: 'Created by my app' } },
- id: true,
- },
- });
- };
-
- return