diff --git a/packages/twenty-docs/l/ar/developers/extend/apps/config/install-hooks.mdx b/packages/twenty-docs/l/ar/developers/extend/apps/config/install-hooks.mdx index 58a76268bb..fc30682ee0 100644 --- a/packages/twenty-docs/l/ar/developers/extend/apps/config/install-hooks.mdx +++ b/packages/twenty-docs/l/ar/developers/extend/apps/config/install-hooks.mdx @@ -4,7 +4,7 @@ description: شغّل منطقًا قبل التثبيت أو بعده — لت icon: wrench --- -خطافات التثبيت هي دوال منطقية خاصة تعمل أثناء دورة حياة التثبيت أو الترقية. تستخدم نفس وقت تشغيل المعالج مثل [دوال المنطق](/l/ar/developers/extend/apps/logic/logic-functions) العادية وتتلقى `InstallPayload`، ولكن يتم التصريح عنها بدوال تعريف خاصة بها — `definePostInstallLogicFunction()` و`definePreInstallLogicFunction()` — وتعمل خارج نموذج المشغّل المعتاد (HTTP، وcron، وأحداث قاعدة البيانات). +خطافات التثبيت هي دوال منطقية خاصة تعمل أثناء دورة حياة التثبيت أو الترقية. تشارك نفس وقت تشغيل المعالج مثل [دوال المنطق](/l/ar/developers/extend/apps/logic/logic-functions) العادية وتتلقى `InstallPayload` (`{ previousVersion?: string; newVersion: string }` — تكون `previousVersion` بقيمة `undefined` في التثبيت الجديد)، ولكن يتم التصريح عنها بدوال تعريف خاصة بها وتعمل خارج نموذج المشغّل المعتاد (HTTP، وcron، وأحداث قاعدة البيانات). يمكن لكل تطبيق تعريف دالة واحدة على الأكثر لما قبل التثبيت ودالة واحدة على الأكثر لما بعد التثبيت. سيُنتِج إنشاء ملف البيان خطأً إذا تم اكتشاف أكثر من واحدة من أيٍّ منهما. @@ -19,111 +19,59 @@ icon: wrench └─────────────────────────────────────────────────────────────┘ ``` - - +## لمحة سريعة -تعمل دالة ما بعد التثبيت تلقائيًا بمجرد انتهاء تثبيت تطبيقك على مساحة عمل. ينفّذه الخادم **بعد** مزامنة البيانات الوصفية للتطبيق وإنشاء عميل SDK، بحيث تكون مساحة العمل جاهزة تمامًا للاستخدام ويكون المخطط الجديد مطبَّقًا. تشمل حالات الاستخدام النموذجية تهيئة البيانات الافتراضية، وإنشاء السجلات الأولية، وتكوين إعدادات مساحة العمل، أو توفير الموارد على خدمات جهات خارجية. +| | `definePreInstallLogicFunction` | `definePostInstallLogicFunction` | +| ------------------ | --------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ | +| عمليات التشغيل | قبل ترحيل البيانات الوصفية — لا يزال المخطط والبيانات **السابقة** سليمين | بعد الترحيل وإنشاء الـ SDK — أصبح المخطط **الجديد** في مكانه | +| التنفيذ | دائمًا متزامن؛ يحجب عملية التثبيت | غير متزامن بشكل افتراضي (يُوضَع في قائمة الانتظار، 3 محاولات إعادة)؛ تفعيل التزامن اختياري عبر `shouldRunSynchronously: true` | +| عند الفشل | يتم **إحباط** التثبيت قبل أي تغيير في المخطط | غير متزامن: تُعاد المحاولة حتى 3 مرات. متزامن: يتلقى المستدعي `POST_INSTALL_ERROR` (لن يتم التراجع عن تغييرات المخطط **not**). | +| الاستخدام النموذجي | نسخ احتياطي للبيانات أو إصلاح بيانات قد يفقدها الترحيل؛ رفض ترقية خطِرة عبر الرمي | بذر بيانات افتراضية، تهيئة مساحة العمل، تسجيل موارد خارجية | -```ts src/logic-functions/post-install.ts -import { definePostInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define'; +**قاعدة عامة:** اجعل الافتراضي هو post-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` للتفرع حسب مسار الترقية. +* **أهمية اللاّتغيّر (Idempotency)**: قد يُعاد تشغيل post-install غير المتزامن، وأيٌّ من الخطافين يُعاد تشغيله عند الترقيات عندما يكون `shouldRunOnVersionUpgrade` مفعّلًا. +* يتم حقن بيئة دوال المنطق المعتادة (`APPLICATION_ID`، و`APP_ACCESS_TOKEN`، و`API_URL`)، لذا يمكنك استدعاء Twenty API باستخدام رمز التطبيق الخاص بك. +* يُربَط الخطّاف تلقائيًا بملف بيان التطبيق وقت الإنشاء (`preInstallLogicFunction` / `postInstallLogicFunction`) — لا حاجة للإشارة إليه في [`defineApplication()`](/l/ar/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` ويعمل بشكل غير متزامن داخل عامل عمل. يعود ردّ التثبيت بمجرد وضع المهمة في الطابور، لذا فإن معالجًا بطيئًا أو متعطلًا لا يحجب المستدعي. سيُجرِّب العامل إعادة المحاولة حتى ثلاث مرات. **استخدم هذا للمهام طويلة التشغيل** — بَذر مجموعات بيانات كبيرة، استدعاء واجهات برمجة تطبيقات خارجية بطيئة، تهيئة موارد خارجية، أو أي شيء قد يتجاوز نافذة استجابة HTTP المعقولة. - * `shouldRunSynchronously: true` — يُنفّذ الخطاف **ضمن تدفّق التثبيت مباشرةً** (نفس المنفِّذ كما قبل التثبيت). يَحجُب طلب التثبيت حتى ينتهي المعالج، وإذا رمى استثناءً، سيتلقى مستدعي التثبيت `POST_INSTALL_ERROR`. لا توجد محاولات إعادة تلقائية. **استخدم هذا للمهام السريعة التي يجب إكمالها قبل الاستجابة** — مثل إظهار خطأ تحقق للمستخدم، أو إعداد سريع سيعتمد عليه العميل مباشرةً بعد عودة نداء التثبيت. ضع في اعتبارك أن ترحيل البيانات الوصفية يكون قد طُبِّق بالفعل عند تشغيل ما بعد التثبيت، لذلك فإن فشل الوضع المتزامن **لا** يعيد التغييرات على المخطط إلى الوراء — بل يكتفي بإبراز الخطأ. -* تأكّد من أن معالجك قابل للتنفيذ المتكرر دون آثار جانبية. في الوضع غير المتزامن قد تُعيد قائمة الانتظار المحاولة حتى ثلاث مرات؛ وفي أي من الوضعين قد يعمل الخطاف مجددًا أثناء الترقيات عند ضبط `shouldRunOnVersionUpgrade: true`. -* متغيرات البيئة `APPLICATION_ID` و`APP_ACCESS_TOKEN` و`API_URL` متاحة داخل المعالج (كما في أي دالة منطق أخرى)، لذا يمكنك استدعاء واجهة Twenty API باستخدام رمز وصول للتطبيق مقيّد بنطاق تطبيقك. -* يُسمح بدالة ما بعد التثبيت واحدة فقط لكل تطبيق. سيُنتج إنشاء ملف البيان خطأً إذا تم اكتشاف أكثر من واحدة. -* تُرفَق خصائص الدالة `universalIdentifier` و`shouldRunOnVersionUpgrade` و`shouldRunSynchronously` تلقائيًا ببيان التطبيق ضمن الحقل `postInstallLogicFunction` أثناء عملية البناء — ولا تحتاج إلى الإشارة إليها في [`defineApplication()`](/l/ar/developers/extend/apps/config/application). -* تم تعيين مهلة افتراضية إلى 300 ثانية (5 دقائق) للسماح بمهام الإعداد الأطول مثل تهيئة البيانات. -* **لا يُنفَّذ في وضع التطوير**: عند تسجيل تطبيق محليًا (عبر `yarn twenty dev`)، يتجاوز الخادم تدفّق التثبيت بالكامل ويُزامن الملفات مباشرةً عبر مراقِب CLI — لذا لن يعمل ما بعد التثبيت في وضع التطوير مطلقًا، بغضّ النظر عن `shouldRunSynchronously`. استخدم `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`). قبل التنفيذ، يُشغِّل الخادم مزامنة "pared-down sync" ذات طابع إضافي فقط تقوم بتسجيل دالة ما قبل التثبيت للإصدار **الجديد** في البيانات الوصفية لمساحة العمل — دون لمس أي شيء آخر — ثم يُنفّذها. لأن هذه المزامنة «إضافية فقط»، تبقى كائنات وحقول وبيانات الإصدار السابق سليمة عند تشغيل معالجك: يمكنك قراءة حالة ما قبل الترحيل ونسخها احتياطيًا بأمان. -* **نموذج التنفيذ**: يُنفَّذ ما قبل التثبيت **بشكل متزامن** و**يحجب عملية التثبيت**. إذا رمى المعالج استثناءً، تُلغى عملية التثبيت قبل تطبيق أي تغييرات على المخطط — وتبقى مساحة العمل على الإصدار السابق بحالة متّسقة. هذا مقصود: ما قبل التثبيت هو فرصتك الأخيرة لرفض ترقية تنطوي على مخاطر. -* كما هو الحال مع ما بعد التثبيت، يُسمح بدالة ما قبل التثبيت واحدة فقط لكل تطبيق. تُربَط تلقائيًا ببيان التطبيق تحت `preInstallLogicFunction` أثناء عملية البناء. -* **لا يُنفَّذ في وضع التطوير**: كما في ما بعد التثبيت — يتم تجاوز تدفّق التثبيت بالكامل للتطبيقات المسجّلة محليًا، لذا لن يعمل ما قبل التثبيت مطلقًا عند `yarn twenty dev`. استخدم `yarn twenty dev:function:exec --preInstall` لتشغيله يدويًا. + + - - - -كلا الخطافين جزء من تدفّق التثبيت نفسه ويتلقّيان نفس `InstallPayload`. الاختلاف يكمن في **موعد** تشغيلهما نسبةً إلى ترحيل البيانات الوصفية لمساحة العمل، وهذا يغيّر البيانات التي يمكنهما التعامل معها بأمان. - -ما قبل التثبيت دائمًا **متزامن** (يحجب التثبيت ويمكنه إحباطه). ما بعد التثبيت **غير متزامن افتراضيًا** — يُدرج على عامل مع محاولات إعادة تلقائية — لكن يمكن التبديل إلى تنفيذ متزامن عبر `shouldRunSynchronously: true`. راجع الأكورديون `definePostInstallLogicFunction` أعلاه لمعرفة متى تستخدم كل وضع. - -**استخدم `post-install` لأي شيء يتطلّب وجود المخطط الجديد.** وهذا هو السيناريو الشائع: - -* بَذر بيانات افتراضية (إنشاء سجلات أولية وعروض افتراضية ومحتوى تجريبي) للكائنات والحقول المضافة حديثًا. -* تسجيل خطافات الويب مع خدمات أطراف ثالثة بعد أن حصل التطبيق على بيانات الاعتماد الخاصة به. -* استدعاء واجهة برمجة التطبيقات الخاصة بك لإكمال إعداد يعتمد على البيانات الوصفية المتزامنة. -* منطق قابل للتنفيذ المتكرر دون آثار جانبية لتحقيق "تأكّد من وجود هذا" والذي ينبغي مواءمة الحالة في كل ترقية — بالاقتران مع `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` وتحتاج أولًا إلى حذف الصفوف ذات القيم الفارغة أو إصلاحها. -* **التحقق من التوافق ورفض الترقية إذا تعذّر ترحيل البيانات الحالية بسلاسة** — ارمِ من داخل المعالج وسيُلغى التثبيت دون تطبيق أي تغييرات. هذا أكثر أمانًا من اكتشاف عدم التوافق في منتصف الترحيل. -* **إعادة تسمية البيانات أو إعادة تعيين مفاتيحها** قبل تغيير في المخطط قد يؤدي إلى فقدان الارتباط. +* `false` *(الإعداد الافتراضي)* — يُوضَع في قائمة انتظار الرسائل (`retryLimit: 3`) ويُشغِّله عامل. تعود استجابة التثبيت بمجرد وضع المهمة في قائمة الانتظار. **يُستخدم للأعمال طويلة الأمد** — بذر مجموعات بيانات كبيرة، وواجهات برمجة تطبيقات بطيئة لأطراف ثالثة. +* `true` — يُنفَّذ مضمَّنًا أثناء تدفق التثبيت. يحجب طلب التثبيت حتى ينتهي المعالج؛ يظهر الخطأ الذي يتم رميه كـ `POST_INSTALL_ERROR` للمستدعي (بدون محاولات إعادة). **يُستخدم للأعمال السريعة التي يجب إتمامها قبل الاستجابة.** تم تطبيق الترحيل بالفعل في هذه المرحلة، لذا لا يؤدي الفشل إلى التراجع عن تغييرات المخطط — بل يُظهِر الخطأ فقط. -مثال — أرشف السجلات قبل ترحيل هدّام: + + + +يعمل قبل ترحيل البيانات الوصفية، مقابل المخطط **السابق** — المكان المناسب لنسخ البيانات احتياطيًا التي قد يفقدها الترحيل، أو لرفض ترقية خطِرة. قبل التنفيذ، يُشغِّل الخادم مزامنة ذات طابع إضافي فقط "pared-down sync" تُسجِّل دالة ما قبل التثبيت للإصدار الجديد في إصدارها الجديد؛ كل ما عدا ذلك — كائنات الإصدار السابق وحقوله وبياناته — يبقى دون لمس عندما يعمل المعالج. + +ما قبل التثبيت دائمًا **متزامن** ويحجب عملية التثبيت. إذا رمى المعالج استثناءً، تُلغى عملية التثبيت قبل أي تغيير في المخطط — وتبقى مساحة العمل على الإصدار السابق بحالة متّسقة. هذا مقصود: ما قبل التثبيت هو فرصتك الأخيرة لرفض ترقية تنطوي على مخاطر. + +مثال — نسخ قيم حقل قديم قبل أن يُسقِطه الترحيل: ```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**. الجأ إلى ما قبل التثبيت فقط عندما يكون الترحيل نفسه هدّامًا وتحتاج إلى التقاط الحالة السابقة قبل أن تزول. - - diff --git a/packages/twenty-docs/l/ar/developers/extend/apps/data/objects.mdx b/packages/twenty-docs/l/ar/developers/extend/apps/data/objects.mdx index 48ad8a5709..071428232a 100644 --- a/packages/twenty-docs/l/ar/developers/extend/apps/data/objects.mdx +++ b/packages/twenty-docs/l/ar/developers/extend/apps/data/objects.mdx @@ -86,6 +86,22 @@ export default defineObject({ **تُضاف الحقول الأساسية تلقائيًا.** عند تعريف كائن مخصص، ينشئ Twenty حقولًا قياسية مثل `id` و`name` و`createdAt` و`updatedAt` و`createdBy` و`updatedBy` و`deletedAt` من أجلك. لا تحتاج إلى تعريفها في مصفوفة `fields` — أضف فقط حقولك المخصصة. يمكنك تجاوز حقلًا افتراضيًا بتعريف حقل يحمل الاسم نفسه، لكن هذا نادرًا ما يكون فكرة جيدة. +## أنواع الحقول + +مجموعة القيم الكاملة لـ`FieldType`، والمصدَّرة من `twenty-sdk/define`: + +| الفئة | الأنواع | +| -------------------- | ------------------------------------------------------------------------------------------------------------------------- | +| نص | `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` (انظر [العلاقات](/l/ar/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/ar/developers/extend/apps/getting-started/project-structure.mdx b/packages/twenty-docs/l/ar/developers/extend/apps/getting-started/project-structure.mdx index 64f4972a9f..cea501a88d 100644 --- a/packages/twenty-docs/l/ar/developers/extend/apps/getting-started/project-structure.mdx +++ b/packages/twenty-docs/l/ar/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` | إرشادات لوكلاء برمجة الذكاء الاصطناعي الذين يعملون على التطبيق. | **تنظيم الملفات متروك لك.** المجلدات المذكورة أعلاه هي أعراف متَّبعة — يكتشف SDK الكيانات عبر تحليل AST على استدعاءات `export default defineEntity(...)` بغض النظر عن مكان وجود الملف. @@ -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 وأدوات البناء/إنشاء الهياكل (scaffolding). يعمل فقط أثناء التطوير ووقت البناء، ولا يتم استيراده أبدًا في وقت تشغيل تطبيقك المنشور. * يتم استيراد **`twenty-client-sdk`** بواسطة كود تطبيقك (`CoreApiClient`، `MetadataApiClient`، `RestApiClient`)؛ لكن Twenty توفّره في وقت التشغيل — حيث تحصل عليه دوال المنطق من طبقة SDK مُولَّدة، وتحصل عليه مكوّنات الواجهة من وحدات يتم تقديمها من الخادم. يُستخدَم الإصدار المثبّت لديك فقط لفحص الأنواع (typechecking) ولبناء النشر (deploy-time build)، لذا لا يلزم أبدًا أن يتم تضمينه في حزمة النشر. -الاحتفاظ بأي من الحزمتين ضمن `dependencies` يؤدي إلى سحبها داخل حزمة وقت تشغيل التطبيق المثبّت، حيث تكون عبئًا زائدًا بلا فائدة. يُطلق `twenty build` تحذيرًا عندما تكون أيٌّ منهما ما تزال مدرجة ضمن `dependencies`. +الاحتفاظ بأي من الحزمتين ضمن `dependencies` يؤدي إلى سحبها داخل حزمة وقت تشغيل التطبيق المثبّت، حيث تكون عبئًا زائدًا بلا فائدة. يُطلق `twenty dev:build` تحذيرًا عندما تكون أيٌّ منهما ما تزال مدرجة ضمن `dependencies`. أضِف تبعيات وقت التشغيل الخاصة بتطبيقك (المكتبات التي تستوردها دوال المنطق لديك فعلًا في وقت التشغيل) ضمن `dependencies` كالمعتاد. diff --git a/packages/twenty-docs/l/ar/developers/extend/apps/getting-started/quick-start.mdx b/packages/twenty-docs/l/ar/developers/extend/apps/getting-started/quick-start.mdx index 3d28211656..397c3a06c7 100644 --- a/packages/twenty-docs/l/ar/developers/extend/apps/getting-started/quick-start.mdx +++ b/packages/twenty-docs/l/ar/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** — يأتي مع Node.js عبر Corepack. قم بتمكينه: `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` | تظهر تغييراتك في واجهة المستخدم | +| المرحلة | ماذا تفعل | الأداة | النتيجة | +| ------------------- | ---------------------------------- | ----------------------------------- | ------------------------------- | +| **1. تهيئة الهيكل** | توليد الشفرة المصدرية للتطبيق | `npx create-twenty-app` | مشروع TypeScript على القرص | +| **2. تشغيل خادم** | بدء تشغيل خادم Twenty للمزامنة معه | Docker + `yarn twenty docker:start` | مثيل Twenty قيد التشغيل | +| **3. مزامنة** | قم بمزامنة شفرتك مباشرةً مع الخادم | `yarn twenty dev` | تظهر تغييراتك في واجهة المستخدم | --- @@ -28,7 +28,7 @@ description: أنشئ أول تطبيق Twenty خلال دقائق. npx create-twenty-app@latest my-twenty-app ``` -ستتم مطالبتك باسم ووصف — اضغط **Enter** للقيم الافتراضية. يُنشئ هذا مشروع TypeScript في `my-twenty-app/` يتضمن ملف بداية `application-config.ts`، ودورًا افتراضيًا، وسير عمل CI، واختبار تكامل. +أداة التهيئة غير تفاعلية: يصبح اسم الدليل هو اسم التطبيق. مرِّر `--display-name` و`--description` لتخصيص البيانات الوصفية المُولَّدة (يمكنك أيضًا تعديلها لاحقًا في `src/constants/universal-identifiers.ts`). يُنشئ هذا مشروع TypeScript في `my-twenty-app/` يتضمّن ملف بداية `application-config.ts`، ودورًا افتراضيًا، وسير عمل CI/CD، واختبار تكامل. **بعد هذه المرحلة:** سيكون لديك الشفرة المصدرية لتطبيق على جهازك. ليس قيد التشغيل بعد — وهذه هي المرحلة 2. @@ -38,28 +38,14 @@ npx create-twenty-app@latest my-twenty-app يحتاج تطبيقك إلى خادم Twenty للمزامنة معه. الخادم هو مثيل Twenty كامل — واجهة مستخدم، واجهة برمجة تطبيقات GraphQL، PostgreSQL — يعمل محليًا داخل Docker. ترفع شفرتك المحلية تعريفاتها إلى ذلك الخادم، مما يجعلها تظهر في واجهة المستخدم. -تقترح أداة توليد الهيكل تشغيل خادم لك: +يقوم مُنشئ الهياكل بتشغيل مثيل لك: مع تشغيل Docker، يسحب صورة `twentycrm/twenty-app-dev`، ويبدأها على المنفذ `2020`، ويُجري مصادقة أداة CLI على مساحة العمل التجريبية المهيأة مسبقًا (`tim@apple.dev`) — دون الحاجة إلى تسجيل الدخول. -> **هل ترغب في إعداد مثيل محلي من Twenty؟** - -* **نعم (موصى به)** — ستسحب صورة Docker `twentycrm/twenty-app-dev` وتبدأ تشغيلها على المنفذ `2020`. تأكّد أولًا من أن Docker قيد التشغيل. -* **لا** — اختر هذا إذا كان لديك بالفعل خادم Twenty تريد الاتصال به. يمكنك ربطه لاحقًا باستخدام `yarn twenty remote:add`. - -
- هل يجب بدء المثيل المحلي؟ -
- -بمجرد أن يصبح الخادم جاهزًا، سيفتح المتصفح لإجراء تسجيل الدخول. استخدم حساب العرض التوضيحي المُجهَّز مسبقًا: - -* **البريد الإلكتروني:** `tim@apple.dev` -* **كلمة المرور:** `tim@apple.dev` +للاتصال بخادم Twenty موجود بدلاً من ذلك، مرِّر الخيار `--url \`. تُجري الخوادم البعيدة المصادقة باستخدام OAuth: يفتح المتصفح حتى تتمكن من تسجيل الدخول والنقر على **Authorize**، مما يمنح أداة CLI حق الوصول إلى مساحة عملك. (يمكنك أيضًا اختيار استخدام OAuth محليًا باستخدام `--authentication-method oauth` — سجِّل الدخول باستخدام `tim@apple.dev` / `tim@apple.dev`.)
شاشة تسجيل الدخول إلى Twenty
-انقر **Authorize** في الشاشة التالية — يمنح هذا واجهة سطر الأوامر CLI حق الوصول إلى مساحة العمل الخاصة بك. -
شاشة تفويض واجهة الأوامر (CLI) الخاصة بـ Twenty
@@ -117,28 +103,32 @@ yarn twenty dev ### مزامنة لمرة واحدة لـ CI والبرامج النصية -مرّر `--once` لتشغيل عملية بناء واحدة + مزامنة واحدة ثم الخروج — نفس خط الأنابيب، من دون مراقِب: +استخدم `plan` و`apply` لتشغيل نفس خط الأنابيب مرة واحدة، بدون أداة مراقبة (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 ``` -| أمر | السلوك | متى يُستخدم | -| ---------------------------------- | ------------------------------------------------------------------- | --------------------------------------------------------------------------- | -| `yarn twenty dev` | يراقب ويعيد المزامنة عند كل تغيير. يستمر في العمل حتى توقفه. | تطوير محلي تفاعلي. | -| `yarn twenty dev --once` | بناء واحد + مزامنة واحدة، يخرج برمز `0` عند النجاح، و`1` عند الفشل. | CI، وخطافات ما قبل الالتزام، ووكلاء الذكاء الاصطناعي، وسير عمل مكتوب بنصوص. | -| `yarn twenty dev --once --dry-run` | يبني تغييرات البيانات الوصفية ويطبعها **من دون تطبيقها**. | فحص ما الذي سيُغيِّره التزامن قبل تطبيقه. | +| أمر | السلوك | متى يُستخدم | +| ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------- | +| `yarn twenty dev` | يراقب ويعيد المزامنة عند كل تغيير. يستمر في العمل حتى توقفه. | تطوير محلي تفاعلي. | +| `yarn twenty apply` | بناء واحد + مزامنة واحدة، يخرج برمز `0` عند النجاح، و`1` عند الفشل. يطلب تأكيدًا عند إجراء تغييرات مدمِّرة (مرِّر `--force` لتجاوز ذلك). | CI، وخطافات ما قبل الالتزام، ووكلاء الذكاء الاصطناعي، وسير عمل مكتوب بنصوص. | +| `yarn twenty plan` | يبني تغييرات البيانات الوصفية ويطبعها **من دون تطبيقها**. | فحص ما الذي سيُغيِّره التزامن قبل تطبيقه. | -كلا الوضعين يحتاجان إلى جهة بعيدة موثَّقة. راجع قسم [المزامنة والاستعادة](/l/ar/developers/extend/apps/operations/sync-and-recovery#previewing-changes-dry-run) للحصول على المزيد من المعلومات حول `--dry-run`. +جميع الأوضاع تحتاج إلى جهة بعيدة موثَّقة. راجع قسم [المزامنة والاستعادة](/l/ar/developers/extend/apps/operations/sync-and-recovery#previewing-changes-plan) للحصول على مزيد من المعلومات حول `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/ar/developers/extend/apps/getting-started/scaffolding.mdx b/packages/twenty-docs/l/ar/developers/extend/apps/getting-started/scaffolding.mdx index 924a3971c2..25cdefbe47 100644 --- a/packages/twenty-docs/l/ar/developers/extend/apps/getting-started/scaffolding.mdx +++ b/packages/twenty-docs/l/ar/developers/extend/apps/getting-started/scaffolding.mdx @@ -22,18 +22,22 @@ yarn twenty dev:add frontComponent ## أنواع الكيانات المتاحة -| نوع الكيان | أمر | الملف المُولَّد | -| ------------------ | ---------------------------------------- | ------------------------------------------------------- | -| كائن | `yarn twenty dev:add object` | `src/objects/\.ts` | -| الحقل | `yarn twenty dev:add field` | `src/fields/\.ts` | -| دالة منطقية | `yarn twenty dev:add logicFunction` | `src/logic-functions/\.ts` | -| مكوّن أمامي | `yarn twenty dev:add frontComponent` | `src/front-components/\.tsx` | -| دور | `yarn twenty dev:add role` | `src/roles/\.ts` | -| مهارة | `yarn twenty dev:add skill` | `src/skills/\.ts` | -| وكيل | `yarn twenty dev:add agent` | `src/agents/\.ts` | -| عرض | `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 object` | `src/objects/\.ts` | +| الحقل | `yarn twenty dev:add field` | `src/fields/\.ts` | +| دالة منطقية | `yarn twenty dev:add logicFunction` | `src/logic-functions/\.ts` | +| مكوّن أمامي | `yarn twenty dev:add frontComponent` | `src/front-components/\.tsx` | +| دور | `yarn twenty dev:add role` | `src/roles/\.ts` | +| مهارة | `yarn twenty dev:add skill` | `src/skills/\.ts` | +| وكيل | `yarn twenty dev:add agent` | `src/agents/\.ts` | +| عرض | `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/ar/developers/extend/apps/getting-started/troubleshooting.mdx b/packages/twenty-docs/l/ar/developers/extend/apps/getting-started/troubleshooting.mdx index 0cc4bce849..efd4c54ff8 100644 --- a/packages/twenty-docs/l/ar/developers/extend/apps/getting-started/troubleshooting.mdx +++ b/packages/twenty-docs/l/ar/developers/extend/apps/getting-started/troubleshooting.mdx @@ -5,10 +5,10 @@ icon: wrench --- * **أخطاء Docker** — تأكّد من أن Docker Desktop (أو الـ daemon) قيد التشغيل قبل `yarn twenty docker:start`. ستعرض رسالة الخطأ أمر البدء المناسب لنظام التشغيل لديك. -* **إصدار 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** — تم نقله من `dependencies` إلى `devDependencies` في الإصدار v2.8.0. انظر إلى [بنية المشروع → التبعيات](/l/ar/developers/extend/apps/getting-started/project-structure#dependencies). -* **`twenty build` يُصدر تحذيرًا بشأن `twenty-client-sdk` تحت `dependencies`** — يتم توفيره في وقت التشغيل بواسطة Twenty، لذلك يجب نقله إلى `devDependencies` جنبًا إلى جنب مع `twenty-sdk`. انظر إلى [بنية المشروع → التبعيات](/l/ar/developers/extend/apps/getting-started/project-structure#dependencies). +* **`twenty dev:build` يُصدر تحذيرًا بشأن `twenty-client-sdk` تحت `dependencies`** — يتم توفيره في وقت التشغيل بواسطة Twenty، لذلك يجب نقله إلى `devDependencies` جنبًا إلى جنب مع `twenty-sdk`. انظر إلى [بنية المشروع → التبعيات](/l/ar/developers/extend/apps/getting-started/project-structure#dependencies). هل علِقت؟ اطلب المساعدة على [خادم Twenty على Discord](https://discord.com/channels/1130383047699738754/1130386664812982322). diff --git a/packages/twenty-docs/l/ar/developers/extend/apps/layout/command-menu-items.mdx b/packages/twenty-docs/l/ar/developers/extend/apps/layout/command-menu-items.mdx index 11460d8a93..977a4ee6c4 100644 --- a/packages/twenty-docs/l/ar/developers/extend/apps/layout/command-menu-items.mdx +++ b/packages/twenty-docs/l/ar/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` | نعم | معرّف فريد ثابت للأمر | -| `label` | نعم | التسمية الكاملة المعروضة في قائمة الأوامر (Cmd+K) | -| `frontComponentUniversalIdentifier` | نعم | قيمة `universalIdentifier` للمكوّن الأمامي الذي يفتحه هذا الأمر | -| `shortLabel` | لا | تسمية أقصر تُعرَض على زر الإجراء السريع المثبّت | -| `icon` | لا | اسم الأيقونة المعروض بجانب التسمية (مثل `'IconBolt'` و`'IconSend'`) | -| `isPinned` | لا | عند كونها `true`، يعرض الأمر كزر إجراء سريع في الزاوية العلوية اليمنى من الصفحة | -| `availabilityType` | لا | تتحكّم في مكان ظهور الأمر: `'GLOBAL'` (متاح دائمًا)، و`'RECORD_SELECTION'` (فقط عند تحديد سجلات)، أو `'FALLBACK'` (يُعرَض عند عدم تطابق أي أوامر أخرى) | -| `availabilityObjectUniversalIdentifier` | لا | تقييد الأمر بصفحات نوع كائن معيّن (مثل سجلات Company فقط) | -| `conditionalAvailabilityExpression` | لا | تعبير منطقي يتحكّم ديناميكيًا في الظهور (انظر أدناه) | +| الحقل | مطلوب | الوصف | +| --------------------------------------- | ----- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `universalIdentifier` | نعم | معرّف فريد ثابت للأمر | +| `label` | نعم | التسمية الكاملة المعروضة في قائمة الأوامر (Cmd+K) | +| `frontComponentUniversalIdentifier` | نعم | قيمة `universalIdentifier` للمكوّن الأمامي الذي يفتحه هذا الأمر | +| `shortLabel` | لا | تسمية أقصر تُعرَض على زر الإجراء السريع المثبّت | +| `icon` | لا | **مهمل** — يتم تجاهله لصالح أيقونة التطبيق؛ يُصدر البناء تحذيرًا إذا تم تعيينه | +| `isPinned` | لا | عند كونها `true`، يعرض الأمر كزر إجراء سريع في الزاوية العلوية اليمنى من الصفحة | +| `availabilityType` | لا | يتحكّم في مكان ظهور الأمر: `'GLOBAL'` (متاح دائمًا)، و`'GLOBAL_OBJECT_CONTEXT'` (فقط في الصفحات ذات سياق الكائن — صفحات الفهرس والسجل)، و`'RECORD_SELECTION'` (فقط عند تحديد سجلات)، أو `'FALLBACK'` (يُعرَض عند عدم تطابق أي أوامر أخرى) | +| `availabilityObjectUniversalIdentifier` | لا | تقييد الأمر بصفحات نوع كائن معيّن (مثل سجلات Company فقط) | +| `conditionalAvailabilityExpression` | لا | تعبير منطقي يتحكّم ديناميكيًا في الظهور (انظر أدناه) | ## أوامر بدون واجهة يُعَدّ عنصر قائمة الأوامر المقترن بـ[مكوّن واجهة أمامية بدون واجهة](/l/ar/developers/extend/apps/layout/front-components#headless-vs-non-headless) الطريقة القياسية لتوفير إجراء بنقرة واحدة — لتشغيل الشفرة أو التنقّل أو التأكيد ثم التنفيذ. تغطي صفحة مكوّنات الواجهة الأمامية [مكوّنات الأوامر في SDK](/l/ar/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/ar/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/ar/developers/extend/apps/layout/front-components.mdx b/packages/twenty-docs/l/ar/developers/extend/apps/layout/front-components.mdx index 748c492adb..907e87e5db 100644 --- a/packages/twenty-docs/l/ar/developers/extend/apps/layout/front-components.mdx +++ b/packages/twenty-docs/l/ar/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 مصممة للمكوّنات عديمة الرأس في الواجهة الأمامية. كل مكوّن ينفّذ إجراءً عند التركيب، ويتعامل مع الأخطاء بعرض إشعار Snackbar، ويزيل تركيب مكوّن الواجهة الأمامية تلقائيًا عند الانتهاء. -استوردها من `twenty-sdk/command`: +استوردها من `twenty-sdk/front-component`: * **`Command`** — يشغّل رد نداء غير متزامن عبر الخاصية `execute`. * **`CommandLink`** — ينتقل إلى مسار في التطبيق. الخصائص: `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`. للمتصلين الخارجيين، انسخ عنوان URL الدقيق من إعدادات **HTTP trigger** الخاصة بالدالة أو من علامة تبويب **Settings** في التطبيق. +> **على Twenty Cloud، يتم تقديم الدوال المنطقية المُفعَّلة عبر HTTP على نطاق مخصص لكل مساحة عمل** عند `https://\.withtwenty.com\` — وهذا بالضبط ما تُشير إليه قيمة `TWENTY_FUNCTIONS_URL`. للمتصلين الخارجيين، انسخ عنوان URL الدقيق من إعدادات **HTTP trigger** الخاصة بالدالة أو من علامة تبويب **Settings** في التطبيق. مسار الدالة القديم `/s/` **مهمَل (deprecated)** وسيتم **إيقاف تفعيله في 2026-07-24**. استخدم بدلًا من ذلك `TWENTY_FUNCTIONS_URL` (أعلاه)، ورحِّل أي عناوين URL ثابتة من نوع `/s/` قبل ذلك التاريخ. يبقى مسار `/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/ar/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/ar/developers/extend/apps/layout/navigation-menu-items.mdx b/packages/twenty-docs/l/ar/developers/extend/apps/layout/navigation-menu-items.mdx index 43fe180a16..0a4092a981 100644 --- a/packages/twenty-docs/l/ar/developers/extend/apps/layout/navigation-menu-items.mdx +++ b/packages/twenty-docs/l/ar/developers/extend/apps/layout/navigation-menu-items.mdx @@ -35,6 +35,8 @@ export default defineNavigationMenuItem({ * `position` يتحكّم في الترتيب ضمن الشريط الجانبي. +* يحتوي التعداد أيضًا على `NavigationMenuItemType.RECORD`، ويُستخدم داخليًا للمفضلات الخاصة بالسجلات التي ينشئها المستخدم — ولا يمكن استخدامه من بيان التطبيق (app manifest) لأنه لا يوجد حقل للإشارة إلى سجل). + * `icon` و`color` اختياريان ويخصّصان مظهر الإدخال. * `folderUniversalIdentifier` متاح أيضًا على أي عنصر لوضعه متداخلًا داخل عنصر أب من النوع `FOLDER`. diff --git a/packages/twenty-docs/l/ar/developers/extend/apps/layout/views.mdx b/packages/twenty-docs/l/ar/developers/extend/apps/layout/views.mdx index 5a0ae63a70..aa53ce820e 100644 --- a/packages/twenty-docs/l/ar/developers/extend/apps/layout/views.mdx +++ b/packages/twenty-docs/l/ar/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` | عروض التقويم: التخطيط وحقل التاريخ الذي يحدد موضع السجلات. | + +يتم تصدير جميع التعدادات أعلاه من `twenty-sdk/define`. + ## الفلاتر يمكن أن تأتي طريقة العرض مع عوامل تصفية مُطبَّقة مسبقًا. لكل عامل تصفية ثلاثة مكونات: **الحقل** الذي تُطبَّق عليه التصفية، و**المعامل** (كيفية المقارنة)، و**القيمة** (ما تتم المقارنة به). يجب أن تتطابق العناصر الثلاثة جميعًا — حيث سيتم رفض استخدام معامل لا ينطبق على نوع الحقل في وقت المزامنة. ```ts -import { ViewFilterOperand } from 'twenty-shared/types'; +import { ViewFilterOperand } from 'twenty-sdk/define'; filters: [ { diff --git a/packages/twenty-docs/l/ar/developers/extend/apps/logic/logic-functions.mdx b/packages/twenty-docs/l/ar/developers/extend/apps/logic/logic-functions.mdx index fcc3dfa2bb..1e7f4c6605 100644 --- a/packages/twenty-docs/l/ar/developers/extend/apps/logic/logic-functions.mdx +++ b/packages/twenty-docs/l/ar/developers/extend/apps/logic/logic-functions.mdx @@ -51,8 +51,12 @@ export default defineLogicFunction({ ``` أنواع المشغّلات المتاحة: -* **httpRoute**: يعرِض وظيفتك على مسار وطريقة HTTP **تحت نقطة النهاية `/s/`**: -> مثال: `path: '/post-card/create'` يمكن استدعاؤه عبر `https://your-twenty-server.com/s/post-card/create` +* **httpRoute**: يعرض الدالة الخاصة بك على مسار وطريقة HTTP في مساحة العمل الخاصة بك **URL الأساسي للوظائف** - القيمة العشرين حقن كـ `TWENTY_FUNCTIONS_URL` (على 20 Cloud, مجال مخصص لكل عمل: +> مثال: `path: '/post-card/create'` يمكن استدعاؤه عبر `https://your-workspace.withtwenty.com/post-card/create` + + +مسار البادئة القديم `/s/' (https://your-twenty-server.com/s/post-card/create`) **مهمل على 20 Cloud** وسيتم إبطاله على **2026-07-24**. يبقى متاحا للحالات التي تستضيف ذاتيا أو المحلية والتي لا تشكل نطاق وظائف معزولة - استخدم `TWENTY_FUNCTIONS_URL` عند تعيينه. والعودة إلى `\/s/\` خلاف ذلك. + لاستدعاء دالة منطقية يتم تشغيلها بواسطة مسار من مكون واجهة (بدون واجهة رسومية)، راجع قسم [استدعاء دالة منطقية](/l/ar/developers/extend/apps/layout/front-components#calling-a-logic-function). diff --git a/packages/twenty-docs/l/ar/developers/extend/apps/logic/overview.mdx b/packages/twenty-docs/l/ar/developers/extend/apps/logic/overview.mdx index 767cef1f05..eccf1927b0 100644 --- a/packages/twenty-docs/l/ar/developers/extend/apps/logic/overview.mdx +++ b/packages/twenty-docs/l/ar/developers/extend/apps/logic/overview.mdx @@ -40,13 +40,13 @@ icon: bolt دالة المنطق تختار واحدًا أو أكثر من المشغلات — كل إدخال أدناه هو حقل منفصل في `defineLogicFunction()`: -| المشغّل | متى يعمل | الإعداد | -| ---------------------- | -------------------------------------------------------------- | ------------------------------- | -| **مسار HTTP** | طلب يصل إلى نقطة نهاية `/s/\` الخاصة بك | `httpRouteTriggerSettings` | -| **كرون** | عند تطابق تعبير CRON | `cronTriggerSettings` | -| **حدث قاعدة البيانات** | يتم إنشاء سجل في مساحة العمل أو تحديثه أو حذفه | `databaseEventTriggerSettings` | -| **أداة ذكاء اصطناعي** | ميزة ذكاء اصطناعي في Twenty تقرر استدعاء دالتك | `toolTriggerSettings` | -| **إجراء سير العمل** | تستدعي خطوة في سير العمل دالتك | `workflowActionTriggerSettings` | +| المشغّل | متى يعمل | الإعداد | +| ---------------------- | ---------------------------------------------- | ------------------------------- | +| **مسار HTTP** | طلب يضرب عنوان URL العام لوظيفتك | `httpRouteTriggerSettings` | +| **كرون** | عند تطابق تعبير CRON | `cronTriggerSettings` | +| **حدث قاعدة البيانات** | يتم إنشاء سجل في مساحة العمل أو تحديثه أو حذفه | `databaseEventTriggerSettings` | +| **أداة ذكاء اصطناعي** | ميزة ذكاء اصطناعي في Twenty تقرر استدعاء دالتك | `toolTriggerSettings` | +| **إجراء سير العمل** | تستدعي خطوة في سير العمل دالتك | `workflowActionTriggerSettings` | تعمل الدوال ضمن عمليات Node.js معزولة، وتصل إلى مساحة العمل عبر عميل واجهة برمجة تطبيقات مضبوط الأنواع ومحدّد النطاق بالدور المصرّح عنه في [`defineApplication()`](/l/ar/developers/extend/apps/config/application). diff --git a/packages/twenty-docs/l/ar/developers/extend/apps/operations/cli.mdx b/packages/twenty-docs/l/ar/developers/extend/apps/operations/cli.mdx index 97f1ccfb78..a1fd62893a 100644 --- a/packages/twenty-docs/l/ar/developers/extend/apps/operations/cli.mdx +++ b/packages/twenty-docs/l/ar/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` وسيلتك للتعامل مع كل ما يتعلق بالتطبيق. القائمة الكاملة للأوامر: + +| أمر | ماذا يفعل | موثَّق في | +| ----------------------------------------------- | ------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------- | +| `dev` | يراقب ملفات المصدر ويزامن التغييرات مباشرة | [البدء السريع](/l/ar/developers/extend/apps/getting-started/quick-start) | +| `الخطة` | معاينة تغييرات البيانات الوصفية بدون تطبيقها | [المزامنة والاستعادة](/l/ar/developers/extend/apps/operations/sync-and-recovery#previewing-changes-plan) | +| `تطبيق` | تطبيق تغييرات البيانات الوصفية بعد عرض الخطة | [المزامنة والاستعادة](/l/ar/developers/extend/apps/operations/sync-and-recovery) | +| `dev:build` | ترجمة التطبيق وإنشاء عميل واجهة برمجة التطبيقات (استخدم `--tarball` لحزم ملف `.tgz`) | [النشر](/l/ar/developers/extend/apps/operations/publishing) | +| `dev:typecheck` | تشغيل فحص الأنواع في TypeScript | [الاختبار](/l/ar/developers/extend/apps/operations/testing) | +| `dev:add` | إنشاء هيكل كيان جديد | [إنشاء الهياكل](/l/ar/developers/extend/apps/getting-started/scaffolding) | +| `dev:generate-client` | إعادة إنشاء عميل واجهة برمجة التطبيقات مضبوط الأنواع | هذه الصفحة | +| `dev:function:exec` / `dev:function:logs` | تنفيذ الدوال وبث سجلاتها | هذه الصفحة | +| `dev:translations-extract` | استخراج السلاسل القابلة للترجمة إلى كتالوجات `locales/` | [الترجمات](/l/ar/developers/extend/apps/translations/overview) | +| `dev:catalog-sync` | تشغيل مزامنة كتالوج السوق | [النشر](/l/ar/developers/extend/apps/operations/publishing#how-marketplace-discovery-works) | +| `app:publish` / `app:install` / `app:uninstall` | دورة حياة الإصدارات | [النشر](/l/ar/developers/extend/apps/operations/publishing) وهذه الصفحة | +| `docker:*` | إدارة حاوية خادم Twenty المحلي | [الخادم المحلي](/l/ar/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/ar/developers/extend/apps/operations/publishing.mdx b/packages/twenty-docs/l/ar/developers/extend/apps/operations/publishing.mdx index dd55029bd6..3c8f05c923 100644 --- a/packages/twenty-docs/l/ar/developers/extend/apps/operations/publishing.mdx +++ b/packages/twenty-docs/l/ar/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) أعلاه. إذا لم يحدد تطبيقك `aboutDescription` في `defineApplication()`، فسيستخدم السوق تلقائيًا ملف `README.md` الخاص بحزمتك من npm كمحتوى لصفحة حول. هذا يعني أنه يمكنك الاحتفاظ بملف README واحد لكل من npm وسوق Twenty. إذا كنت تريد وصفًا مختلفًا في السوق، فقم بتعيين `aboutDescription` بشكل صريح. diff --git a/packages/twenty-docs/l/ar/developers/extend/apps/operations/sync-and-recovery.mdx b/packages/twenty-docs/l/ar/developers/extend/apps/operations/sync-and-recovery.mdx index 3b6040a440..54bb9cf9de 100644 --- a/packages/twenty-docs/l/ar/developers/extend/apps/operations/sync-and-recovery.mdx +++ b/packages/twenty-docs/l/ar/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` لتخطي رسالة التأكيد. | | إرسال ملف tarball إلى خادم | `yarn twenty app:publish --private` | يتطلّب إصدارًا **أعلى بشكل صارم** في `package.json` — راجع قسم [النشر](/l/ar/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` بمزامنة ملف manifest في مكانه ولا يتطلّب تغيير الإصدار أبدًا، لذا لست بحاجة إلى تعديل `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) ``` هذه أداتك الأولى للتشخيص: تُخبرك بدقة ما الكائنات والحقول والتخطيطات التي تغيّرت، بحيث يمكنك التأكد من أن المزامنة أنجزت ما توقّعته قبل التحقّق من واجهة المستخدم. +التغييرات التدميرية (`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) استخدم ذلك المعرّف للعثور على الكيان في ملف manifest الخاص بك (وإن لزم الأمر، في مساحة العمل) بدلًا من تخمين أيّها يتعارض. -## معاينة التغييرات (تشغيل تجريبي dry run) +## معاينة التغييرات (plan) -يبني `yarn twenty dev --once --dry-run` ملف manifest الخاص بك، ويطلب من الخادم خطة الترحيل، ويطبعها — **بدون تطبيق أي شيء**. إنها الطريقة الآمنة للإجابة عن سؤال "ما الذي ستغيّره هذه المزامنة؟" قبل الالتزام بها. +يقوم `yarn twenty plan` ببناء ملف manifest الخاص بك، ويطلب من الخادم خطة الترحيل، ويطبعها — **بدون تطبيق أي شيء**. إنها الطريقة الآمنة للإجابة عن سؤال "ما الذي ستغيّره هذه المزامنة؟" قبل الالتزام بها. ```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. * يُرجع **نفس الفرق** الذي ستُطبِّقه مزامنة حقيقية، حتى تتمكن من مراجعة الكيانات التي سيتم إنشاؤها/تحديثها/حذفها مسبقًا. * يكون مفيدًا قبل إجراء تغيير محفوف بالمخاطر، أو عند مراجعة تغيير تم إنشاؤه بواسطة الذكاء الاصطناعي، أو في سكربت يجب أن يفشل إذا كان تغيير غير متوقَّع على وشك الحدوث. -يُعاين التشغيل التجريبي فقط **تغييرات البيانات الوصفية**، ويتطلّب أن يكون التطبيق قد تمت مزامنته مرة واحدة على الأقل (حتى تعرف به مساحة العمل). إذا شغّلته ضد تطبيق لم تتم مزامنته من قبل، سيبلغ الخادم أن التطبيق غير مُثبّت — شغّل `yarn twenty dev` مرة واحدة أولًا. +تقوم الخطة فقط بمعاينة **تغييرات البيانات الوصفية**، ويتطلّب ذلك أن يكون التطبيق قد تمت مزامنته مرة واحدة على الأقل (حتى تعرف به مساحة العمل). إذا شغّلته ضد تطبيق لم تتم مزامنته من قبل، سيبلغ الخادم أن التطبيق غير مُثبّت — شغّل `yarn twenty dev` مرة واحدة أولًا. ## سلّم الاستعادة عندما تبدو البيانات الوصفية المحلية غير صحيحة، صعِّد الإجراءات بهذا الترتيب وتوقّف بمجرد زوال العائق. كل خطوة أكثر إرباكًا من التي قبلها. -1. **أعد المزامنة.** شغّل `yarn twenty dev --once` مرة أخرى. عمليات المزامنة متطابِقة الأثر (idempotent) — إعادة تشغيل ملف manifest النظيف آمنة وغالبًا ما تحل تعثرًا عابرًا. -2. **عاين الخطة.** شغّل `yarn twenty dev --once --dry-run` لرؤية ما الذي تنوي المزامنة التالية تغييره بالضبط، بدون تطبيقه. +1. **أعد المزامنة.** شغّل `yarn twenty apply` مرة أخرى. عمليات المزامنة متطابِقة الأثر (idempotent) — إعادة تشغيل ملف manifest النظيف آمنة وغالبًا ما تحل تعثرًا عابرًا. +2. **عاين الخطة.** شغّل `yarn twenty plan` لرؤية ما الذي تنوي المزامنة التالية تغييره بالضبط، بدون تطبيقه. 3. **اقرأ الخطأ المسمّى.** إذا فشلت المزامنة، لاحظ نوع البيانات الوصفية و`universalIdentifier` في الرسالة (انظر أعلاه) وحدّد ذلك الكيان في ملف manifest الخاص بك. يشير التعارض عادةً إلى معرّف مكرر أو مُعاد استخدامه. 4. **إلغاء التثبيت وإعادة التثبيت.** شغّل `yarn twenty app:uninstall`، ثم أجرِ مزامنة مرة أخرى (`yarn twenty dev`). هذا يعيد بناء بيانات التطبيق الوصفية من نقطة بداية نظيفة مع إبقاء باقي مساحة العمل سليمة. 5. **إعادة تعيين كاملة (الملاذ الأخير).** شغّل `yarn twenty docker:reset`، ثم أعد التهيئة والمزامنة. diff --git a/packages/twenty-docs/l/ar/developers/extend/apps/operations/testing.mdx b/packages/twenty-docs/l/ar/developers/extend/apps/operations/testing.mdx index 89282760dd..2d264df7d0 100644 --- a/packages/twenty-docs/l/ar/developers/extend/apps/operations/testing.mdx +++ b/packages/twenty-docs/l/ar/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 البرمجية يُصدِّر المسار الفرعي `twenty-sdk/cli` دوالًا يمكنك استدعاؤها مباشرةً من شيفرة الاختبار: -| دالة | الوصف | -| -------------- | ----------------------------------------- | -| `appBuild` | بناء التطبيق واختياريًا حزم ملف tarball | -| `appDeploy` | رفع ملف tarball إلى الخادم | -| `appInstall` | تثبيت التطبيق على مساحة العمل النشطة | -| `appUninstall` | إلغاء تثبيت التطبيق من مساحة العمل النشطة | +| دالة | الوصف | +| -------------- | ------------------------------------------------------------------- | +| `appBuild` | بناء التطبيق واختياريًا حزم ملف tarball | +| `appDeploy` | رفع ملف tarball إلى الخادم | +| `appDevOnce` | بناء التطبيق ومزامنته مرة واحدة (نفس الأمر مثل `yarn twenty apply`) | +| `appInstall` | تثبيت التطبيق على مساحة العمل النشطة | +| `appUninstall` | إلغاء تثبيت التطبيق من مساحة العمل النشطة | تُرجع كل دالة كائن نتيجة يحتوي على `success: boolean` وعلى إمّا `data` أو `error`. @@ -238,64 +253,10 @@ yarn test:watch yarn twenty dev:typecheck ``` -يشغِّل هذا الأمر `tsc --noEmit` ويبلغ عن أي أخطاء في الأنواع. +يشغِّل هذا الأمر `tsc --noEmit` على ملف `tsconfig.json` الخاص بتطبيقك ويبلغ عن أي أخطاء في الأنواع. التطبيقات المُنشأة بالهيكل تأتي أيضًا مع سكربت `yarn typecheck` الذي يشمل ملفات الاختبار أيضًا (`tsconfig.spec.json`). ## التكامل المستمر (CI) باستخدام GitHub Actions -تولّد أداة إنشاء الهيكل سير عمل GitHub Actions جاهزًا للاستخدام في `.github/workflows/ci.yml`. يشغّل اختبارات التكامل لديك تلقائيًا عند كل دفع إلى `main` وعلى طلبات السحب. +تولّد أداة إنشاء الهيكل سير عمل جاهزًا للاستخدام في `.github/workflows/ci.yml`. عند كل دفع إلى الفرع `main` وكل طلب سحب، تُنشئ الأداة خادم Twenty مؤقتًا في بيئة التشغيل (عبر الإجراء `twentyhq/twenty/.github/actions/spawn-twenty-app-dev-test`)، ثم تشغِّل الأوامر `yarn lint` و`yarn typecheck` و`yarn test:unit` و`yarn test` مع ضبط المتغيرين `TWENTY_API_URL` و`TWENTY_API_KEY` للإشارة إلى ذلك الخادم. لا تُطلَب أي أسرار، ويمكنك تثبيت إصدار الخادم عبر متغير البيئة `TWENTY_VERSION` في أعلى سير العمل. -سير العمل: - -1. يجلب الشيفرة الخاصة بك -2. يشغّل خادم Twenty مؤقتًا باستخدام الإجراء `twentyhq/twenty/.github/actions/spawn-twenty-docker-image` -3. يثبّت التبعيات باستخدام `yarn install --immutable` -4. يشغّل `yarn test` مع حقن `TWENTY_API_URL` و`TWENTY_API_KEY` من مخرجات الإجراء - -```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. - -لتثبيت إصدار محدّد من Twenty بدلًا من `latest`، غيّر متغير البيئة `TWENTY_VERSION` في أعلى سير العمل. +راجع قسم [النشر → التكامل/التسليم المستمران الآليان](/l/ar/developers/extend/apps/operations/publishing#automated-cicd-scaffolded-workflows) للاطلاع على شرح كامل لكلا سيرَي العمل المُنشأين بالهيكل (`ci.yml` وخط أنابيب النشر `cd.yml`). diff --git a/packages/twenty-docs/l/ar/developers/extend/apps/tutorials/document-generator/building-the-ui.mdx b/packages/twenty-docs/l/ar/developers/extend/apps/tutorials/document-generator/building-the-ui.mdx index 2e1cd50a41..720ae7876e 100644 --- a/packages/twenty-docs/l/ar/developers/extend/apps/tutorials/document-generator/building-the-ui.mdx +++ b/packages/twenty-docs/l/ar/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 }), @@ -179,7 +181,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/ar/developers/extend/apps/tutorials/document-generator/http-routes.mdx b/packages/twenty-docs/l/ar/developers/extend/apps/tutorials/document-generator/http-routes.mdx index 1538093658..2447fd578b 100644 --- a/packages/twenty-docs/l/ar/developers/extend/apps/tutorials/document-generator/http-routes.mdx +++ b/packages/twenty-docs/l/ar/developers/extend/apps/tutorials/document-generator/http-routes.mdx @@ -9,8 +9,14 @@ description: قم بتفعيل الوظيفة على HTTP وتقديم الوث * نقطة النهاية **POST** مكالمات واجهة المستخدم لإنشاء وثيقة، و * نقطة نهاية عامة **GET** تجعل الوثيقة صفحة ويب قابلة للطباعة. -وكلاهما يستخدم `httpRouteTriggerSettings`. طرق التطبيق تقدم تحت `/s` على خادم -20 (على سبيل المثال 'http://localhost:2020/s/documents/generate\`). +وكلاهما يستخدم `httpRouteTriggerSettings`. على خادم dev المحلي، طرق التطبيق هي +تقدم تحت بادئة `/s` (على سبيل المثال 'http://localhost:2020/s/documents/generate\`). + + +على عشرين سحابة، يتم خدمة المسارات على نطاق وظائف مساحة العمل المخصصة- عنوان URL 20 حقن بـ 'TWENTY_FUNCTIONS_URL`، بدون بادئة '/s'. البادئة `/s\` + مهملة هناك ولا تبقى إلا للجهات المحلية والمحلية. + انظر [تسمية دالة منطقية](/l/ar/developers/extend/apps/layout/front-components#calling-a-logic-function). + ## مسار POST - توليد حسب الطلب diff --git a/packages/twenty-docs/l/ar/developers/extend/apps/tutorials/document-generator/publishing.mdx b/packages/twenty-docs/l/ar/developers/extend/apps/tutorials/document-generator/publishing.mdx index 9593cf05dc..4f9d039acd 100644 --- a/packages/twenty-docs/l/ar/developers/extend/apps/tutorials/document-generator/publishing.mdx +++ b/packages/twenty-docs/l/ar/developers/extend/apps/tutorials/document-generator/publishing.mdx @@ -77,11 +77,11 @@ export default defineApplication({ 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/ar/developers/extend/apps/operations/testing) و [المزامنة والاسترداد](/l/ar/developers/extend/apps/operations/sync-and-recovery). diff --git a/packages/twenty-docs/l/cs/developers/extend/apps/config/install-hooks.mdx b/packages/twenty-docs/l/cs/developers/extend/apps/config/install-hooks.mdx index b3f3c6e92f..f00b95b653 100644 --- a/packages/twenty-docs/l/cs/developers/extend/apps/config/install-hooks.mdx +++ b/packages/twenty-docs/l/cs/developers/extend/apps/config/install-hooks.mdx @@ -4,9 +4,9 @@ description: Spouštějte logiku před instalací nebo po ní — naplňte data, icon: wrench --- -Instalační hooky jsou speciální logické funkce, které se spouštějí během životního cyklu instalace nebo upgradu. Sdílí stejný runtime handleru jako běžné [logické funkce](/l/cs/developers/extend/apps/logic/logic-functions) a přijímají `InstallPayload`, ale deklarují se pomocí vlastních definičních funkcí — `definePostInstallLogicFunction()` a `definePreInstallLogicFunction()` — a fungují mimo běžný model triggerů (HTTP, cron, databázové události). +Instalační hooky jsou speciální logické funkce, které se spouštějí během životního cyklu instalace nebo upgradu. Sdílí stejný runtime handleru jako běžné [logické funkce](/l/cs/developers/extend/apps/logic/logic-functions) a přijímají `InstallPayload` (`{ previousVersion?: string; newVersion: string }` — `previousVersion` je při čisté instalaci `undefined`), ale deklarují se pomocí vlastních definičních funkcí a fungují mimo běžný model triggerů (HTTP, cron, databázové události). -Každá aplikace může definovat **nanejvýš jednu pre-install** a **nanejvýš jednu post-install** funkci. Sestavení manifestu skončí chybou, pokud je zjištěno více než jedno. +Každá aplikace může definovat **nanejvýš jednu pre-install** a **nanejvýš jednu post-install** funkci. Sestavení manifestu skončí chybou, pokud je zjištěno více než jedno z nich. ``` ┌─────────────────────────────────────────────────────────────┐ @@ -19,111 +19,59 @@ Každá aplikace může definovat **nanejvýš jednu pre-install** a **nanejvý └─────────────────────────────────────────────────────────────┘ ``` - - +## Na první pohled -Postinstalační funkce se spustí automaticky, jakmile je instalace vaší aplikace v pracovním prostoru dokončena. Server ji provede **poté**, co byla synchronizována metadata aplikace a vygenerován klient SDK, takže je pracovní prostor plně připraven k použití a nové schéma je zavedeno. Mezi typické případy použití patří naplnění výchozími daty, vytvoření počátečních záznamů, konfigurace nastavení pracovního prostoru nebo zřizování prostředků ve službách třetích stran. +| | `definePreInstallLogicFunction` | `definePostInstallLogicFunction` | +| --------------- | --------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- | +| Běhy | Před migrací metadat — **předchozí** schéma a data jsou stále neporušené | Po migraci a vygenerování SDK — **nové** schéma je připravené | +| Spuštění | Vždy synchronní; blokuje instalaci | Ve výchozím nastavení asynchronní (zařazeno do fronty, 3 pokusy); synchronní režim volitelný přes `shouldRunSynchronously: true` | +| Při selhání | Instalace je **zrušena** před jakoukoli změnou schématu | Async: opakovaný pokus až 3krát. Sync: volající obdrží `POST_INSTALL_ERROR` (změny schématu se **ne**vracejí zpět) | +| Typické použití | Zálohovat nebo opravit data, která by migrace ztratila; odmítnout rizikovou aktualizaci vyvoláním výjimky | Naplňte výchozí data, nakonfigurujte pracovní prostor, zaregistrujte externí prostředky | -```ts src/logic-functions/post-install.ts -import { definePostInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define'; +**Pravidlo:** výchozí volbou je post-install. Po pre-install sáhněte pouze tehdy, když je samotná migrace destruktivní a potřebujete zachytit předchozí stav, než zmizí. -const handler = async (payload: InstallPayload): Promise => { - console.log('Post install logic function executed successfully!', payload.previousVersion); -}; +| Chcete... | Použít | +| ------------------------------------------------------------------------------- | ------------------------------------------------------------------------ | +| Naplňte data, nakonfigurujte pracovní prostor, zaregistrujte externí prostředky | `post-install` | +| Dlouho běžící práce, která by neměla blokovat odpověď instalace | `post-install` (výchozí asynchronní režim, s opakovanými pokusy workeru) | +| Rychlé nastavení, na které volající spoléhá ihned po dokončení instalace | `post-install` s `shouldRunSynchronously: true` | +| Čtěte nebo zálohujte data, která by nadcházející migrace ztratila | `pre-install` | +| Odmítněte aktualizaci, která by poškodila existující data | `pre-install` (vyhoďte výjimku z obslužné funkce) | +| Srovnání stavu při každé aktualizaci | Libovolný hook s `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, -}); -``` +## Chování sdílené oběma hooky -Postinstalační funkci můžete také kdykoli spustit ručně pomocí CLI: +* Konfigurace je konfigurace `defineLogicFunction` bez nastavení triggeru, doplněná o `shouldRunOnVersionUpgrade`. +* **Kdy se spouští**: ve výchozím nastavení pouze při čistých instalacích. Nastavte `shouldRunOnVersionUpgrade: true`, aby se spouštěl i při aktualizacích. Použijte `previousVersion` / `newVersion` k větvení podle cesty aktualizace. +* **Idempotence je důležitá**: asynchronní post-install se může spouštět opakovaně a kterýkoli hook se znovu spouští při aktualizacích, když je `shouldRunOnVersionUpgrade` zapnuté. +* Běžné prostředí logické funkce (`APPLICATION_ID`, `APP_ACCESS_TOKEN`, `API_URL`) je injektováno, takže můžete volat Twenty API s tokenem své aplikace. +* Hook je při sestavení automaticky připojen k manifestu aplikace (`preInstallLogicFunction` / `postInstallLogicFunction`) — v [`defineApplication()`](/l/cs/developers/extend/apps/config/application) není potřeba na nic odkazovat. +* Výchozí `timeoutSeconds` je 300, aby umožnil delší úlohy nastavení, jako je naplnění daty. +* **Nespouští se v dev režimu**: `yarn twenty dev` přeskočí instalační flow a soubory synchronizuje přímo, takže se hooky v tomto režimu nikdy nespustí. Místo toho je spouštějte ručně: ```bash filename="Terminal" yarn twenty dev:function:exec --postInstall -``` - -Hlavní body: -* Postinstalační funkce používají `definePostInstallLogicFunction()` — specializovanou variantu, která vynechává nastavení spouštěčů (`cronTriggerSettings`, `databaseEventTriggerSettings`, `httpRouteTriggerSettings`, `toolTriggerSettings`, `workflowActionTriggerSettings`). -* Obslužná funkce obdrží `InstallPayload` s `{ previousVersion?: string; newVersion: string }` — `newVersion` je verze, která se instaluje, a `previousVersion` je verze, která byla nainstalována dříve (nebo `undefined` při čisté instalaci). Tyto hodnoty použijte k rozlišení čistých instalací od aktualizací a ke spuštění migrační logiky specifické pro verzi. -* **Kdy se hook spouští**: ve výchozím nastavení pouze při čistých instalacích. Předejte `shouldRunOnVersionUpgrade: true`, pokud chcete, aby se spouštěl i při aktualizaci aplikace z předchozí verze. Pokud je vynechán, příznak má výchozí hodnotu `false` a při aktualizacích se hook přeskočí. -* **Model provádění — ve výchozím nastavení asynchronní, synchronní volitelně**: příznak `shouldRunSynchronously` určuje *jak* se spouští post-install. - * `shouldRunSynchronously: false` *(výchozí)* — hook je **zařazen do fronty zpráv** s `retryLimit: 3` a běží asynchronně ve workeru. Odezva instalace se vrátí hned po zařazení úlohy do fronty, takže pomalá nebo chybující obslužná funkce neblokuje volajícího. Worker se pokusí o opakování až třikrát. **Použijte pro dlouho běžící úlohy** — plnění velkých datových sad, volání pomalých externích API, zřizování externích prostředků, cokoli, co by mohlo přesáhnout rozumné časové okno HTTP odezvy. - * `shouldRunSynchronously: true` — hook se provádí **inline během instalačního procesu** (stejný vykonavatel jako pre-install). Instalační požadavek blokuje, dokud obslužná funkce nedokončí, a pokud vyvolá výjimku, volající instalace obdrží `POST_INSTALL_ERROR`. Žádné automatické opakování. **Použijte pro rychlé úlohy, které se musí dokončit před odpovědí** — například vrácení validační chyby uživateli nebo rychlé nastavení, na kterém bude klient záviset ihned po návratu volání instalace. Mějte na paměti, že v době, kdy se spustí post-install, už byla migrace metadat aplikována, takže selhání v synchronním režimu změny schématu **ne**vrací zpět — pouze odhalí chybu. -* Ujistěte se, že vaše obslužná funkce je idempotentní. V asynchronním režimu se může fronta pokusit až třikrát; v obou režimech se může hook znovu spustit při aktualizacích, pokud je `shouldRunOnVersionUpgrade: true`. -* Proměnné prostředí `APPLICATION_ID`, `APP_ACCESS_TOKEN` a `API_URL` jsou dostupné uvnitř obslužné funkce (stejně jako u jakékoli jiné logické funkce), takže můžete volat Twenty API s aplikačním přístupovým tokenem omezeným na vaši aplikaci. -* Na jednu aplikaci je povolena pouze jedna postinstalační funkce. Sestavení manifestu skončí chybou, pokud je zjištěna více než jedna. -* Atributy funkce `universalIdentifier`, `shouldRunOnVersionUpgrade` a `shouldRunSynchronously` jsou během buildu automaticky připojeny k manifestu aplikace do pole `postInstallLogicFunction` — není potřeba je uvádět v [`defineApplication()`](/l/cs/developers/extend/apps/config/application). -* Výchozí časový limit je nastaven na 300 sekund (5 minut), aby umožnil delší úlohy nastavení, jako je naplnění daty. -* **Nespouští se v režimu dev**: když je aplikace registrována lokálně (pomocí `yarn twenty dev`), server zcela přeskočí instalační tok a synchronizuje soubory přímo prostřednictvím sledovače CLI — takže se post-install v režimu dev nikdy nespustí bez ohledu na `shouldRunSynchronously`. Použijte `yarn twenty dev:function:exec --postInstall` k ručnímu spuštění nad běžícím pracovním prostorem. - - - - -Předinstalační funkce se během instalace spouští automaticky, **před aplikováním migrace metadat pracovního prostoru**. Má stejný tvar payloadu jako post-install (`InstallPayload`), ale je zařazena dříve v instalačním toku, aby mohla připravit stav, na němž nadcházející migrace závisí — typické použití zahrnuje zálohování dat, ověření kompatibility s novým schématem nebo archivaci záznamů, které se chystají přeuspořádat nebo odstranit. - -```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, -}); -``` - -Předinstalační funkci můžete také kdykoli spustit ručně pomocí CLI: - -```bash filename="Terminal" yarn twenty dev:function:exec --preInstall ``` -Hlavní body: -* Funkce pre-install používají `definePreInstallLogicFunction()` — stejné specializované nastavení jako u post-install, pouze připojené k jiné fázi životního cyklu. -* Obě obslužné funkce pre- i post-install přijímají stejný typ `InstallPayload`: `{ previousVersion?: string; newVersion: string }`. Importujte jej jednou a znovu použijte pro oba hooky. -* **Kdy se hook spouští**: umístěn těsně před migrací metadat pracovního prostoru (`synchronizeFromManifest`). Před spuštěním server provede čistě aditivní "zjednodušenou synchronizaci", která v metadatech pracovního prostoru zaregistruje pre-install funkci **nové** verze — ničeho dalšího se nedotkne — a poté ji spustí. Protože tato synchronizace je pouze aditivní, objekty, pole a data předchozí verze zůstávají při spuštění vaší obslužné funkce zachována: můžete bezpečně číst a zálohovat stav před migrací. -* **Model provádění**: pre-install se provádí **synchronně** a **blokuje instalaci**. Pokud obslužná funkce vyvolá výjimku, instalace se přeruší ještě před aplikováním jakýchkoli změn schématu — pracovní prostor zůstane na předchozí verzi v konzistentním stavu. Je to záměrné: pre-install je vaše poslední šance odmítnout rizikovou aktualizaci. -* Stejně jako u post-install je na jednu aplikaci povolena pouze jedna funkce pre-install. Během buildu je automaticky připojena k manifestu aplikace pod `preInstallLogicFunction`. -* **Nespouští se v režimu dev**: stejně jako u post-install — u lokálně registrovaných aplikací je instalační tok zcela přeskočen, takže se pre-install pod `yarn twenty dev` nikdy nespustí. Použijte `yarn twenty dev:function:exec --preInstall` k ručnímu spuštění. + + - - - -Oba hooky jsou součástí téhož instalačního toku a přijímají stejný `InstallPayload`. Rozdíl je v tom, **kdy** se spouštějí vzhledem k migraci metadat pracovního prostoru, a to určuje, jakých dat se mohou bezpečně dotýkat. - -Pre-install je vždy **synchronní** (blokuje instalaci a může ji přerušit). Post-install je **ve výchozím nastavení asynchronní** — zařazen do workeru s automatickými pokusy o opakování — ale může přejít na synchronní provádění pomocí `shouldRunSynchronously: true`. Viz accordion `definePostInstallLogicFunction` výše, kdy použít jednotlivé režimy. - -**Použijte `post-install` pro cokoli, co vyžaduje existenci nového schématu.** To je běžný případ: - -* Plnění výchozími daty (vytváření počátečních záznamů, výchozích pohledů, demo obsahu) vůči nově přidaným objektům a polím. -* Registrace webhooků u služeb třetích stran poté, co má aplikace své přihlašovací údaje. -* Volání vlastního API k dokončení nastavení, které závisí na synchronizovaných metadatech. -* Idempotentní logika "zajisti, že to existuje", která má při každé aktualizaci uvést stav do souladu — kombinujte s `shouldRunOnVersionUpgrade: true`. - -Příklad — po instalaci naplňte výchozí záznam `PostCard`: +Spouští se, jakmile vaše aplikace dokončí instalaci: metadata jsou synchronizovaná, klient SDK vygenerovaný a nové schéma je možné dotazovat. Příklad — při čisté instalaci naplňte výchozí záznam: ```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, }); ``` -**Použijte `pre-install`, pokud by migrace jinak zničila nebo poškodila existující data.** Protože pre-install běží proti *předchozímu* schématu a jeho selhání vrací aktualizaci zpět, je to správné místo pro cokoli rizikového: +Příznak `shouldRunSynchronously` řídí model spuštění: -* **Zálohování dat, která se chystají odstranit nebo přeuspořádat** — např. odstraňujete pole ve verzi v2 a potřebujete jeho hodnoty zkopírovat do jiného pole nebo je před spuštěním migrace exportovat do úložiště. -* **Archivace záznamů, které by nové omezení zneplatnilo** — např. pole se stává `NOT NULL` a je třeba nejprve smazat nebo opravit řádky s hodnotami null. -* **Ověření kompatibility a odmítnutí aktualizace, pokud nelze aktuální data čistě migrovat** — vyhoďte výjimku z obslužné funkce a instalace se ukončí bez provedených změn. Je to bezpečnější, než zjistit nekompatibilitu uprostřed migrace. -* **Přejmenování nebo změna klíčů dat** před změnou schématu, která by ztratila vazby. +* `false` *(výchozí)* — zařazeno do fronty zpráv (`retryLimit: 3`) a spuštěno workerem. Odezva instalace se vrátí, jakmile je úloha zařazena do fronty. **Používejte pro dlouho běžící práci** — naplňování velkých datových sad, pomalá API třetích stran. +* `true` — spuštěno inline během instalačního flow. Instalační požadavek blokuje, dokud handler neskončí; vyvolaná chyba se projeví pro volajícího jako `POST_INSTALL_ERROR` (žádné opakované pokusy). **Používejte pro rychlou práci, která musí být dokončena před odpovědí.** Migrace už je v tomto bodě aplikovaná, takže selhání nevrací změny schématu zpět — pouze předá chybu dál. -Příklad — archivujte záznamy před destruktivní migrací: + + + +Spouští se před migrací metadat, nad **předchozím** schématem — vhodné místo pro zálohování dat, která by migrace ztratila, nebo pro odmítnutí rizikové aktualizace. Před spuštěním server provede čistě aditivní „zjednodušenou synchronizaci“, která zaregistruje pouze pre-install funkci nové verze; vše ostatní — objekty, pole a data předchozí verze — zůstává při běhu vašeho handleru nedotčeno. + +Pre-install je vždy **synchronní** a blokuje instalaci. Pokud obslužná funkce vyvolá výjimku, instalace se přeruší ještě před jakoukoli změnou schématu — pracovní prostor zůstane na předchozí verzi v konzistentním stavu. Je to záměrné: pre-install je vaše poslední šance odmítnout rizikovou aktualizaci. + +Příklad — zkopírujte hodnoty staršího pole dříve, než ho migrace odstraní: ```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({ }); ``` -**Zlaté pravidlo:** - -| Chcete... | Použít | -| ------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- | -| Naplňte výchozí data, nakonfigurujte pracovní prostor, zaregistrujte externí prostředky | `post-install` | -| Spusťte dlouho běžící plnění nebo volání třetích stran, která by neměla blokovat odezvu instalace | `post-install` (výchozí — `shouldRunSynchronously: false`, s opakovanými pokusy workeru) | -| Spusťte rychlé nastavení, na které bude volající spoléhat ihned po návratu volání instalace | `post-install` s `shouldRunSynchronously: true` | -| Čtěte nebo zálohujte data, která by nadcházející migrace ztratila | `pre-install` | -| Odmítněte aktualizaci, která by poškodila existující data | `pre-install` (vyhoďte výjimku z obslužné funkce) | -| Spouštějte srovnání stavu při každé aktualizaci | `post-install` s `shouldRunOnVersionUpgrade: true` | -| Proveďte jednorázové nastavení pouze při první instalaci | `post-install` s `shouldRunOnVersionUpgrade: false` (výchozí) | - - -Pokud si nejste jisti, výchozí volbou je **post-install**. Po pre-install sáhněte pouze tehdy, když je samotná migrace destruktivní a potřebujete zachytit předchozí stav, než zmizí. - - diff --git a/packages/twenty-docs/l/cs/developers/extend/apps/data/objects.mdx b/packages/twenty-docs/l/cs/developers/extend/apps/data/objects.mdx index 5dcd4855f7..d4c57f0317 100644 --- a/packages/twenty-docs/l/cs/developers/extend/apps/data/objects.mdx +++ b/packages/twenty-docs/l/cs/developers/extend/apps/data/objects.mdx @@ -86,6 +86,22 @@ export default defineObject({ **Základní pole jsou přidána automaticky.** Když definujete vlastní objekt, Twenty pro vás vytvoří standardní pole jako `id`, `name`, `createdAt`, `updatedAt`, `createdBy`, `updatedBy` a `deletedAt`. Nemusíte je uvádět v poli `fields` — pouze svá vlastní pole. Výchozí pole můžete přepsat tak, že deklarujete pole se stejným názvem, ale jen zřídka je to dobrý nápad. +## Typy polí + +Úplná sada hodnot `FieldType`, exportovaných z `twenty-sdk/define`: + +| Kategorie | Typy | +| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------- | +| Text | `TEXT`, `RICH_TEXT`, `ARRAY` (řetězců), `RAW_JSON` | +| Číselné | `NUMBER` (`universalSettings.dataType`: `'float'` / `'int'` / `'bigint'`), `NUMERIC` (libovolná přesnost), `RATING`, `POSITION` | +| Data | `DATE`, `DATE_TIME` | +| Výběr | `BOOLEAN`, `SELECT`, `MULTI_SELECT` | +| Složené | `FULL_NAME`, `ADDRESS`, `EMAILS`, `PHONES`, `LINKS`, `CURRENCY`, `ACTOR`, `FILES` | +| Identifikátory a relace | `UUID`, `RELATION`, `MORPH_RELATION` (viz [Relations](/l/cs/developers/extend/apps/data/relations)) | +| Systém | `TS_VECTOR` (vektor pro fulltextové vyhledávání, spravovaný serverem) | + +Složené typy ukládají více podpolí (např. `FULL_NAME` = křestní + příjmení; `CURRENCY` = `amountMicros` + `currencyCode`). `SELECT` a `MULTI_SELECT` vyžadují pole `options`, jak je ukázáno v příkladu výše. + ## Výchozí hodnoty Výchozí textové hodnoty musí být uzavřené v jednoduchých uvozovkách **uvnitř** řetězce — `defaultValue: "'Draft'"`, ne `defaultValue: "Draft"`. Proto pole `status` výše používá `` `'${PostCardStatus.DRAFT}'` ``. diff --git a/packages/twenty-docs/l/cs/developers/extend/apps/getting-started/project-structure.mdx b/packages/twenty-docs/l/cs/developers/extend/apps/getting-started/project-structure.mdx index 6568a7fc96..134fcf2e6f 100644 --- a/packages/twenty-docs/l/cs/developers/extend/apps/getting-started/project-structure.mdx +++ b/packages/twenty-docs/l/cs/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 ``` ## Klíčové soubory -| Soubor / Složka | Účel | -| ---------------------------------------- | ----------------------------------------------------------------------- | -| `src/application-config.ts` | **Povinné.** Hlavní konfigurační soubor vaší aplikace. | -| `src/default-role.ts` | Výchozí role, která řídí, k čemu mohou vaše logické funkce přistupovat. | -| `src/constants/universal-identifiers.ts` | Automaticky generovaná UUID a metadata (zobrazovaný název, popis). | -| `src/__tests__/` | Integrační testy (nastavení + ukázkový test). | -| `public/` | Statické soubory (obrázky, písma) doručované vaší aplikací. | +| Soubor / Složka | Účel | +| -------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- | +| `src/application-config.ts` | **Povinné.** Hlavní konfigurační soubor vaší aplikace. | +| `src/default-role.ts` | Výchozí role, která řídí, k čemu mohou vaše logické funkce přistupovat. | +| `src/constants/universal-identifiers.ts` | Automaticky generovaná UUID a metadata (zobrazovaný název, popis). | +| `src/front-components/`, `src/navigation-menu-items/`, `src/page-layouts/` | Úvodní uvítací stránka: front komponenta renderovaná samostatným rozložením stránky, dostupná z postranního panelu. | +| `src/__tests__/` | Jednotkový test plus integrační test (s jeho globálním nastavením), který synchronizuje aplikaci proti skutečnému serveru. | +| `public/` | Statické soubory (obrázky, písma) doručované vaší aplikací. | +| `AGENTS.md` / `CLAUDE.md` | Pokyny pro agenty AI pro psaní kódu, kteří pracují na aplikaci. | **Uspořádání souborů je na vás.** Výše uvedené složky jsou konvence — SDK detekuje entity pomocí analýzy AST u volání `export default defineEntity(...)` bez ohledu na to, kde se soubor nachází. @@ -47,15 +60,18 @@ Oba balíčky Twenty SDK patří pod `devDependencies`, ne pod `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" } } ``` +Nástroj pro scaffolding připne `twenty-sdk` a `twenty-client-sdk` ke své vlastní verzi — při aktualizaci udržujte tyto dva balíčky synchronizované. + * **`twenty-sdk`** dodává `twenty` CLI a nástroje pro sestavení/scaffolding. Běží pouze při vývoji a sestavování a nikdy není importován za běhu zveřejněné aplikace. * **`twenty-client-sdk`** je importován kódem vaší aplikace (`CoreApiClient`, `MetadataApiClient`, `RestApiClient`), ale Twenty ho poskytuje za běhu — logické funkce ho získávají z vygenerované SDK vrstvy a front-endové komponenty ho načítají z modulů poskytovaných serverem. Vaše nainstalovaná kopie se používá pouze pro kontrolu typů a build v době nasazení, takže ji nikdy není potřeba přibalit do nasazeného balíčku. -Ponechání kteréhokoli balíčku pod `dependencies` ho vtáhne do runtime balíčku nainstalované aplikace, kde je jen mrtvou vahou. `twenty build` vypíše varování, pokud je kterýkoli z nich stále uveden pod `dependencies`. +Ponechání kteréhokoli balíčku pod `dependencies` ho vtáhne do runtime balíčku nainstalované aplikace, kde je jen mrtvou vahou. `twenty dev:build` vypíše varování, pokud je kterýkoli z nich stále uveden pod `dependencies`. Vlastní runtime závislosti vaší aplikace (knihovny, které vaše logické funkce skutečně importují za běhu) přidejte jako obvykle pod `dependencies`. diff --git a/packages/twenty-docs/l/cs/developers/extend/apps/getting-started/quick-start.mdx b/packages/twenty-docs/l/cs/developers/extend/apps/getting-started/quick-start.mdx index ebb6f01dd3..df72263e72 100644 --- a/packages/twenty-docs/l/cs/developers/extend/apps/getting-started/quick-start.mdx +++ b/packages/twenty-docs/l/cs/developers/extend/apps/getting-started/quick-start.mdx @@ -6,17 +6,17 @@ description: Vytvořte svou první aplikaci Twenty během několika minut. ## Předpoklady -* **Node.js 24+** — [Stáhnout](https://nodejs.org/) +* **Node.js 24.5+** — [Stáhnout](https://nodejs.org/) * **Yarn 4** — je součástí Node.js prostřednictvím Corepacku. Povolte jej: `corepack enable` * **Docker** — [Stáhnout](https://www.docker.com/products/docker-desktop/). Nutné pro spuštění lokálního serveru Twenty. Přeskočte, pokud už máte Twenty spuštěné jinde. Vytváření aplikace Twenty má tři fáze. Generátor kostry je spojuje do jediného příkazu pro ideální scénář, ale každá fáze je samostatný koncept — když se něco nepovede, znalost aktuální fáze napoví, co opravit. -| Fáze | Co děláte | Nástroj | Výsledek | -| ---------------------- | -------------------------------------------------------- | ----------------------------- | -------------------------------------------- | -| **1. Vytvořit kostru** | Vygenerovat zdrojový kód aplikace | `npx create-twenty-app` | Projekt v TypeScriptu na disku | -| **2. Spustit server** | Spustit server Twenty, do kterého se bude synchronizovat | Docker + `yarn twenty server` | Běžící instance Twenty | -| **3. Synchronizovat** | Živě synchronizovat kód na server | `yarn twenty dev` | Vaše změny se objeví v uživatelském rozhraní | +| Fáze | Co děláte | Nástroj | Výsledek | +| ---------------------- | -------------------------------------------------------- | ----------------------------------- | -------------------------------------------- | +| **1. Vytvořit kostru** | Vygenerovat zdrojový kód aplikace | `npx create-twenty-app` | Projekt v TypeScriptu na disku | +| **2. Spustit server** | Spustit server Twenty, do kterého se bude synchronizovat | Docker + `yarn twenty docker:start` | Běžící instance Twenty | +| **3. Synchronizovat** | Živě synchronizovat kód na server | `yarn twenty dev` | Vaše změny se objeví v uživatelském rozhraní | --- @@ -28,7 +28,7 @@ Vytvořte novou aplikaci ze šablony: npx create-twenty-app@latest my-twenty-app ``` -Budete vyzváni k zadání názvu a popisu — výchozí hodnoty potvrdíte klávesou **Enter**. Tím se v `my-twenty-app/` vytvoří projekt v TypeScriptu se startovacím `application-config.ts`, výchozí rolí, CI workflow a integračním testem. +Generátor kostry je neinteraktivní: název adresáře se stane názvem aplikace. Předejte `--display-name` a `--description` pro přizpůsobení vygenerovaných metadat (později je můžete také upravit v `src/constants/universal-identifiers.ts`). Tím se v `my-twenty-app/` vytvoří projekt v TypeScriptu se startovacím `application-config.ts`, výchozí rolí, pracovními postupy CI/CD a integračním testem. **Po této fázi:** máte na svém počítači zdrojový kód aplikace. Zatím neběží — to je předmětem Fáze 2. @@ -38,28 +38,14 @@ Budete vyzváni k zadání názvu a popisu — výchozí hodnoty potvrdíte klá Vaše aplikace potřebuje server Twenty, do kterého se bude synchronizovat. Server je plnohodnotná instance Twenty — UI, GraphQL API, PostgreSQL — běžící lokálně v Dockeru. Váš lokální kód nahrává své definice na tento server, díky čemuž se objeví v UI. -Generátor kostry nabídne, že vám jej spustí: +Nástroj pro vytváření projektů to spustí za vás: při spuštěném Dockeru stáhne image `twentycrm/twenty-app-dev`, spustí ji na portu `2020` a autentizuje CLI vůči předpřipravenému demo workspace (`tim@apple.dev`) — není vyžadováno žádné přihlášení. -> **Chcete nastavit lokální instanci Twenty?** - -* **Ano (doporučeno)** — stáhne Docker image `twentycrm/twenty-app-dev` a spustí jej na portu `2020`. Nejprve se ujistěte, že Docker běží. -* **Ne** — zvolte, pokud už máte server Twenty, ke kterému se chcete připojit. Můžete jej propojit později pomocí `yarn twenty remote:add`. - -
- Spustit lokální instanci? -
- -Jakmile server běží, otevře se prohlížeč pro přihlášení. Použijte předpřipravený demo účet: - -* **E-mail:** `tim@apple.dev` -* **Heslo:** `tim@apple.dev` +Chcete-li se místo toho připojit k existujícímu serveru Twenty, předejte `--url \`. Vzdálené servery se ověřují pomocí OAuth: otevře se prohlížeč, abyste se mohli přihlásit a kliknout na **Authorize**, čímž udělíte nástroji CLI přístup k vašemu pracovnímu prostoru. (Můžete se také rozhodnout pro OAuth lokálně pomocí `--authentication-method oauth` — přihlaste se pomocí `tim@apple.dev` / `tim@apple.dev`.)
Přihlašovací obrazovka Twenty
-Na další obrazovce klikněte na **Authorize** — tím udělíte nástroji CLI přístup k vašemu pracovnímu prostoru. -
Autorizační obrazovka Twenty CLI
@@ -117,27 +103,31 @@ Klikněte na **View installed app**, abyste zobrazili instalaci v pracovním pro ### Jednorázová synchronizace pro CI a skripty -Předejte `--once` pro provedení jednoho sestavení + synchronizace a ukončení — stejný postup, bez sledování změn: +Použijte `plan` a `apply` ke spuštění stejné pipeline jednorázově, bez watcheru: ```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 ``` -| Příkaz | Chování | Kdy použít | -| ---------------------------------- | ---------------------------------------------------------------------------------- | ------------------------------------------------------------------------ | -| `yarn twenty dev` | Sleduje změny a při každé změně znovu synchronizuje. Běží, dokud jej nezastavíte. | Interaktivní lokální vývoj. | -| `yarn twenty dev --once` | Jedno sestavení + synchronizace, ukončí se s kódem `0` při úspěchu, `1` při chybě. | CI, pre-commit hooky, AI agenti, skriptované pracovní postupy. | -| `yarn twenty dev --once --dry-run` | Sestaví a vypíše změny metadat **bez jejich použití**. | Kontrola toho, co by synchronizace změnila, ještě před jejím potvrzením. | +| Příkaz | Chování | Kdy použít | +| ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------ | +| `yarn twenty dev` | Sleduje změny a při každé změně znovu synchronizuje. Běží, dokud jej nezastavíte. | Interaktivní lokální vývoj. | +| `yarn twenty apply` | Jedno sestavení + synchronizace, ukončí se s kódem `0` při úspěchu, `1` při chybě. Vyžádá si potvrzení destruktivních změn (předejte `--force`, abyste to přeskočili). | CI, pre-commit hooky, AI agenti, skriptované pracovní postupy. | +| `yarn twenty plan` | Sestaví a vypíše změny metadat **bez jejich použití**. | Kontrola toho, co by synchronizace změnila, ještě před jejím potvrzením. | -Oba režimy vyžadují autentizovaný vzdálený server. Více informací o `--dry-run` najdete v části [Synchronizace a obnovení](/l/cs/developers/extend/apps/operations/sync-and-recovery#previewing-changes-dry-run). +Všechny režimy vyžadují autentizovaný vzdálený server. Více informací o `plan` najdete v části [Synchronizace a obnovení](/l/cs/developers/extend/apps/operations/sync-and-recovery#previewing-changes-plan). + + +`yarn twenty dev --once` a `yarn twenty dev --once --dry-run` jsou zastaralé aliasy pro `yarn twenty apply` a `yarn twenty plan`. + ### Možnosti vývojového režimu | Přepínač | Popis | | ------------------------------------- | --------------------------------------------------------------------------------------------- | -| `--once` | Jednou sestavit a synchronizovat, poté ukončit. | -| `--dry-run` | Pomocí `--once` zobrazíte náhled změn metadat, aniž byste je použili. Nic nezapisuje. | -| `--debounceMs \` | Nastaví prodlevu pro potlačení zákmitů při změnách souborů v milisekundách (výchozí: `2000`). | +| `--force` | Aplikuje destruktivní změny (mazání) bez potvrzení. | +| `--debounceMs \` | Nastaví prodlevu pro potlačení zákmitů při změnách souborů v milisekundách (výchozí: `1000`). | | `--verbose` / `--debug` | Zobrazí podrobné protokoly sestavení, požadavky synchronizace a trasování chyb. | ## Co můžete vytvořit diff --git a/packages/twenty-docs/l/cs/developers/extend/apps/getting-started/scaffolding.mdx b/packages/twenty-docs/l/cs/developers/extend/apps/getting-started/scaffolding.mdx index 204631717d..44752625b7 100644 --- a/packages/twenty-docs/l/cs/developers/extend/apps/getting-started/scaffolding.mdx +++ b/packages/twenty-docs/l/cs/developers/extend/apps/getting-started/scaffolding.mdx @@ -34,6 +34,10 @@ yarn twenty dev:add frontComponent | Zobrazení | `yarn twenty dev:add view` | `src/views/\.ts` | | Položka navigační nabídky | `yarn twenty dev:add navigationMenuItem` | `src/navigation-menu-items/\.ts` | | Rozvržení stránky | `yarn twenty dev:add pageLayout` | `src/page-layouts/\.ts` | +| Karta Rozložení stránky | `yarn twenty dev:add pageLayoutTab` | `src/page-layout-tabs/\.ts` | +| Položka menu příkazů | `yarn twenty dev:add commandMenuItem` | `src/command-menu-items/\.ts` | +| Pole zobrazení | `yarn twenty dev:add viewField` | `src/view-fields/\.ts` | +| Poskytovatel připojení | `yarn twenty dev:add connectionProvider` | `src/connection-providers/\.ts` | ## Co generátor vytváří diff --git a/packages/twenty-docs/l/cs/developers/extend/apps/getting-started/troubleshooting.mdx b/packages/twenty-docs/l/cs/developers/extend/apps/getting-started/troubleshooting.mdx index f679deedaa..1b67d5f9cd 100644 --- a/packages/twenty-docs/l/cs/developers/extend/apps/getting-started/troubleshooting.mdx +++ b/packages/twenty-docs/l/cs/developers/extend/apps/getting-started/troubleshooting.mdx @@ -5,10 +5,10 @@ icon: wrench --- * **Chyby Dockeru** — Před spuštěním `yarn twenty docker:start` se ujistěte, že Docker Desktop (nebo démon) běží. Chybová zpráva ukáže správný příkaz pro spuštění pro váš operační systém. -* **Nesprávná verze Node** — Je potřeba 24+. Ověřte pomocí `node -v`. +* **Špatná verze Node** — Potřebujete verzi 24.5+ (`engines.node: ^24.5.0`). Ověřte pomocí `node -v`. * **Chybí Yarn 4** — Spusťte `corepack enable`. * **Rozbité závislosti** — `rm -rf node_modules && yarn install`. * **Chyby `twenty-sdk` po upgradu na v2.8.0** — V 2.8.0 byl přesunut z `dependencies` do `devDependencies`. Viz [Struktura projektu → Závislosti](/l/cs/developers/extend/apps/getting-started/project-structure#dependencies). -* **`twenty build` upozorňuje na `twenty-client-sdk` v sekci `dependencies`** — je poskytován za běhu Twenty, takže by měl být přesunut do `devDependencies` vedle `twenty-sdk`. Viz [Struktura projektu → Závislosti](/l/cs/developers/extend/apps/getting-started/project-structure#dependencies). +* **`twenty dev:build` upozorňuje na `twenty-client-sdk` v sekci `dependencies`** — je poskytován za běhu Twenty, takže by měl být přesunut do `devDependencies` vedle `twenty-sdk`. Viz [Struktura projektu → Závislosti](/l/cs/developers/extend/apps/getting-started/project-structure#dependencies). Zasekli jste se? Zeptejte se na [Discordu Twenty](https://discord.com/channels/1130383047699738754/1130386664812982322). diff --git a/packages/twenty-docs/l/cs/developers/extend/apps/layout/command-menu-items.mdx b/packages/twenty-docs/l/cs/developers/extend/apps/layout/command-menu-items.mdx index feb8ad48cd..1ae9c604a1 100644 --- a/packages/twenty-docs/l/cs/developers/extend/apps/layout/command-menu-items.mdx +++ b/packages/twenty-docs/l/cs/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({ ## Konfigurační pole -| Pole | Povinné | Popis | -| --------------------------------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `universalIdentifier` | Ano | Stabilní jedinečné ID pro příkaz | -| `label` | Ano | Plný popisek zobrazený v příkazovém menu (Cmd+K) | -| `frontComponentUniversalIdentifier` | Ano | `universalIdentifier` frontendové komponenty, kterou tento příkaz otevírá | -| `shortLabel` | Ne | Kratší popisek zobrazený na připnutém tlačítku rychlé akce | -| `icon` | Ne | Název ikony zobrazený vedle popisku (např. `'IconBolt'`, `'IconSend'`) | -| `isPinned` | Ne | Pokud je `true`, zobrazí příkaz jako tlačítko rychlé akce v pravém horním rohu stránky | -| `availabilityType` | Ne | Určuje, kde se příkaz zobrazuje: `'GLOBAL'` (vždy dostupné), `'RECORD_SELECTION'` (pouze když jsou vybrány záznamy) nebo `'FALLBACK'` (zobrazeno, když neodpovídají žádné jiné příkazy) | -| `availabilityObjectUniversalIdentifier` | Ne | Omezí příkaz na stránky konkrétního typu objektu (např. pouze u záznamů Company) | -| `conditionalAvailabilityExpression` | Ne | Logický výraz, který dynamicky řídí viditelnost (viz níže) | +| Pole | Povinné | Popis | +| --------------------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `universalIdentifier` | Ano | Stabilní jedinečné ID pro příkaz | +| `label` | Ano | Plný popisek zobrazený v příkazovém menu (Cmd+K) | +| `frontComponentUniversalIdentifier` | Ano | `universalIdentifier` frontendové komponenty, kterou tento příkaz otevírá | +| `shortLabel` | Ne | Kratší popisek zobrazený na připnutém tlačítku rychlé akce | +| `icon` | Ne | **Zastaralé** — ignorováno ve prospěch ikony aplikace; při nastavení sestavení vypíše varování | +| `isPinned` | Ne | Pokud je `true`, zobrazí příkaz jako tlačítko rychlé akce v pravém horním rohu stránky | +| `availabilityType` | Ne | Určuje, kde se příkaz zobrazuje: `'GLOBAL'` (vždy dostupné), `'GLOBAL_OBJECT_CONTEXT'` (pouze na stránkách s kontextem objektu — indexové a záznamové stránky), `'RECORD_SELECTION'` (pouze když jsou vybrány záznamy) nebo `'FALLBACK'` (zobrazeno, když neodpovídají žádné jiné příkazy) | +| `availabilityObjectUniversalIdentifier` | Ne | Omezí příkaz na stránky konkrétního typu objektu (např. pouze u záznamů Company) | +| `conditionalAvailabilityExpression` | Ne | Logický výraz, který dynamicky řídí viditelnost (viz níže) | ## Příkazy bez rozhraní Položka příkazového menu spárovaná s [front komponentou bez rozhraní](/l/cs/developers/extend/apps/layout/front-components#headless-vs-non-headless) je idiomatický způsob, jak dodat akci na jedno kliknutí — spustit kód, přejít na stránku nebo potvrdit a provést. Stránka Front Components popisuje [SDK Command komponenty](/l/cs/developers/extend/apps/layout/front-components#sdk-command-components) (`Command`, `CommandLink`, `CommandModal`, `CommandOpenSidePanelPage`), které obsluhují pattern akce-a-unmount. -Typický průběh: - -```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, -}); -``` +Typický průběh: bezhlavá komponenta vykreslí `` (viz [úplný příklad](/l/cs/developers/extend/apps/layout/front-components#sdk-command-components)) a položka v nabídce příkazů na ni ukazuje: ```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/cs/developers/extend/apps/layout/front-components.mdx b/packages/twenty-docs/l/cs/developers/extend/apps/layout/front-components.mdx index 182f5289d3..b18447d5c1 100644 --- a/packages/twenty-docs/l/cs/developers/extend/apps/layout/front-components.mdx +++ b/packages/twenty-docs/l/cs/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', }); ``` -Po synchronizaci pomocí `yarn twenty dev` (nebo po jednorázovém spuštění `yarn twenty dev --once`) se rychlá akce zobrazí v pravém horním rohu stránky: +Po synchronizaci pomocí `yarn twenty dev` (nebo po jednorázovém spuštění `yarn twenty apply`) se rychlá akce zobrazí v pravém horním rohu stránky:
Tlačítko rychlé akce v pravém horním rohu @@ -88,11 +87,11 @@ Front-endové komponenty existují ve dvou režimech vykreslování řízených ```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 @@ Protože komponenta vrací `null`, Twenty přeskočí vykreslení kontejneru — Balíček `twenty-sdk` poskytuje čtyři pomocné komponenty Command navržené pro headless front-endové komponenty. Každá komponenta při připojení provede akci, chyby zpracuje zobrazením oznámení ve snackbaru a po dokončení automaticky odpojí front-endovou komponentu. -Importujte je z `twenty-sdk/command`: +Importujte je z `twenty-sdk/front-component`: * **`Command`** — Spustí asynchronní callback přes prop `execute`. * **`CommandLink`** — Naviguje na cestu v aplikaci. Props: `to`, `params`, `queryParams`, `options`. @@ -127,8 +126,8 @@ Zde je kompletní příklad headless front-endové komponenty, která pomocí `C ```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 @@ A příklad s použitím `CommandModal` k vyžádání potvrzení před proveden ```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 komponenty běží v prohlížeči v izolovaném web workeru, zatímco [lo Logická funkce deklarovaná pomocí `httpRouteTriggerSettings` je přes HTTP dostupná na své cestě (route path). Twenty vloží do workeru základní URL, ze které jsou vaše funkce poskytovány, jako `TWENTY_FUNCTIONS_URL` spolu s `TWENTY_APP_ACCESS_TOKEN`, který volání autentizuje. Zatím neexistuje žádný specializovaný klient SDK pro volání vlastních funkcí, takže je volejte pomocí prostého `fetch`: -> **V Twenty Cloud jsou logické funkce spouštěné přes HTTP poskytovány na vyhrazené doméně pro každý workspace** na adrese `https://\.twenty.com\` — právě na tuto adresu `TWENTY_FUNCTIONS_URL` směřuje. Pro externí volající zkopírujte přesnou URL z nastavení funkce **HTTP trigger** nebo z karty **Settings** aplikace. +> **V Twenty Cloud jsou logické funkce spouštěné přes HTTP poskytovány na vyhrazené doméně pro každý workspace** na adrese `https://\.withtwenty.com\` — právě na tuto adresu `TWENTY_FUNCTIONS_URL` směřuje. Pro externí volající zkopírujte přesnou URL z nastavení funkce **HTTP trigger** nebo z karty **Settings** aplikace. Původní funkční trasa `/s/` je **zastaralá** a bude **deaktivována dne 2026-07-24**. Místo toho použijte `TWENTY_FUNCTIONS_URL` (viz výše) a do tohoto data migrujte všechny pevně zakódované adresy URL `/s/`. Trasa `/s/` zůstává k dispozici pro self-hosting. @@ -212,7 +210,7 @@ Headless front komponenta může volání spustit při mountu přes komponentu ` ```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 @@ Uvnitř komponenty použijte hooky SDK pro přístup k aktuálnímu uživateli, 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 @@ Zde je příklad, který používá hostitelské API k zobrazení snackbaru a za ```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({ Použijte `useSelectedRecordIds()` pro zpracování více vybraných záznamů. To je užitečné pro hromadné operace: ```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, - }, +}); +``` + +Zobrazte ji pomocí [položky příkazové nabídky](/l/cs/developers/extend/apps/layout/command-menu-items) omezené na výběry záznamů: + +```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/cs/developers/extend/apps/layout/navigation-menu-items.mdx b/packages/twenty-docs/l/cs/developers/extend/apps/layout/navigation-menu-items.mdx index 13e713a105..3ee81d4350 100644 --- a/packages/twenty-docs/l/cs/developers/extend/apps/layout/navigation-menu-items.mdx +++ b/packages/twenty-docs/l/cs/developers/extend/apps/layout/navigation-menu-items.mdx @@ -35,6 +35,8 @@ export default defineNavigationMenuItem({ * `position` určuje pořadí v postranním panelu. +* Výčet také obsahuje `NavigationMenuItemType.RECORD`, používaný interně pro oblíbené záznamy vytvořené uživateli — nelze jej použít z manifestu aplikace (neexistuje žádné pole, které by odkazovalo na záznam). + * `icon` a `color` jsou volitelné a upravují, jak položka vypadá. * `folderUniversalIdentifier` je k dispozici také na libovolné položce, aby ji bylo možné vložit do nadřazené položky typu `FOLDER`. diff --git a/packages/twenty-docs/l/cs/developers/extend/apps/layout/views.mdx b/packages/twenty-docs/l/cs/developers/extend/apps/layout/views.mdx index 9eb7384258..a9c5219604 100644 --- a/packages/twenty-docs/l/cs/developers/extend/apps/layout/views.mdx +++ b/packages/twenty-docs/l/cs/developers/extend/apps/layout/views.mdx @@ -33,17 +33,32 @@ export default defineView({ ## Hlavní body * `objectUniversalIdentifier` určuje, na který objekt se toto zobrazení vztahuje. Může to být vlastní objekt, který jste definovali, nebo standardní objekt Twenty. -* `key` určuje typ zobrazení — `ViewKey.INDEX` je hlavní seznamové zobrazení pro daný objekt. +* `key: ViewKey.INDEX` označuje zobrazení jako hlavní seznamové zobrazení objektu (to, které se otevře po kliknutí na navigační položku `OBJECT`). * `fields` určuje, které sloupce se zobrazí a v jakém pořadí. Každé pole odkazuje na `fieldMetadataUniversalIdentifier`. -* Pro pokročilé konfigurace můžete také deklarovat `filters`, `filterGroups`, `groups` a `fieldGroups`. +* Pro pokročilé konfigurace můžete také deklarovat `filters`, `filterGroups`, `sorts`, `groups` a `fieldGroups`. * `position` určuje pořadí, pokud pro stejný objekt existuje více zobrazení. +## Volitelné vlastnosti + +| Vlastnost | Hodnoty | Popis | +| ----------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ | +| `type` | `ViewType.TABLE` (výchozí), `ViewType.KANBAN`, `ViewType.CALENDAR` | Jak jsou záznamy uspořádány. (`FIELDS_WIDGET` / `TABLE_WIDGET` také existují, ale jsou používány interně widgety rozvržení stránky.) | +| `visibility` | `ViewVisibility.WORKSPACE` (výchozí), `ViewVisibility.UNLISTED` | Zda je zobrazení uvedeno pro celý workspace nebo skryto ve výběrech. | +| `openRecordIn` | `ViewOpenRecordIn.SIDE_PANEL` (výchozí), `ViewOpenRecordIn.RECORD_PAGE` | Kde se záznam otevře po kliknutí. | +| `sorts` | `{ fieldMetadataUniversalIdentifier, direction: ViewSortDirection.ASC \| DESC }[]` | Výchozí pořadí řazení. | +| `isCompact` | `boolean` | Kompaktní zobrazení řádků. | +| `mainGroupByFieldMetadataUniversalIdentifier` + `shouldHideEmptyGroups` | — | Seskupení záznamů (např. sloupce kanbanu) podle pole. | +| `kanbanAggregateOperation`, `kanbanAggregateOperationFieldMetadataUniversalIdentifier`, `kanbanColumnWidth` | `AggregateOperations.*` | Agregace a velikost sloupců v kanbanu. | +| `calendarLayout`, `calendarFieldMetadataUniversalIdentifier` | `ViewCalendarLayout.DAY` / `WEEK` / `MONTH` | Zobrazení kalendáře: rozvržení a datumové pole, které určuje umístění záznamů. | + +Všechny výše uvedené výčtové typy jsou exportovány z `twenty-sdk/define`. + ## Filtry Zobrazení může být dodáno s předem aplikovanými filtry. Každý filtr má tři souřadnice: **pole**, které se filtruje, **operand** (jak porovnávat) a **hodnotu** (proti čemu porovnávat). Všechny tři musí být v souladu — použití operandu, který se nehodí k typu pole, bude při synchronizaci odmítnuto. ```ts -import { ViewFilterOperand } from 'twenty-shared/types'; +import { ViewFilterOperand } from 'twenty-sdk/define'; filters: [ { diff --git a/packages/twenty-docs/l/cs/developers/extend/apps/logic/logic-functions.mdx b/packages/twenty-docs/l/cs/developers/extend/apps/logic/logic-functions.mdx index 88286fc584..860a81afc3 100644 --- a/packages/twenty-docs/l/cs/developers/extend/apps/logic/logic-functions.mdx +++ b/packages/twenty-docs/l/cs/developers/extend/apps/logic/logic-functions.mdx @@ -51,8 +51,12 @@ export default defineLogicFunction({ ``` Dostupné typy spouštěčů: -* **httpRoute**: Zpřístupní vaši funkci na HTTP cestě a metodě **pod koncovým bodem `/s/`**: -> např. `path: '/post-card/create'` je volatelné na `https://your-twenty-server.com/s/post-card/create` +* **httpRoute**: Expose your function on the HTTP path and method at a **functions base URL** of your workspace — value Twenty injects as `TWENTY_FUNCTIONS_URL` (na dvaceti Cloudu), vyhrazená doména na pracovní plochu): +> např. `path: '/post-card/create'` je volatelné na `https://your-workspace.withtwenty.com/post-card/create` + + +Starší trasa `/s/` prefixu (`https://your-twenty-server.com/s/post-card/create`) je **zastaralá na dvacet Cloud** a bude deaktivována na **2026-07-24**. Zůstává k dispozici pro vlastní hostované a místní instance, které nenastavují izolovanou doménu funkcí – použijte při nastavení `TWENTY_FUNCTIONS_URL`, a přejděte zpět na `\/s/\` v opačném případě. + Chcete-li vyvolat logickou funkci spuštěnou trasou z (bezhlavé) front-endové komponenty, podívejte se na [Volání logické funkce](/l/cs/developers/extend/apps/layout/front-components#calling-a-logic-function). diff --git a/packages/twenty-docs/l/cs/developers/extend/apps/logic/overview.mdx b/packages/twenty-docs/l/cs/developers/extend/apps/logic/overview.mdx index 6a58244f47..eef4f73ac2 100644 --- a/packages/twenty-docs/l/cs/developers/extend/apps/logic/overview.mdx +++ b/packages/twenty-docs/l/cs/developers/extend/apps/logic/overview.mdx @@ -42,7 +42,7 @@ Logická funkce volí jeden nebo více spouštěčů — každá z níže uveden | Spouštěč | Kdy se spouští | Nastavení | | --------------------------- | ----------------------------------------------------------------- | ------------------------------- | -| **HTTP route** | Požadavek dorazí na váš koncový bod `/s/\` | `httpRouteTriggerSettings` | +| **HTTP route** | Požadavek zasáhne veřejnou adresu URL funkce | `httpRouteTriggerSettings` | | **Cron** | CRON výraz se shoduje | `cronTriggerSettings` | | **Událost databáze** | Záznam v pracovním prostoru je vytvořen, aktualizován nebo smazán | `databaseEventTriggerSettings` | | **Nástroj AI** | Funkce Twenty AI se rozhodne zavolat vaši funkci | `toolTriggerSettings` | diff --git a/packages/twenty-docs/l/cs/developers/extend/apps/operations/cli.mdx b/packages/twenty-docs/l/cs/developers/extend/apps/operations/cli.mdx index be09dd9da0..fc99259973 100644 --- a/packages/twenty-docs/l/cs/developers/extend/apps/operations/cli.mdx +++ b/packages/twenty-docs/l/cs/developers/extend/apps/operations/cli.mdx @@ -4,7 +4,25 @@ description: příkazy `yarn twenty` pro spouštění funkcí, streamování log icon: terminal --- -Kromě `dev`, `dev:build`, `dev:add` a `dev:typecheck` poskytuje `yarn twenty` CLI příkazy pro spouštění funkcí, zobrazení logů a správu instalací aplikací. +CLI `yarn twenty` je vaše rozhraní ke všemu, co souvisí s aplikací. Úplný seznam příkazů: + +| Příkaz | K čemu slouží | Zdokumentováno v | +| ----------------------------------------------- | ----------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- | +| `dev` | Sleduje zdrojové soubory a živě synchronizuje změny | [Rychlý start](/l/cs/developers/extend/apps/getting-started/quick-start) | +| `plán` | Náhled změn metadat bez jejich aplikování | [Synchronizace a obnovení](/l/cs/developers/extend/apps/operations/sync-and-recovery#previewing-changes-plan) | +| `použít` | Aplikovat změny metadat po zobrazení plánu | [Synchronizace a obnovení](/l/cs/developers/extend/apps/operations/sync-and-recovery) | +| `dev:build` | Zkompilovat aplikaci a vygenerovat API klienta (`--tarball` pro zabalení do `.tgz`) | [Publikování](/l/cs/developers/extend/apps/operations/publishing) | +| `dev:typecheck` | Spustit kontrolu typů TypeScriptu | [Testování](/l/cs/developers/extend/apps/operations/testing) | +| `dev:add` | Vytvořit základ nového objektu (scaffold) | [Scaffolding](/l/cs/developers/extend/apps/getting-started/scaffolding) | +| `dev:generate-client` | Znovu vygenerovat typovaného API klienta | tato stránka | +| `dev:function:exec` / `dev:function:logs` | Spustit funkce a streamovat jejich logy | tato stránka | +| `dev:translations-extract` | Extrahovat přeložitelné řetězce do katalogů v `locales/` | [Překlady](/l/cs/developers/extend/apps/translations/overview) | +| `dev:catalog-sync` | Spustit synchronizaci katalogu tržiště | [Publikování](/l/cs/developers/extend/apps/operations/publishing#how-marketplace-discovery-works) | +| `app:publish` / `app:install` / `app:uninstall` | Životní cyklus vydání | [Publikování](/l/cs/developers/extend/apps/operations/publishing) a tato stránka | +| `docker:*` | Spravovat kontejner lokálního serveru Twenty | [Lokální server](/l/cs/developers/extend/apps/getting-started/local-server) | +| `remote:*` | Spravovat připojení k serveru | tato stránka | + +Každý příkaz přijímá `-r, --remote \` pro zacílení na konkrétní remote místo výchozího. ## Spouštění funkcí (`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 ``` ## Zobrazení logů funkcí (`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 ``` Vaše přihlašovací údaje jsou uloženy v `~/.twenty/config.json`. diff --git a/packages/twenty-docs/l/cs/developers/extend/apps/operations/publishing.mdx b/packages/twenty-docs/l/cs/developers/extend/apps/operations/publishing.mdx index 7b45b3592f..01cca7069d 100644 --- a/packages/twenty-docs/l/cs/developers/extend/apps/operations/publishing.mdx +++ b/packages/twenty-docs/l/cs/developers/extend/apps/operations/publishing.mdx @@ -229,7 +229,7 @@ yarn twenty dev:catalog-sync # yarn twenty dev:catalog-sync --remote production ``` -Metadata zobrazená v tržišti pocházejí z vaší konfigurace `defineApplication()` — z polí jako `displayName`, `description`, `author`, `category`, `logoUrl`, `screenshots`, `aboutDescription`, `websiteUrl` a `termsUrl`. +Metadata zobrazená v marketplace pochází z vaší konfigurace `defineApplication()` — viz výše [Metadata marketplace](#marketplace-metadata). Pokud vaše aplikace nedefinuje `aboutDescription` v `defineApplication()`, tržiště automaticky použije soubor `README.md` vašeho balíčku z npm jako obsah stránky O aplikaci. To znamená, že můžete spravovat jediný soubor README jak pro npm, tak pro tržiště Twenty. Pokud chcete v tržišti jiný popis, explicitně nastavte `aboutDescription`. diff --git a/packages/twenty-docs/l/cs/developers/extend/apps/operations/sync-and-recovery.mdx b/packages/twenty-docs/l/cs/developers/extend/apps/operations/sync-and-recovery.mdx index 51a927c244..71a011e7f1 100644 --- a/packages/twenty-docs/l/cs/developers/extend/apps/operations/sync-and-recovery.mdx +++ b/packages/twenty-docs/l/cs/developers/extend/apps/operations/sync-and-recovery.mdx @@ -12,16 +12,20 @@ Lokální vývoj aplikací se točí kolem **synchronizace**: CLI znovu sestaví Pro každodenní lokální iteraci téměř vždy chcete `yarn twenty dev`. Nasazování a publikování slouží k vydávání verzí, **ne** pro lokální vývojovou smyčku. -| Chcete… | Příkaz | Poznámky | -| --------------------------------------------------------- | ----------------------------------- | -------------------------------------------------------------------------------------------------------------------- | -| Iterujte lokálně s živou synchronizací | `yarn twenty dev` | Sleduje vaše soubory a při každé změně spustí synchronizaci. | -| Jednorázová synchronizace a ukončení (CI, skripty, hooky) | `yarn twenty dev --once` | Provede jedno sestavení + synchronizaci a skončí. | -| Náhled změn **bez jejich aplikování** | `yarn twenty dev --once --dry-run` | Spočítá a vypíše rozdíly; nic nezapisuje. | -| Odebrat aplikaci z pracovního prostoru | `yarn twenty app:uninstall` | Přidejte `--yes` pro přeskočení výzvy. | -| Odeslat tarball na server | `yarn twenty app:publish --private` | Vyžaduje **přísně vyšší** verzi v `package.json` — viz [Publikování](/l/cs/developers/extend/apps/operations/publishing). | -| Publikovat na marketplace (npm) | `yarn twenty app:publish` | — | -| Nainstalovat / aktualizovat nasazenou verzi | `yarn twenty app:install` | Nainstaluje aktuálně nasazenou verzi. | -| Vymazat lokální server a začít znovu | `yarn twenty docker:reset` | Smaže **všechna** lokální data — krajní řešení. | +| Chcete… | Příkaz | Poznámky | +| --------------------------------------------------------- | ----------------------------------- | --------------------------------------------------------------------------------------------------------------------- | +| Iterujte lokálně s živou synchronizací | `yarn twenty dev` | Sleduje vaše soubory a při každé změně spustí synchronizaci. | +| Jednorázová synchronizace a ukončení (CI, skripty, hooky) | `yarn twenty apply` | Provede jedno sestavení + synchronizaci a skončí. Přidejte `--force`, abyste přeskočili potvrzení destruktivní změny. | +| Náhled změn **bez jejich aplikování** | `yarn twenty plan` | Spočítá a vypíše rozdíly; nic nezapisuje. | +| Odebrat aplikaci z pracovního prostoru | `yarn twenty app:uninstall` | Přidejte `--yes` pro přeskočení výzvy. | +| Odeslat tarball na server | `yarn twenty app:publish --private` | Vyžaduje **přísně vyšší** verzi v `package.json` — viz [Publikování](/l/cs/developers/extend/apps/operations/publishing). | +| Publikovat na marketplace (npm) | `yarn twenty app:publish` | — | +| Nainstalovat / aktualizovat nasazenou verzi | `yarn twenty app:install` | Nainstaluje aktuálně nasazenou verzi. | +| Vymazat lokální server a začít znovu | `yarn twenty docker:reset` | Smaže **všechna** lokální data — krajní řešení. | + + +`yarn twenty dev --once` a `yarn twenty dev --once --dry-run` stále fungují jako zastaralé aliasy pro `yarn twenty apply` a `yarn twenty plan`. + ### Lokální synchronizace nevyžaduje zvýšení verze @@ -29,19 +33,26 @@ Pravidlo striktně rostoucí `version` (`VERSION_ALREADY_EXISTS` při nasazení, ## Čtení výstupu synchronizace -Každá synchronizace vypíše změny metadat, které aplikovala (nebo by aplikovala s `--dry-run`): +Každá synchronizace vypíše změny metadat, které aplikovala (nebo by aplikovala s `plan`), ve stylu Terraformu — jeden blok na entitu s jejími atributy a poté souhrnný řádek: ```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) ``` To je váš první diagnostický nástroj: přesně ukazuje, které objekty, pole a rozložení se změnily, takže si můžete ověřit, že synchronizace udělala to, co jste očekávali, ještě před kontrolou v UI. +Destruktivní změny (`to destroy`) jsou uvedeny s tím, co odstraňují (např. `objectMetadata "auditNote" — drops the table and all its rows`) a vyžadují interaktivní potvrzení, nebo `--force` ve skriptech. + Když synchronizace selže na jedné entitě, chyba uvede problematickou entitu a její `universalIdentifier`, například: ```text @@ -50,39 +61,42 @@ Migration action 'create' for 'fieldMetadata' (universalIdentifier: 2020...4337) Tento identifikátor použijte k nalezení entity ve vašem manifestu (a případně v pracovním prostoru) místo hádání, která je v konfliktu. -## Náhled změn (dry run) +## Náhled změn (plan) -`yarn twenty dev --once --dry-run` sestaví váš manifest, požádá server o migrační plán a vypíše ho — aniž by cokoli aplikoval. Je to bezpečný způsob, jak si předem zodpovědět otázku „co by tato synchronizace změnila?“ ještě předtím, než se k ní zavážete. +`yarn twenty plan` sestaví váš manifest, požádá server o migrační plán a vypíše ho — **aniž by cokoli aplikoval**. Je to bezpečný způsob, jak si předem zodpovědět otázku „co by tato synchronizace změnila?“ ještě předtím, než se k ní zavážete. ```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 ``` -Spuštění nanečisto: +Plán: * **Nic nezapisuje** — žádná migrace metadat, žádná aktualizace záznamu aplikace, žádné změny výchozí role / karty a žádná generace API klienta. * Vrací **stejný diff**, jaký by aplikovala skutečná synchronizace, takže můžete předem zkontrolovat vytvořené / aktualizované / smazané entity. * Je užitečný před rizikovou změnou, při kontrole změny vygenerované pomocí AI nebo ve skriptu, který má selhat, pokud se má provést neočekávaná změna. -Režim dry run zobrazuje pouze náhled změn **metadat** a vyžaduje, aby byla aplikace alespoň jednou synchronizovaná (aby o ní pracovní prostor věděl). Pokud jej spustíte proti aplikaci, která nikdy nebyla synchronizovaná, server ohlásí, že aplikace není nainstalovaná — nejprve jednou spusťte `yarn twenty dev`. +Plán zobrazuje pouze náhled změn **metadat** a vyžaduje, aby byla aplikace alespoň jednou synchronizovaná (aby o ní pracovní prostor věděl). Pokud jej spustíte proti aplikaci, která nikdy nebyla synchronizovaná, server ohlásí, že aplikace není nainstalovaná — nejprve jednou spusťte `yarn twenty dev`. ## Postup obnovy Když lokální metadata vypadají špatně, postupujte v tomto pořadí a zastavte se, jakmile se problém vyřeší. Každý další krok je rušivější než ten předchozí. -1. **Znovu synchronizujte.** Znovu spusťte `yarn twenty dev --once`. Synchronizace jsou idempotentní — znovu spuštěný čistý manifest je bezpečný a často vyřeší přechodný problém. -2. **Prohlédněte si plán.** Spusťte `yarn twenty dev --once --dry-run`, abyste přesně viděli, co chce další synchronizace změnit, aniž by to aplikovala. +1. **Znovu synchronizujte.** Znovu spusťte `yarn twenty apply`. Synchronizace jsou idempotentní — znovu spuštěný čistý manifest je bezpečný a často vyřeší přechodný problém. +2. **Prohlédněte si plán.** Spusťte `yarn twenty plan`, abyste přesně viděli, co chce další synchronizace změnit, aniž by to aplikovala. 3. **Přečtěte si pojmenovanou chybu.** Pokud synchronizace selže, poznamenejte si typ metadat a `universalIdentifier` ve zprávě (viz výše) a tuto entitu najděte ve svém manifestu. Konflikt obvykle ukazuje na duplicitní nebo znovu použitý identifikátor. 4. **Odinstalujte a znovu nainstalujte.** `yarn twenty app:uninstall`, poté znovu synchronizujte (`yarn twenty dev`). Tím znovu vybudujete metadata aplikace z čistého stavu, zatímco zbytek vašeho pracovního prostoru zůstane nedotčený. 5. **Úplný reset (krajní řešení).** `yarn twenty docker:reset`, poté znovu naplňte data a synchronizujte. diff --git a/packages/twenty-docs/l/cs/developers/extend/apps/operations/testing.mdx b/packages/twenty-docs/l/cs/developers/extend/apps/operations/testing.mdx index e9b454f9c1..3c2425c185 100644 --- a/packages/twenty-docs/l/cs/developers/extend/apps/operations/testing.mdx +++ b/packages/twenty-docs/l/cs/developers/extend/apps/operations/testing.mdx @@ -78,6 +78,13 @@ Vytvořte `vitest.config.ts` v kořeni vaší aplikace: 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, }, }, }); ``` -Vytvořte soubor nastavení, který před spuštěním testů ověří dostupnost serveru: +Vytvořte globální soubor pro nastavení, který ověří, že je server dosažitelný, zapíše testovací konfiguraci pro SDK (`~/.twenty/config.test.json`) a před spuštěním testů provede synchronizaci aplikace: -```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 }); +} ``` ## Programová rozhraní SDK Subcesta `twenty-sdk/cli` exportuje funkce, které můžete volat přímo z testovacího kódu: -| Funkce | Popis | -| -------------- | ----------------------------------------------------- | -| `appBuild` | Sestaví aplikaci a volitelně zabalí tarball | -| `appDeploy` | Nahraje tarball na server | -| `appInstall` | Nainstaluje aplikaci do aktivního pracovního prostoru | -| `appUninstall` | Odinstaluje aplikaci z aktivního pracovního prostoru | +| Funkce | Popis | +| -------------- | ------------------------------------------------------------------------------ | +| `appBuild` | Sestaví aplikaci a volitelně zabalí tarball | +| `appDeploy` | Nahraje tarball na server | +| `appDevOnce` | Jednorázově sestaví a synchronizuje aplikaci (stejně jako `yarn twenty apply`) | +| `appInstall` | Nainstaluje aplikaci do aktivního pracovního prostoru | +| `appUninstall` | Odinstaluje aplikaci z aktivního pracovního prostoru | Každá funkce vrací objekt výsledku se `success: boolean` a buď `data`, nebo `error`. @@ -238,64 +253,10 @@ Kontrolu typů můžete spustit i na vaší aplikaci bez spuštění testů: yarn twenty dev:typecheck ``` -Spustí se `tsc --noEmit` a nahlásí se případné chyby typů. +Spustí se `tsc --noEmit` proti souboru `tsconfig.json` vaší aplikace a nahlásí se případné chyby typů. Vygenerované aplikace také obsahují skript `yarn typecheck`, který pokrývá i testovací soubory (`tsconfig.spec.json`). ## CI s GitHub Actions -Generátor kostry vytvoří připravený k použití workflow GitHub Actions v `.github/workflows/ci.yml`. Automaticky spouští integrační testy při každém pushi do `main` a u pull requestů. +Generátor kostry vytvoří připravený k použití workflow v `.github/workflows/ci.yml`. Při každém pushi do `main` a při každém pull requestu spustí efemérní server Twenty v runneru (pomocí akce `twentyhq/twenty/.github/actions/spawn-twenty-app-dev-test`), poté spustí `yarn lint`, `yarn typecheck`, `yarn test:unit` a `yarn test` s `TWENTY_API_URL` / `TWENTY_API_KEY` směřujícími na tento server. Nejsou vyžadována žádná tajemství a verzi serveru můžete připnout pomocí proměnné prostředí `TWENTY_VERSION` na začátku workflow. -Workflow: - -1. Načte váš kód (checkout). -2. Spustí dočasný server Twenty pomocí akce `twentyhq/twenty/.github/actions/spawn-twenty-docker-image` -3. Nainstaluje závislosti pomocí `yarn install --immutable` -4. Spustí `yarn test` s proměnnými `TWENTY_API_URL` a `TWENTY_API_KEY` vloženými z výstupů akce - -```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 }} -``` - -Není potřeba konfigurovat žádné secrets — akce `spawn-twenty-docker-image` spustí dočasný server Twenty přímo v runneru a vypíše podrobnosti připojení. Secret `GITHUB_TOKEN` je poskytován GitHubem automaticky. - -Chcete-li připnout konkrétní verzi Twenty místo `latest`, změňte proměnnou prostředí `TWENTY_VERSION` na začátku workflow. +Úplný postup pro obě workflow generované kostrou (`ci.yml` a nasazovací pipeline `cd.yml`) najdete v části [Publishing → Automated CI/CD](/l/cs/developers/extend/apps/operations/publishing#automated-cicd-scaffolded-workflows). diff --git a/packages/twenty-docs/l/cs/developers/extend/apps/tutorials/document-generator/building-the-ui.mdx b/packages/twenty-docs/l/cs/developers/extend/apps/tutorials/document-generator/building-the-ui.mdx index 482657ae26..cbaedfc639 100644 --- a/packages/twenty-docs/l/cs/developers/extend/apps/tutorials/document-generator/building-the-ui.mdx +++ b/packages/twenty-docs/l/cs/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/cs/developers/extend/apps/tutorials/document-generator/http-routes.mdx b/packages/twenty-docs/l/cs/developers/extend/apps/tutorials/document-generator/http-routes.mdx index 0c067f6253..3c308757df 100644 --- a/packages/twenty-docs/l/cs/developers/extend/apps/tutorials/document-generator/http-routes.mdx +++ b/packages/twenty-docs/l/cs/developers/extend/apps/tutorials/document-generator/http-routes.mdx @@ -9,8 +9,15 @@ Stejný handler může také odpovědět na HTTP požadavky. Přidáme dva trasy * **POST** koncový bod uživatelského rozhraní volá, aby vytvořilo dokument, a * veřejný **GET** koncový bod, který vykresluje dokument jako tiskovou webovou stránku. -Oba použijte `httpRouteTriggerSettings`. Trasy aplikací jsou vedeny pod `/s` na vašem -serveru (např. `http://localhost:2020/s/documents/generate`). +Oba použijte `httpRouteTriggerSettings`. Na místním serveru vývojáře jsou trasy +provozovány pod předponou `/s` (např. `http://localhost:2020/s/documents/generate`). + + +Na dvaceti Cloudu jsou trasy vedeny na doméně funkcí vyhrazených v pracovním prostoru +— URL je dvacet vložena jako `TWENTY_FUNCTIONS_URL`, bez předpony `/s`. Prefix `/s` +je tam zastaralý a zůstává pouze pro samostatně hostované a místní instance. +Viz [Volání logické funkce](/l/cs/developers/extend/apps/layout/front-components#calling-a-logic-function). + ## POST trasa – generovat na požádání diff --git a/packages/twenty-docs/l/cs/developers/extend/apps/tutorials/document-generator/publishing.mdx b/packages/twenty-docs/l/cs/developers/extend/apps/tutorials/document-generator/publishing.mdx index 235017c88c..7ba785b6b5 100644 --- a/packages/twenty-docs/l/cs/developers/extend/apps/tutorials/document-generator/publishing.mdx +++ b/packages/twenty-docs/l/cs/developers/extend/apps/tutorials/document-generator/publishing.mdx @@ -77,11 +77,11 @@ Spustit tytéž brány 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 ``` -Suchý běh vypíše přesně to, co by se změnilo na serveru bez jeho použití – -je dobrá závěrečná kontrola. Viz +Plán vypíše přesně to, co by se na serveru změnilo, aniž by změny provedl — +dobrá závěrečná kontrola. Viz [Testing](/l/cs/developers/extend/apps/operations/testing) a [Synchronizace a obnovy](/l/cs/developers/extend/apps/operations/sync-and-recovery). diff --git a/packages/twenty-docs/l/de/developers/extend/apps/config/install-hooks.mdx b/packages/twenty-docs/l/de/developers/extend/apps/config/install-hooks.mdx index 0de548f328..d1cee9b31d 100644 --- a/packages/twenty-docs/l/de/developers/extend/apps/config/install-hooks.mdx +++ b/packages/twenty-docs/l/de/developers/extend/apps/config/install-hooks.mdx @@ -4,7 +4,7 @@ description: Führen Sie Logik vor oder nach der Installation aus – befüllen icon: wrench --- -Installations-Hooks sind spezielle Logikfunktionen, die während des Installations- oder Upgrade-Lebenszyklus ausgeführt werden. Sie verwenden dieselbe Handler-Laufzeit wie reguläre [Logikfunktionen](/l/de/developers/extend/apps/logic/logic-functions) und erhalten ein `InstallPayload`, werden jedoch mit eigenen Define-Funktionen deklariert – `definePostInstallLogicFunction()` und `definePreInstallLogicFunction()` – und sind vom normalen Trigger-Modell (HTTP, Cron, Datenbankereignisse) getrennt. +Installations-Hooks sind spezielle Logikfunktionen, die während des Installations- oder Upgrade-Lebenszyklus ausgeführt werden. Sie verwenden dieselbe Handler-Laufzeit wie reguläre [Logikfunktionen](/l/de/developers/extend/apps/logic/logic-functions) und erhalten ein `InstallPayload` (`{ previousVersion?: string; newVersion: string }` — `previousVersion` ist bei einer Neuinstallation `undefined`), werden jedoch mit eigenen Define-Funktionen deklariert und befinden sich außerhalb des normalen Trigger-Modells (HTTP, Cron, Datenbankereignisse). Jede App darf **höchstens eine Pre-Install-Funktion** und **höchstens eine Post-Install-Funktion** definieren. Der Manifest-Build schlägt fehl, wenn mehr als eine von beiden erkannt wird. @@ -19,111 +19,59 @@ Jede App darf **höchstens eine Pre-Install-Funktion** und **höchstens eine Pos └─────────────────────────────────────────────────────────────┘ ``` - - +## Auf einen Blick -Eine Post-Install-Funktion wird automatisch ausgeführt, sobald Ihre App die Installation in einem Arbeitsbereich abgeschlossen hat. Der Server führt sie **nach** der Synchronisierung der Metadaten der App und der Generierung des SDK-Clients aus, sodass der Arbeitsbereich vollständig einsatzbereit ist und das neue Schema bereitsteht. Typische Anwendungsfälle umfassen das Befüllen von Standarddaten, das Erstellen anfänglicher Datensätze, das Konfigurieren von Arbeitsbereichseinstellungen oder das Bereitstellen von Ressourcen bei Diensten von Drittanbietern. +| | `definePreInstallLogicFunction` | `definePostInstallLogicFunction` | +| ------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | +| Läufe | Vor der Metadatenmigration — das **bisherige** Schema und die Daten sind noch intakt | Nach der Migration und SDK-Generierung — das **neue** Schema ist aktiv | +| Ausführung | Immer synchron; blockiert die Installation | Standardmäßig asynchron (warteschlangengesteuert, 3 Wiederholungsversuche); synchrones Opt-in über `shouldRunSynchronously: true` | +| Im Fehlerfall | Die Installation wird **abgebrochen**, bevor eine Schemaänderung erfolgt | Asynchron: bis zu 3 Mal erneut ausgeführt. Synchron: Der Aufrufer erhält `POST_INSTALL_ERROR` (Schemaänderungen werden **nicht** zurückgerollt) | +| Typische Verwendung | Daten sichern oder korrigieren, die eine Migration verlieren würde; ein riskantes Upgrade ablehnen, indem ein Fehler geworfen wird | Standarddaten befüllen, den Arbeitsbereich konfigurieren, externe Ressourcen registrieren | -```ts src/logic-functions/post-install.ts -import { definePostInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define'; +**Faustregel:** Standardmäßig Post-Install verwenden. Greifen Sie nur zu Pre-Install, wenn die Migration selbst destruktiv ist und Sie den vorherigen Zustand abfangen müssen, bevor er verloren geht. -const handler = async (payload: InstallPayload): Promise => { - console.log('Post install logic function executed successfully!', payload.previousVersion); -}; +| Sie möchten ... | Verwenden | +| --------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- | +| Daten befüllen, den Arbeitsbereich konfigurieren, externe Ressourcen registrieren | `post-install` | +| Lange laufende Aufgaben, die die Installationsantwort nicht blockieren sollten | `post-install` (standardmäßig asynchroner Modus, mit Worker-Wiederholungsversuchen) | +| Schnelle Einrichtung, auf die sich der Aufrufer unmittelbar nach der Rückkehr der Installationsantwort verlässt | `post-install` mit `shouldRunSynchronously: true` | +| Daten lesen oder sichern, die bei der bevorstehenden Migration verloren gingen | `pre-install` | +| Ein Upgrade ablehnen, das vorhandene Daten beschädigen würde | `pre-install` (`throw` im Handler) | +| Abgleich bei jedem Upgrade | Einer der Hooks mit `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, -}); -``` +## Verhalten, das von beiden Hooks geteilt wird -Sie können die Post-Installationsfunktion auch jederzeit manuell über die CLI ausführen: +* Die Konfiguration ist eine `defineLogicFunction`-Konfiguration ohne die Trigger-Einstellungen, aber mit `shouldRunOnVersionUpgrade`. +* **Wann sie ausgeführt werden**: standardmäßig nur bei Neuinstallationen. Setze `shouldRunOnVersionUpgrade: true`, um sie auch bei Upgrades auszuführen. Verwende `previousVersion` / `newVersion`, um je nach Upgrade-Pfad unterschiedlich zu verzweigen. +* **Idempotenz ist wichtig**: Asynchrones Post-Install kann erneut ausgeführt werden, und beide Hooks werden bei Upgrades erneut ausgeführt, wenn `shouldRunOnVersionUpgrade` aktiviert ist. +* Die übliche Logikfunktions-Umgebung (`APPLICATION_ID`, `APP_ACCESS_TOKEN`, `API_URL`) wird injiziert, sodass du die Twenty-API mit dem Token deiner App aufrufen kannst. +* Der Hook wird zur Build-Zeit automatisch an das Anwendungsmanifest angehängt (`preInstallLogicFunction` / `postInstallLogicFunction`) — in [`defineApplication()`](/l/de/developers/extend/apps/config/application) muss nichts referenziert werden. +* Der Standardwert für `timeoutSeconds` ist 300, um längere Einrichtungsaufgaben wie Daten-Seeding zu ermöglichen. +* **Wird im Dev-Modus nicht ausgeführt**: `yarn twenty dev` überspringt den Installations-Flow und synchronisiert Dateien direkt, sodass Hooks dort nie ausgeführt werden. Führe sie stattdessen manuell aus: ```bash filename="Terminal" yarn twenty dev:function:exec --postInstall -``` - -Hauptpunkte: -* Post-Installationsfunktionen verwenden `definePostInstallLogicFunction()` — eine spezialisierte Variante, die Trigger-Einstellungen (`cronTriggerSettings`, `databaseEventTriggerSettings`, `httpRouteTriggerSettings`, `toolTriggerSettings`, `workflowActionTriggerSettings`) weglässt. -* Der Handler erhält ein `InstallPayload` mit `{ previousVersion?: string; newVersion: string }` — `newVersion` ist die zu installierende Version, und `previousVersion` ist die zuvor installierte Version (oder `undefined` bei einer Neuinstallation). Verwenden Sie diese Werte, um Neuinstallationen von Upgrades zu unterscheiden und versionsspezifische Migrationslogik auszuführen. -* **Wann der Hook ausgeführt wird**: standardmäßig nur bei Neuinstallationen. Übergeben Sie `shouldRunOnVersionUpgrade: true`, wenn er auch beim Upgrade der App von einer vorherigen Version ausgeführt werden soll. Wenn weggelassen, ist das Flag standardmäßig `false` und Upgrades überspringen den Hook. -* **Ausführungsmodell — standardmäßig asynchron, synchron optional**: Das Flag `shouldRunSynchronously` steuert, *wie* Post-Install ausgeführt wird. - * `shouldRunSynchronously: false` *(Standard)* — der Hook wird **in die Nachrichtenwarteschlange eingereiht** mit `retryLimit: 3` und läuft asynchron in einem Worker. Die Installationsantwort kommt zurück, sobald der Job eingereiht ist, sodass ein langsamer oder fehlschlagender Handler den Aufrufer nicht blockiert. Der Worker versucht es bis zu dreimal erneut. **Verwenden Sie dies für lang laufende Jobs** — das Befüllen großer Datensätze, Aufrufe langsamer Drittanbieter-APIs, Bereitstellung externer Ressourcen, alles, was ein vernünftiges HTTP-Antwortfenster überschreiten könnte. - * `shouldRunSynchronously: true` — der Hook wird **inline während des Installationsablaufs** ausgeführt (gleicher Executor wie bei Pre-Install). Die Installationsanforderung blockiert, bis der Handler fertig ist, und wenn er einen Fehler wirft, erhält der Installationsaufrufer einen `POST_INSTALL_ERROR`. Keine automatischen Wiederholungen. **Verwenden Sie dies für schnelle Aufgaben, die vor der Antwort abgeschlossen sein müssen** — z. B. um dem Benutzer einen Validierungsfehler auszugeben oder für eine schnelle Einrichtung, auf die der Client unmittelbar nach der Rückkehr des Installationsaufrufs angewiesen ist. Beachten Sie, dass die Metadatenmigration bereits angewendet wurde, wenn Post-Install läuft, sodass ein Fehler im Synchronmodus die Schemaänderungen **nicht** rückgängig macht — er zeigt lediglich den Fehler an. -* Stellen Sie sicher, dass Ihr Handler idempotent ist. Im asynchronen Modus kann die Warteschlange bis zu dreimal erneut versuchen; in beiden Modi kann der Hook bei Upgrades erneut laufen, wenn `shouldRunOnVersionUpgrade: true`. -* Die Umgebungsvariablen `APPLICATION_ID`, `APP_ACCESS_TOKEN` und `API_URL` sind im Handler verfügbar (wie bei jeder anderen Logikfunktion), sodass Sie die Twenty API mit einem auf Ihre App beschränkten Anwendungszugriffstoken aufrufen können. -* Pro Anwendung ist nur eine Post-Installationsfunktion zulässig. Der Manifest-Build schlägt fehl, wenn mehr als eine erkannt wird. -* Die `universalIdentifier`, `shouldRunOnVersionUpgrade` und `shouldRunSynchronously` der Funktion werden während des Builds automatisch dem Anwendungsmanifest unter dem Feld `postInstallLogicFunction` hinzugefügt – Sie müssen sie in [`defineApplication()`](/l/de/developers/extend/apps/config/application) nicht referenzieren. -* Das standardmäßige Timeout ist auf 300 Sekunden (5 Minuten) festgelegt, um längere Einrichtungsvorgänge wie Daten-Seeding zu ermöglichen. -* **Nicht im Dev-Modus ausgeführt**: Wenn eine App lokal registriert ist (über `yarn twenty dev`), überspringt der Server den Installationsablauf vollständig und synchronisiert Dateien direkt über den CLI-Watcher — daher läuft Post-Install im Dev-Modus nie, unabhängig von `shouldRunSynchronously`. Verwenden Sie `yarn twenty dev:function:exec --postInstall`, um es manuell gegen einen laufenden Arbeitsbereich auszulösen. - - - - -Eine Pre-Install-Funktion wird automatisch während der Installation ausgeführt, **bevor die Metadatenmigration des Arbeitsbereichs angewendet wird**. Sie hat die gleiche Payload-Struktur wie Post-Install (`InstallPayload`), ist aber früher im Installationsablauf positioniert, sodass sie Zustände vorbereiten kann, von denen die bevorstehende Migration abhängt — typische Anwendungsfälle sind das Sichern von Daten, die Validierung der Kompatibilität mit dem neuen Schema oder das Archivieren von Datensätzen, die umstrukturiert oder entfernt werden sollen. - -```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, -}); -``` - -Sie können die Pre-Installationsfunktion auch jederzeit manuell über die CLI ausführen: - -```bash filename="Terminal" yarn twenty dev:function:exec --preInstall ``` -Hauptpunkte: -* Pre-Install-Funktionen verwenden `definePreInstallLogicFunction()` — dieselbe spezialisierte Konfiguration wie bei Post-Install, nur an einen anderen Lifecycle-Slot gebunden. -* Sowohl Pre- als auch Post-Install-Handler erhalten denselben `InstallPayload`-Typ: `{ previousVersion?: string; newVersion: string }`. Importieren Sie ihn einmal und verwenden Sie ihn für beide Hooks wieder. -* **Wann der Hook ausgeführt wird**: positioniert direkt vor der Metadatenmigration des Arbeitsbereichs (`synchronizeFromManifest`). Vor der Ausführung führt der Server einen rein additiven "pared-down sync" durch, der die Pre-Install-Funktion der **neuen** Version in den Metadaten des Arbeitsbereichs registriert — sonst wird nichts angefasst — und führt sie dann aus. Da dieser Sync nur additiv ist, sind die Objekte, Felder und Daten der vorherigen Version noch intakt, wenn Ihr Handler läuft: Sie können den Zustand vor der Migration gefahrlos lesen und sichern. -* **Ausführungsmodell**: Pre-Install wird **synchron** ausgeführt und **blockiert die Installation**. Wenn der Handler einen Fehler wirft, wird die Installation abgebrochen, bevor Schemaänderungen angewendet werden — der Arbeitsbereich verbleibt in der vorherigen Version in einem konsistenten Zustand. Das ist beabsichtigt: Pre-Install ist Ihre letzte Chance, ein riskantes Upgrade abzulehnen. -* Wie bei Post-Install ist pro Anwendung nur eine Pre-Installationsfunktion zulässig. Sie wird während des Builds automatisch dem Anwendungsmanifest unter `preInstallLogicFunction` hinzugefügt. -* **Nicht im Dev-Modus ausgeführt**: wie bei Post-Install — der Installationsablauf wird für lokal registrierte Apps vollständig übersprungen, daher läuft Pre-Install unter `yarn twenty dev` nie. Verwenden Sie `yarn twenty dev:function:exec --preInstall`, um es manuell auszulösen. + + - - - -Beide Hooks sind Teil desselben Installationsablaufs und erhalten dasselbe `InstallPayload`. Der Unterschied besteht darin, **wann** sie relativ zur Metadatenmigration des Workspaces ausgeführt werden, und das ändert, auf welche Daten sie gefahrlos zugreifen können. - -Pre-Install ist immer **synchron** (blockiert die Installation und kann sie abbrechen). Post-Install ist **standardmäßig asynchron** — in einen Worker eingereiht mit automatischen Wiederholungen — kann aber per `shouldRunSynchronously: true` in die synchrone Ausführung wechseln. Siehe das Akkordeon zu `definePostInstallLogicFunction` oben, wann welcher Modus zu verwenden ist. - -**Verwenden Sie `post-install` für alles, wofür das neue Schema existieren muss.** Dies ist der Regelfall: - -* Standarddaten befüllen (Anlegen anfänglicher Datensätze, Standardansichten, Demo-Inhalte) für neu hinzugefügte Objekte und Felder. -* Registrieren von Webhooks bei Drittanbieter-Diensten, jetzt, da die App ihre Anmeldedaten hat. -* Aufrufen Ihrer eigenen API, um eine Einrichtung abzuschließen, die von den synchronisierten Metadaten abhängt. -* Idempotente "Stelle sicher, dass dies existiert"-Logik, die bei jedem Upgrade den Zustand abgleichen soll — kombinieren Sie dies mit `shouldRunOnVersionUpgrade: true`. - -Beispiel — nach der Installation einen Standard-`PostCard`-Datensatz anlegen: +Wird ausgeführt, nachdem deine App die Installation abgeschlossen hat: Metadaten synchronisiert, SDK-Client generiert, neues Schema abfragbar. Beispiel — bei Neuinstallationen einen Standarddatensatz anlegen: ```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, }); ``` -**Verwenden Sie `pre-install`, wenn eine Migration ansonsten vorhandene Daten löschen oder beschädigen würde.** Da Pre-Install gegen das vorherige Schema läuft und ein Fehlschlag das Upgrade zurückrollt, ist es der richtige Ort für alles Riskante: +Das Flag `shouldRunSynchronously` steuert das Ausführungsmodell: -* **Sichern von Daten, die gleich gelöscht oder umstrukturiert werden** — z. B. Sie entfernen in v2 ein Feld und müssen dessen Werte vor der Migration in ein anderes Feld kopieren oder in einen Speicher exportieren. -* **Archivieren von Datensätzen, die eine neue Einschränkung ungültig machen würde** — z. B. ein Feld wird `NOT NULL` und Sie müssen zuerst Zeilen mit Null-Werten löschen oder korrigieren. -* **Kompatibilität validieren und das Upgrade ablehnen, wenn die aktuellen Daten nicht sauber migriert werden können** — werfen Sie im Handler einen Fehler, und die Installation wird ohne Änderungen abgebrochen. Das ist sicherer, als die Inkompatibilität mitten in der Migration zu entdecken. -* **Daten umbenennen oder Schlüssel neu zuweisen** vor einer Schemaänderung, bei der sonst die Zuordnung verloren ginge. +* `false` *(Standard)* — in die Nachrichtenwarteschlange eingereiht (`retryLimit: 3`) und von einem Worker ausgeführt. Die Installationsantwort wird zurückgegeben, sobald der Job in die Warteschlange eingereiht wurde. **Für lange laufende Aufgaben verwenden** — das Befüllen großer Datensätze, langsame Drittanbieter-APIs. +* `true` — wird inline während des Installations-Flows ausgeführt. Die Installationsanforderung blockiert, bis der Handler fertig ist; ein geworfener Fehler erscheint als `POST_INSTALL_ERROR` beim Aufrufer (keine Wiederholungsversuche). **Für schnelle Aufgaben verwenden, die unbedingt vor der Antwort abgeschlossen sein müssen.** Die Migration wurde zu diesem Zeitpunkt bereits angewendet, daher werden Schemaänderungen bei einem Fehler nicht zurückgerollt — es wird nur der Fehler nach außen gegeben. -Beispiel — Datensätze vor einer destruktiven Migration archivieren: + + + +Wird vor der Metadatenmigration gegen das **bisherige** Schema ausgeführt — die richtige Stelle, um Daten zu sichern, die eine Migration verlieren würde, oder um ein riskantes Upgrade abzulehnen. Vor der Ausführung führt der Server einen rein additiven „pared-down sync“ durch, der nur die Pre-Install-Funktion der neuen Version registriert; alles andere — Objekte, Felder und Daten der vorherigen Version — bleibt unangetastet, wenn dein Handler ausgeführt wird. + +Pre-Install ist immer **synchron** und blockiert die Installation. Wenn der Handler einen Fehler wirft, wird die Installation abgebrochen, bevor eine Schemaänderung erfolgt — der Arbeitsbereich verbleibt in der vorherigen Version in einem konsistenten Zustand. Das ist beabsichtigt: Pre-Install ist Ihre letzte Chance, ein riskantes Upgrade abzulehnen. + +Beispiel — die Werte eines Legacy-Feldes kopieren, bevor die Migration es entfernt: ```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({ }); ``` -**Faustregel:** - -| Sie möchten ... | Verwenden | -| ------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- | -| Standarddaten befüllen, den Arbeitsbereich konfigurieren, externe Ressourcen registrieren | `post-install` | -| Lang laufendes Seeding oder Drittanbieteraufrufe ausführen, die die Installationsantwort nicht blockieren sollten | `post-install` (Standard — `shouldRunSynchronously: false`, mit Worker-Wiederholungen) | -| Schnelle Einrichtung ausführen, auf die sich der Aufrufer unmittelbar nach der Rückkehr des Installationsaufrufs verlassen wird | `post-install` mit `shouldRunSynchronously: true` | -| Daten lesen oder sichern, die bei der bevorstehenden Migration verloren gingen | `pre-install` | -| Ein Upgrade ablehnen, das vorhandene Daten beschädigen würde | `pre-install` (`throw` im Handler) | -| Bei jedem Upgrade einen Abgleich ausführen | `post-install` mit `shouldRunOnVersionUpgrade: true` | -| Einmalige Einrichtung nur bei der ersten Installation durchführen | `post-install` mit `shouldRunOnVersionUpgrade: false` (Standard) | - - -Im Zweifel auf **Post-Install** setzen. Greifen Sie nur zu Pre-Install, wenn die Migration selbst destruktiv ist und Sie den vorherigen Zustand abfangen müssen, bevor er verloren geht. - - diff --git a/packages/twenty-docs/l/de/developers/extend/apps/data/objects.mdx b/packages/twenty-docs/l/de/developers/extend/apps/data/objects.mdx index 0a5de81c57..be6b7a4332 100644 --- a/packages/twenty-docs/l/de/developers/extend/apps/data/objects.mdx +++ b/packages/twenty-docs/l/de/developers/extend/apps/data/objects.mdx @@ -86,6 +86,22 @@ export default defineObject({ **Basisfelder werden automatisch hinzugefügt.** Wenn Sie ein benutzerdefiniertes Objekt definieren, erstellt Twenty Standardfelder wie `id`, `name`, `createdAt`, `updatedAt`, `createdBy`, `updatedBy` und `deletedAt` für Sie. Sie müssen diese nicht in Ihrem `fields`-Array deklarieren – nur Ihre benutzerdefinierten Felder. Sie können ein Standardfeld überschreiben, indem Sie eines mit demselben Namen deklarieren, aber das ist nur selten eine gute Idee. +## Feldtypen + +Die vollständige Menge von `FieldType`-Werten, exportiert aus `twenty-sdk/define`: + +| Kategorie | Typen | +| ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | +| Text | `TEXT`, `RICH_TEXT`, `ARRAY` (von Zeichenketten), `RAW_JSON` | +| Numerisch | `NUMBER` (`universalSettings.dataType`: `'float'` / `'int'` / `'bigint'`), `NUMERIC` (beliebige Genauigkeit), `RATING`, `POSITION` | +| Daten | `DATE`, `DATE_TIME` | +| Auswahl | `BOOLEAN`, `SELECT`, `MULTI_SELECT` | +| Zusammengesetzt | `FULL_NAME`, `ADDRESS`, `EMAILS`, `PHONES`, `LINKS`, `CURRENCY`, `ACTOR`, `FILES` | +| Bezeichner & Relationen | `UUID`, `RELATION`, `MORPH_RELATION` (siehe [Relationen](/l/de/developers/extend/apps/data/relations)) | +| System | `TS_VECTOR` (Volltext-Suchvektor, vom Server verwaltet) | + +Zusammengesetzte Typen speichern mehrere Unterfelder (z. B. `FULL_NAME` = Vorname + Nachname; `CURRENCY` = `amountMicros` + `currencyCode`). `SELECT` und `MULTI_SELECT` erfordern ein `options`-Array wie im obigen Beispiel. + ## Standardwerte Wörtliche Zeichenfolgen-Standardwerte müssen in einfache Anführungszeichen **innerhalb** der Zeichenfolge eingeschlossen werden — `defaultValue: "'Draft'"`, nicht `defaultValue: "Draft"`. Deshalb verwendet das `status`-Feld oben `` `'${PostCardStatus.DRAFT}'` ``. diff --git a/packages/twenty-docs/l/de/developers/extend/apps/getting-started/project-structure.mdx b/packages/twenty-docs/l/de/developers/extend/apps/getting-started/project-structure.mdx index 608bd18147..7fba3dcf7a 100644 --- a/packages/twenty-docs/l/de/developers/extend/apps/getting-started/project-structure.mdx +++ b/packages/twenty-docs/l/de/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 ``` ## Wichtige Dateien -| Datei / Ordner | Zweck | -| ---------------------------------------- | ------------------------------------------------------------------------------- | -| `src/application-config.ts` | **Erforderlich.** Die Hauptkonfigurationsdatei für Ihre App. | -| `src/default-role.ts` | Standardrolle, die steuert, worauf Ihre Logikfunktionen zugreifen können. | -| `src/constants/universal-identifiers.ts` | Automatisch erzeugte UUIDs und Metadaten (Anzeigename, Beschreibung). | -| `src/__tests__/` | Integrationstests (Setup + Beispieltest). | -| `public/` | Statische Assets (Bilder, Schriftarten), die mit Ihrer App ausgeliefert werden. | +| Datei / Ordner | Zweck | +| -------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `src/application-config.ts` | **Erforderlich.** Die Hauptkonfigurationsdatei für Ihre App. | +| `src/default-role.ts` | Standardrolle, die steuert, worauf Ihre Logikfunktionen zugreifen können. | +| `src/constants/universal-identifiers.ts` | Automatisch erzeugte UUIDs und Metadaten (Anzeigename, Beschreibung). | +| `src/front-components/`, `src/navigation-menu-items/`, `src/page-layouts/` | Eine Willkommens-Startseite: eine Front-Komponente, die von einem eigenständigen Seitenlayout gerendert wird und über die Seitenleiste erreichbar ist. | +| `src/__tests__/` | Ein Komponententest plus ein Integrationstest (mit seinem globalen Setup), der die App mit einem echten Server synchronisiert. | +| `public/` | Statische Assets (Bilder, Schriftarten), die mit Ihrer App ausgeliefert werden. | +| `AGENTS.md` / `CLAUDE.md` | Anleitung für KI-Coding-Agents, die an der App arbeiten. | **Die Dateiorganisation liegt bei Ihnen.** Die oben genannten Ordner sind Konventionen – das SDK erkennt Entitäten über eine AST-Analyse von `export default defineEntity(...)`-Aufrufen, unabhängig davon, wo sich die Datei befindet. @@ -47,15 +60,18 @@ Beide Twenty-SDK-Pakete gehören unter `devDependencies`, nicht unter `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" } } ``` +Das Scaffolding-Tool fixiert `twenty-sdk` und `twenty-client-sdk` auf seine eigene Version – halte beide beim Aktualisieren synchron. + * **`twenty-sdk`** stellt die `twenty`-CLI sowie die Build-/Scaffolding-Tools bereit. Es läuft nur während der Entwicklung und beim Build und wird zur Laufzeit der veröffentlichten App niemals importiert. * **`twenty-client-sdk`** *wird* hingegen von deinem App-Code importiert (`CoreApiClient`, `MetadataApiClient`, `RestApiClient`), aber Twenty stellt es zur Laufzeit bereit – Logikfunktionen beziehen es aus einer generierten SDK-Schicht, und Frontend-Komponenten lösen es aus serverseitig ausgelieferten Modulen auf. Deine installierte Kopie wird nur für die Typprüfung und den Build zum Zeitpunkt des Deployments verwendet, daher muss sie niemals im ausgelieferten Bundle enthalten sein. -Wenn eines der Pakete unter `dependencies` bleibt, wird es in das Runtime-Bundle der installierten App gezogen, wo es nur Ballast ist. `twenty build` gibt eine Warnung aus, wenn eines von beiden weiterhin unter `dependencies` aufgeführt ist. +Wenn eines der Pakete unter `dependencies` bleibt, wird es in das Runtime-Bundle der installierten App gezogen, wo es nur Ballast ist. `twenty dev:build` gibt eine Warnung aus, wenn eines von beiden weiterhin unter `dependencies` aufgeführt ist. Füge die eigenen Runtime-Abhängigkeiten deiner App (Bibliotheken, die deine Logikfunktionen zur Laufzeit tatsächlich importieren) wie gewohnt unter `dependencies` hinzu. diff --git a/packages/twenty-docs/l/de/developers/extend/apps/getting-started/quick-start.mdx b/packages/twenty-docs/l/de/developers/extend/apps/getting-started/quick-start.mdx index 75ec9ce377..5491b4241f 100644 --- a/packages/twenty-docs/l/de/developers/extend/apps/getting-started/quick-start.mdx +++ b/packages/twenty-docs/l/de/developers/extend/apps/getting-started/quick-start.mdx @@ -6,17 +6,17 @@ description: Erstellen Sie in wenigen Minuten Ihre erste Twenty-App. ## Voraussetzungen -* **Node.js 24+** — [Hier herunterladen](https://nodejs.org/) +* **Node.js 24.5+** — [Hier herunterladen](https://nodejs.org/) * **Yarn 4** — Wird mit Node.js über Corepack mitgeliefert. Aktivieren Sie es: `corepack enable` * **Docker** — [Hier herunterladen](https://www.docker.com/products/docker-desktop/). Erforderlich, um einen lokalen Twenty-Server auszuführen. Überspringen Sie dies, wenn Twenty bereits anderswo läuft. Das Erstellen einer Twenty-App umfasst drei Phasen. Das Scaffolding-Tool fasst sie zu einem einzigen Happy-Path-Befehl zusammen, aber jede Phase ist ein eigenes Konzept — wenn etwas fehlschlägt, hilft Ihnen das Wissen, in welcher Phase Sie sich befinden, zu erkennen, was zu beheben ist. -| Phase | Was Sie tun | Tool | Ergebnis | -| ----------------------- | ------------------------------------------------------- | ----------------------------- | ---------------------------------------------------- | -| **1. Gerüst erstellen** | Den Quellcode der App erzeugen | `npx create-twenty-app` | Ein TypeScript-Projekt auf der Festplatte | -| **2. Server starten** | Einen Twenty-Server starten, in den synchronisiert wird | Docker + `yarn twenty server` | Eine laufende Twenty-Instanz | -| **3. Synchronisieren** | Ihren Code live mit dem Server synchronisieren | `yarn twenty dev` | Ihre Änderungen erscheinen in der Benutzeroberfläche | +| Phase | Was Sie tun | Tool | Ergebnis | +| ----------------------- | ------------------------------------------------------- | ----------------------------------- | ---------------------------------------------------- | +| **1. Gerüst erstellen** | Den Quellcode der App erzeugen | `npx create-twenty-app` | Ein TypeScript-Projekt auf der Festplatte | +| **2. Server starten** | Einen Twenty-Server starten, in den synchronisiert wird | Docker + `yarn twenty docker:start` | Eine laufende Twenty-Instanz | +| **3. Synchronisieren** | Ihren Code live mit dem Server synchronisieren | `yarn twenty dev` | Ihre Änderungen erscheinen in der Benutzeroberfläche | --- @@ -28,7 +28,7 @@ Erstellen Sie eine neue App aus der Vorlage: npx create-twenty-app@latest my-twenty-app ``` -Sie werden nach einem Namen und einer Beschreibung gefragt — drücken Sie **Enter** für die Standardwerte. Dadurch wird ein TypeScript-Projekt in `my-twenty-app/` erzeugt, mit einer Startdatei `application-config.ts`, einer Standardrolle, einem CI-Workflow und einem Integrationstest. +Das Scaffolding-Tool ist nicht interaktiv: Der Verzeichnisname wird zum App-Namen. Übergeben Sie `--display-name` und `--description`, um die erzeugten Metadaten anzupassen (Sie können sie später auch in `src/constants/universal-identifiers.ts` bearbeiten). Dadurch wird ein TypeScript-Projekt in `my-twenty-app/` erzeugt, mit einer Startdatei `application-config.ts`, einer Standardrolle, CI/CD-Workflows und einem Integrationstest. **Nach dieser Phase:** Sie haben den Quellcode einer App auf Ihrem Rechner. Es läuft noch nicht — das ist Phase 2. @@ -38,28 +38,14 @@ Sie werden nach einem Namen und einer Beschreibung gefragt — drücken Sie **En Ihre App benötigt einen Twenty-Server, in den sie synchronisieren kann. Der Server ist eine vollständige Twenty-Instanz — UI, GraphQL-API, PostgreSQL — die lokal in Docker läuft. Ihr lokaler Code lädt seine Definitionen auf diesen Server hoch, wodurch sie in der Benutzeroberfläche erscheinen. -Das Scaffolding-Tool bietet an, einen für Sie zu starten: +Der Scaffolder startet eine Instanz für Sie: Bei laufendem Docker zieht er das `twentycrm/twenty-app-dev`-Image, startet es auf Port `2020` und authentifiziert die CLI für den vorbefüllten Demo-Workspace (`tim@apple.dev`) – keine Anmeldung erforderlich. -> **Möchten Sie eine lokale Twenty-Instanz einrichten?** - -* **Ja (empfohlen)** — lädt das Docker-Image `twentycrm/twenty-app-dev` herunter und startet es auf Port `2020`. Stellen Sie sicher, dass Docker läuft. -* **Nein** — wählen Sie dies, wenn Sie bereits einen Twenty-Server haben, mit dem Sie sich verbinden möchten. Sie können die Verbindung später mit `yarn twenty remote:add` herstellen. - -
- Soll die lokale Instanz gestartet werden? -
- -Sobald der Server läuft, öffnet sich ein Browser zur Anmeldung. Verwenden Sie das vorab eingerichtete Demo-Konto: - -* **E-Mail:** `tim@apple.dev` -* **Passwort:** `tim@apple.dev` +Um stattdessen eine Verbindung zu einem bestehenden Twenty-Server herzustellen, übergeben Sie `--url \`. Remote-Server authentifizieren sich mit OAuth: Ein Browser öffnet sich, damit Sie sich anmelden und auf **Authorize** klicken können, wodurch die CLI Zugriff auf Ihren Workspace erhält. (Sie können lokal auch OAuth aktivieren mit `--authentication-method oauth` – melden Sie sich mit `tim@apple.dev` / `tim@apple.dev` an.)
Twenty-Anmeldebildschirm
-Klicken Sie auf dem nächsten Bildschirm auf **Authorize** — dadurch erhält die CLI Zugriff auf Ihren Arbeitsbereich. -
Twenty-CLI-Autorisierungsbildschirm
@@ -117,28 +103,32 @@ Klicken Sie auf **View installed app**, um die Installation im Arbeitsbereich an ### Einmalige Synchronisierung für CI und Skripte -Verwenden Sie `--once`, um einen einzelnen Build + Sync auszuführen und zu beenden — gleiche Pipeline, kein Watcher: +Verwenden Sie `plan` und `apply`, um dieselbe Pipeline einmalig ohne Watcher auszuführen: ```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 ``` -| Befehl | Verhalten | Wann verwenden | -| ---------------------------------- | ---------------------------------------------------------------------------------- | ---------------------------------------------------------------------------- | -| `yarn twenty dev` | Überwacht und synchronisiert bei jeder Änderung erneut. Läuft, bis Sie es stoppen. | Interaktive lokale Entwicklung. | -| `yarn twenty dev --once` | Einmaliger Build + Sync, beendet sich mit `0` bei Erfolg, mit `1` bei Fehler. | CI, Pre-Commit-Hooks, KI-Agenten, skriptgesteuerte Workflows. | -| `yarn twenty dev --once --dry-run` | Erstellt und gibt die Metadatenänderungen aus **ohne sie anzuwenden**. | Prüfen Sie, was eine Synchronisierung ändern würde, bevor Sie sie ausführen. | +| Befehl | Verhalten | Wann verwenden | +| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------- | +| `yarn twenty dev` | Überwacht und synchronisiert bei jeder Änderung erneut. Läuft, bis Sie es stoppen. | Interaktive lokale Entwicklung. | +| `yarn twenty apply` | Einmaliger Build + Sync, beendet sich mit `0` bei Erfolg, mit `1` bei Fehler. Fragt bei destruktiven Änderungen nach einer Bestätigung (übergeben Sie `--force`, um dies zu überspringen). | CI, Pre-Commit-Hooks, KI-Agenten, skriptgesteuerte Workflows. | +| `yarn twenty plan` | Erstellt und gibt die Metadatenänderungen aus **ohne sie anzuwenden**. | Prüfen Sie, was eine Synchronisierung ändern würde, bevor Sie sie ausführen. | -Beide Modi benötigen ein authentifiziertes Remote-Repository. Weitere Informationen zu `--dry-run` finden Sie unter [Synchronisierung & Wiederherstellung](/l/de/developers/extend/apps/operations/sync-and-recovery#previewing-changes-dry-run). +Alle Modi benötigen ein authentifiziertes Remote-Repository. Weitere Informationen zu `plan` finden Sie unter [Synchronisierung & Wiederherstellung](/l/de/developers/extend/apps/operations/sync-and-recovery#previewing-changes-plan). + + +`yarn twenty dev --once` und `yarn twenty dev --once --dry-run` sind veraltete Aliasse für `yarn twenty apply` und `yarn twenty plan`. + ### Dev-Modus-Optionen -| Flag | Beschreibung | -| ------------------------------------- | ----------------------------------------------------------------------------------------------- | -| `--once` | Einmal erstellen und synchronisieren, dann beenden. | -| `--dry-run` | Mit `--once` können Sie die Metadatenänderungen anzeigen, ohne sie anzuwenden. Schreibt nichts. | -| `--debounceMs \` | Legt die Entprellzeit für Dateiänderungen in Millisekunden fest (Standard: `2000`). | -| `--verbose` / `--debug` | Zeigt ausführliche Build-Protokolle, Sync-Anfragen und Fehler-Traces an. | +| Flag | Beschreibung | +| ------------------------------------- | ----------------------------------------------------------------------------------- | +| `--force` | Wendet destruktive Änderungen (Löschungen) ohne Bestätigung an. | +| `--debounceMs \` | Legt die Entprellzeit für Dateiänderungen in Millisekunden fest (Standard: `1000`). | +| `--verbose` / `--debug` | Zeigt ausführliche Build-Protokolle, Sync-Anfragen und Fehler-Traces an. | ## Was Sie erstellen können diff --git a/packages/twenty-docs/l/de/developers/extend/apps/getting-started/scaffolding.mdx b/packages/twenty-docs/l/de/developers/extend/apps/getting-started/scaffolding.mdx index 75cfad227d..0d946073fd 100644 --- a/packages/twenty-docs/l/de/developers/extend/apps/getting-started/scaffolding.mdx +++ b/packages/twenty-docs/l/de/developers/extend/apps/getting-started/scaffolding.mdx @@ -22,18 +22,22 @@ yarn twenty dev:add frontComponent ## Verfügbare Entitätstypen -| Entitätstyp | Befehl | Generierte Datei | -| ---------------------- | ---------------------------------------- | ------------------------------------------------------- | -| Objekt | `yarn twenty dev:add object` | `src/objects/\.ts` | -| Feld | `yarn twenty dev:add field` | `src/fields/\.ts` | -| Logikfunktion | `yarn twenty dev:add logicFunction` | `src/logic-functions/\.ts` | -| Frontend-Komponente | `yarn twenty dev:add frontComponent` | `src/front-components/\.tsx` | -| Rolle | `yarn twenty dev:add role` | `src/roles/\.ts` | -| Skill | `yarn twenty dev:add skill` | `src/skills/\.ts` | -| Agent | `yarn twenty dev:add agent` | `src/agents/\.ts` | -| Ansicht | `yarn twenty dev:add view` | `src/views/\.ts` | -| Navigationsmenüeintrag | `yarn twenty dev:add navigationMenuItem` | `src/navigation-menu-items/\.ts` | -| Seitenlayout | `yarn twenty dev:add pageLayout` | `src/page-layouts/\.ts` | +| Entitätstyp | Befehl | Generierte Datei | +| -------------------------- | ---------------------------------------- | ------------------------------------------------------- | +| Objekt | `yarn twenty dev:add object` | `src/objects/\.ts` | +| Feld | `yarn twenty dev:add field` | `src/fields/\.ts` | +| Logikfunktion | `yarn twenty dev:add logicFunction` | `src/logic-functions/\.ts` | +| Frontend-Komponente | `yarn twenty dev:add frontComponent` | `src/front-components/\.tsx` | +| Rolle | `yarn twenty dev:add role` | `src/roles/\.ts` | +| Skill | `yarn twenty dev:add skill` | `src/skills/\.ts` | +| Agent | `yarn twenty dev:add agent` | `src/agents/\.ts` | +| Ansicht | `yarn twenty dev:add view` | `src/views/\.ts` | +| Navigationsmenüeintrag | `yarn twenty dev:add navigationMenuItem` | `src/navigation-menu-items/\.ts` | +| Seitenlayout | `yarn twenty dev:add pageLayout` | `src/page-layouts/\.ts` | +| Seitenlayout-Registerkarte | `yarn twenty dev:add pageLayoutTab` | `src/page-layout-tabs/\.ts` | +| Befehlsmenü-Eintrag | `yarn twenty dev:add commandMenuItem` | `src/command-menu-items/\.ts` | +| Ansichtsfeld | `yarn twenty dev:add viewField` | `src/view-fields/\.ts` | +| Verbindungsanbieter | `yarn twenty dev:add connectionProvider` | `src/connection-providers/\.ts` | ## Was der Scaffolder generiert diff --git a/packages/twenty-docs/l/de/developers/extend/apps/getting-started/troubleshooting.mdx b/packages/twenty-docs/l/de/developers/extend/apps/getting-started/troubleshooting.mdx index ef6c09557b..4621152086 100644 --- a/packages/twenty-docs/l/de/developers/extend/apps/getting-started/troubleshooting.mdx +++ b/packages/twenty-docs/l/de/developers/extend/apps/getting-started/troubleshooting.mdx @@ -5,10 +5,10 @@ icon: wrench --- * **Docker-Fehler** — Stellen Sie sicher, dass Docker Desktop (oder der Daemon) läuft, bevor Sie `yarn twenty docker:start` ausführen. Die Fehlermeldung zeigt den richtigen Startbefehl für Ihr Betriebssystem an. -* **Falsche Node-Version** — 24+ erforderlich. Prüfen Sie mit `node -v`. +* **Falsche Node-Version** — Es wird 24.5+ benötigt (`engines.node: ^24.5.0`). Prüfen Sie mit `node -v`. * **Yarn 4 fehlt** — Führen Sie `corepack enable` aus. * **Abhängigkeiten defekt** — `rm -rf node_modules && yarn install`. * **`twenty-sdk`-Fehler nach dem Upgrade auf v2.8.0** — es wurde in v2.8.0 von `dependencies` zu `devDependencies` verschoben. Siehe [Projektstruktur → Abhängigkeiten](/l/de/developers/extend/apps/getting-started/project-structure#dependencies). -* **`twenty build` warnt vor `twenty-client-sdk` unter `dependencies`** — Es wird zur Laufzeit von Twenty bereitgestellt, daher sollte es in `devDependencies` neben `twenty-sdk` verschoben werden. Siehe [Projektstruktur → Abhängigkeiten](/l/de/developers/extend/apps/getting-started/project-structure#dependencies). +* **`twenty dev:build` warnt vor `twenty-client-sdk` unter `dependencies`** — Es wird zur Laufzeit von Twenty bereitgestellt, daher sollte es in `devDependencies` neben `twenty-sdk` verschoben werden. Siehe [Projektstruktur → Abhängigkeiten](/l/de/developers/extend/apps/getting-started/project-structure#dependencies). Hängen Sie fest? Bitten Sie im [Twenty-Discord](https://discord.com/channels/1130383047699738754/1130386664812982322) um Hilfe. diff --git a/packages/twenty-docs/l/de/developers/extend/apps/layout/command-menu-items.mdx b/packages/twenty-docs/l/de/developers/extend/apps/layout/command-menu-items.mdx index dc58f2e258..349d497bfb 100644 --- a/packages/twenty-docs/l/de/developers/extend/apps/layout/command-menu-items.mdx +++ b/packages/twenty-docs/l/de/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({ ## Konfigurationsfelder -| Feld | Erforderlich | Beschreibung | -| --------------------------------------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| `universalIdentifier` | Ja | Stabile eindeutige ID für den Befehl | -| `label` | Ja | Vollständiges Label, das im Befehlsmenü (Cmd+K) angezeigt wird | -| `frontComponentUniversalIdentifier` | Ja | Der `universalIdentifier` der Front-Komponente, die dieser Befehl öffnet | -| `shortLabel` | Nein | Kürzeres Label, das auf der angehefteten Schnellaktionsschaltfläche angezeigt wird | -| `icon` | Nein | Neben dem Label angezeigter Icon-Name (z. B. 'IconBolt', 'IconSend') | -| `isPinned` | Nein | Bei `true` wird der Befehl als Schnellaktionsschaltfläche oben rechts auf der Seite angezeigt | -| `availabilityType` | Nein | Steuert, wo der Befehl erscheint: 'GLOBAL' (immer verfügbar), 'RECORD_SELECTION' (nur wenn Datensätze ausgewählt sind) oder 'FALLBACK' (wird angezeigt, wenn keine anderen Befehle passen) | -| `availabilityObjectUniversalIdentifier` | Nein | Beschränken Sie den Befehl auf Seiten eines bestimmten Objekttyps (z. B. nur bei Company-Datensätzen) | -| `conditionalAvailabilityExpression` | Nein | Ein boolescher Ausdruck, der die Sichtbarkeit dynamisch steuert (siehe unten) | +| Feld | Erforderlich | Beschreibung | +| --------------------------------------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `universalIdentifier` | Ja | Stabile eindeutige ID für den Befehl | +| `label` | Ja | Vollständiges Label, das im Befehlsmenü (Cmd+K) angezeigt wird | +| `frontComponentUniversalIdentifier` | Ja | Der `universalIdentifier` der Front-Komponente, die dieser Befehl öffnet | +| `shortLabel` | Nein | Kürzeres Label, das auf der angehefteten Schnellaktionsschaltfläche angezeigt wird | +| `icon` | Nein | **Veraltet** — wird zugunsten des Anwendungssymbols ignoriert; der Build gibt eine Warnung aus, wenn gesetzt | +| `isPinned` | Nein | Bei `true` wird der Befehl als Schnellaktionsschaltfläche oben rechts auf der Seite angezeigt | +| `availabilityType` | Nein | Steuert, wo der Befehl erscheint: `'GLOBAL'` (immer verfügbar), `'GLOBAL_OBJECT_CONTEXT'` (nur auf Seiten mit einem Objektkontext – Index- und Datensatzseiten), `'RECORD_SELECTION'` (nur wenn Datensätze ausgewählt sind) oder `'FALLBACK'` (wird angezeigt, wenn keine anderen Befehle passen) | +| `availabilityObjectUniversalIdentifier` | Nein | Beschränken Sie den Befehl auf Seiten eines bestimmten Objekttyps (z. B. nur bei Company-Datensätzen) | +| `conditionalAvailabilityExpression` | Nein | Ein boolescher Ausdruck, der die Sichtbarkeit dynamisch steuert (siehe unten) | ## Headless-Befehle Ein Befehlsmenü-Eintrag, der mit einer [Headless-Front-Komponente](/l/de/developers/extend/apps/layout/front-components#headless-vs-non-headless) gekoppelt ist, ist die idiomatische Art, eine One-Click-Aktion bereitzustellen – Code ausführen, navigieren oder bestätigen und ausführen. Die Seite „Front Components“ behandelt die [SDK Command-Komponenten](/l/de/developers/extend/apps/layout/front-components#sdk-command-components) (`Command`, `CommandLink`, `CommandModal`, `CommandOpenSidePanelPage`), die das Action-and-Unmount-Muster handhaben. -Ein typischer Ablauf: - -```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, -}); -``` +Ein typischer Ablauf: Eine kopflose Komponente rendert `` (siehe das [vollständige Beispiel](/l/de/developers/extend/apps/layout/front-components#sdk-command-components)), und der Befehl-Menüeintrag verweist darauf: ```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/de/developers/extend/apps/layout/front-components.mdx b/packages/twenty-docs/l/de/developers/extend/apps/layout/front-components.mdx index d817914e91..7c14ff0785 100644 --- a/packages/twenty-docs/l/de/developers/extend/apps/layout/front-components.mdx +++ b/packages/twenty-docs/l/de/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', }); ``` -Nach dem Synchronisieren mit `yarn twenty dev` (oder durch einmaliges Ausführen von `yarn twenty dev --once`) erscheint die Schnellaktion oben rechts auf der Seite: +Nach dem Synchronisieren mit `yarn twenty dev` (oder durch einmaliges Ausführen von `yarn twenty apply`) erscheint die Schnellaktion oben rechts auf der Seite:
Schnellaktionsschaltfläche oben rechts @@ -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`. - -
- ¿Debería iniciar una instancia local? -
- -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`.)
Pantalla de inicio de sesión de Twenty
-Haz clic en **Authorize** en la siguiente pantalla — esto le da a la CLI acceso a tu espacio de trabajo. -
Pantalla de autorización de la CLI de Twenty
@@ -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:
Botón de acción rápida en la esquina superior derecha @@ -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`. - -
- Faut-il démarrer l’instance locale ? -
- -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`.)
Écran de connexion Twenty
-Cliquez sur **Authorize** sur l’écran suivant — cela donne à la CLI l’accès à votre espace de travail. -
Écran d’autorisation de la CLI Twenty
@@ -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 :
Bouton d'action rapide dans le coin supérieur droit @@ -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`. - -
- Avviare l'istanza locale? -
- -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`.)
Schermata di accesso di Twenty
-Fai clic su **Authorize** nella schermata successiva — questo concede alla CLI l'accesso al tuo spazio di lavoro. -
Schermata di autorizzazione della CLI di Twenty
@@ -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:
Pulsante di azione rapida nell'angolo in alto a destra @@ -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` でサインインします。)
Twenty のログイン画面
-次の画面で **Authorize** をクリックします — これにより、CLI にワークスペースへのアクセスが許可されます。 -
Twenty 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`로 로그인하세요.)
Twenty 로그인 화면
-다음 화면에서 **Authorize**를 클릭하세요 — 그러면 CLI가 워크스페이스에 접근할 수 있게 됩니다. -
Twenty 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`. - -
- Deve iniciar instância local? -
- -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`.)
Tela de login do Twenty
-Clique em **Authorize** na próxima tela — isso dá à CLI acesso ao seu espaço de trabalho. -
Tela de autorização da CLI do Twenty
@@ -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:
Botão de ação rápida no canto superior direito @@ -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`. - -
- Porniți instanța locală? -
- -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`.)
Ecranul de autentificare Twenty
-Faceți clic pe **Authorize** pe ecranul următor — aceasta oferă CLI-ului acces la spațiul dvs. de lucru. -
Ecranul de autorizare Twenty CLI
@@ -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 ; -}; - -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 tipic: un component headless redă `` (vezi [exemplul complet](/l/ro/developers/extend/apps/layout/front-components#sdk-command-components)), iar elementul de meniu de comandă îl indică: ```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/ro/developers/extend/apps/layout/front-components.mdx b/packages/twenty-docs/l/ro/developers/extend/apps/layout/front-components.mdx index a3811552fa..7289dfbafb 100644 --- a/packages/twenty-docs/l/ro/developers/extend/apps/layout/front-components.mdx +++ b/packages/twenty-docs/l/ro/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', }); ``` -După sincronizarea cu `yarn twenty dev` (sau prin rularea comenzii `yarn twenty dev --once` o singură dată), acțiunea rapidă apare în colțul din dreapta sus al paginii: +După sincronizarea cu `yarn twenty dev` (sau prin rularea comenzii unice `yarn twenty apply`), acțiunea rapidă apare în colțul din dreapta sus al paginii:
Buton de acțiune rapidă în colțul din dreapta sus @@ -88,11 +87,11 @@ Componentele front-end au două moduri de randare controlate de opțiunea `isHea ```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 @@ Deoarece componenta returnează `null`, Twenty omite redarea unui container pent Pachetul `twenty-sdk` oferă patru componente ajutătoare Command, concepute pentru componente front-end headless. Fiecare componentă execută o acțiune la montare, gestionează erorile afișând o notificare snackbar și demontează automat componenta front-end la final. -Importați-le din `twenty-sdk/command`: +Importați-le din `twenty-sdk/front-component`: * **`Command`** — Rulează un callback asincron prin prop-ul `execute`. * **`CommandLink`** — Navighează către o rută a aplicației. Props: `to`, `params`, `queryParams`, `options`. @@ -127,8 +126,8 @@ Iată un exemplu complet de componentă front-end headless care folosește `Comm ```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 @@ Componentele de front rulează în browser într-un Web Worker izolat, în timp O funcție logică declarată cu `httpRouteTriggerSettings` este accesibilă prin HTTP la ruta sa. Twenty injectează în worker URL-ul de bază de la care sunt deservite funcțiile tale ca `TWENTY_FUNCTIONS_URL`, împreună cu `TWENTY_APP_ACCESS_TOKEN` care autentifică apelul. Nu există încă un client SDK dedicat pentru apelarea propriilor funcții, așa că apelează-le cu un simplu `fetch`: -> **În Twenty Cloud, funcțiile logice declanșate prin HTTP sunt deservite pe un domeniu dedicat pentru fiecare spațiu de lucru** la `https://\.twenty.com\` — acesta este exact URL-ul la care indică `TWENTY_FUNCTIONS_URL`. Pentru apelanții externi, copiază URL-ul exact din setările **HTTP trigger** ale funcției sau din fila **Settings** a aplicației. +> **În Twenty Cloud, funcțiile logice declanșate prin HTTP sunt deservite pe un domeniu dedicat pentru fiecare spațiu de lucru** la `https://\.withtwenty.com\` — acesta este exact URL-ul la care indică `TWENTY_FUNCTIONS_URL`. Pentru apelanții externi, copiază URL-ul exact din setările **HTTP trigger** ale funcției sau din fila **Settings** a aplicației. Ruta veche a funcției `/s/` este **depășită** și va fi **dezactivată la 2026-07-24**. Folosește în schimb `TWENTY_FUNCTIONS_URL` (mai sus) și migrează orice URL-uri `/s/` hard-codate înainte de acea dată. Ruta `/s/` rămâne disponibilă pentru self-hosting. @@ -212,7 +210,7 @@ O componentă de front headless poate efectua apelul la montare prin componenta ```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 @@ Iată un exemplu care folosește API-ul gazdei pentru a afișa un snackbar și a ```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({ Folosiți `useSelectedRecordIds()` pentru a gestiona mai multe înregistrări selectate. Acest lucru este util pentru operațiuni în masă: ```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, - }, +}); +``` + +Afișați-o cu un [element de meniu de comandă](/l/ro/developers/extend/apps/layout/command-menu-items) restricționat la selecțiile de înregistrări: + +```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/ro/developers/extend/apps/layout/navigation-menu-items.mdx b/packages/twenty-docs/l/ro/developers/extend/apps/layout/navigation-menu-items.mdx index 9346bf8f47..55f757f7b4 100644 --- a/packages/twenty-docs/l/ro/developers/extend/apps/layout/navigation-menu-items.mdx +++ b/packages/twenty-docs/l/ro/developers/extend/apps/layout/navigation-menu-items.mdx @@ -35,6 +35,8 @@ export default defineNavigationMenuItem({ * `position` controlează ordonarea în bara laterală. +* Enumul conține și `NavigationMenuItemType.RECORD`, utilizat intern pentru favoritele de înregistrări create de utilizator — nu poate fi folosit dintr-un manifest de aplicație (nu există niciun câmp pentru a face referire la o înregistrare). + * `icon` și `color` sunt opționale și personalizează aspectul intrării. * `folderUniversalIdentifier` este de asemenea disponibil pe orice element pentru a-l îmbrica într-un părinte de tip `FOLDER`. diff --git a/packages/twenty-docs/l/ro/developers/extend/apps/layout/views.mdx b/packages/twenty-docs/l/ro/developers/extend/apps/layout/views.mdx index 5dd734001e..ece80bd74c 100644 --- a/packages/twenty-docs/l/ro/developers/extend/apps/layout/views.mdx +++ b/packages/twenty-docs/l/ro/developers/extend/apps/layout/views.mdx @@ -33,17 +33,32 @@ export default defineView({ ## Puncte cheie * `objectUniversalIdentifier` specifică la ce obiect se aplică această vizualizare. Poate fi un obiect personalizat pe care l-ați definit sau un obiect standard Twenty. -* `key` determină tipul vizualizării — `ViewKey.INDEX` este principala vizualizare de listă pentru obiect. +* `key: ViewKey.INDEX` marchează vizualizarea ca vizualizarea principală de listă a obiectului (cea pe care o deschide un element de navigare `OBJECT`). * `fields` controlează ce coloane apar și ordinea acestora. Fiecare câmp face referire la un `fieldMetadataUniversalIdentifier`. -* Puteți declara, de asemenea, `filters`, `filterGroups`, `groups` și `fieldGroups` pentru configurații avansate. +* Puteți declara, de asemenea, `filters`, `filterGroups`, `sorts`, `groups` și `fieldGroups` pentru configurații avansate. * `position` controlează ordonarea atunci când există mai multe vizualizări pentru același obiect. +## Proprietăți opționale + +| Proprietate | Valori | Descriere | +| ----------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `type` | `ViewType.TABLE` (implicit), `ViewType.KANBAN`, `ViewType.CALENDAR` | Modul în care sunt dispuse înregistrările. (`FIELDS_WIDGET` / `TABLE_WIDGET` există, de asemenea, dar sunt folosite intern de către widget-urile de tip page-layout.) | +| `visibility` | `ViewVisibility.WORKSPACE` (implicit), `ViewVisibility.UNLISTED` | Dacă vizualizarea este listată pentru întregul spațiu de lucru sau ascunsă din selectoare. | +| `openRecordIn` | `ViewOpenRecordIn.SIDE_PANEL` (implicit), `ViewOpenRecordIn.RECORD_PAGE` | Unde se deschide o înregistrare la clic. | +| `sorts` | `{ fieldMetadataUniversalIdentifier, direction: ViewSortDirection.ASC \| DESC }[]` | Ordinea implicită de sortare. | +| `isCompact` | `boolean` | Afișare compactă a rândurilor. | +| `mainGroupByFieldMetadataUniversalIdentifier` + `shouldHideEmptyGroups` | — | Gruparea înregistrărilor (de ex. coloane kanban) după un câmp. | +| `kanbanAggregateOperation`, `kanbanAggregateOperationFieldMetadataUniversalIdentifier`, `kanbanColumnWidth` | `AggregateOperations.*` | Agregări și dimensionare pentru coloanele kanban. | +| `calendarLayout`, `calendarFieldMetadataUniversalIdentifier` | `ViewCalendarLayout.DAY` / `WEEK` / `MONTH` | Vizualizări de tip calendar: layout și câmpul de dată care poziționează înregistrările. | + +Toate enum-urile de mai sus sunt exportate din `twenty-sdk/define`. + ## Filtre O vizualizare poate include filtre aplicate în prealabil. Fiecare filtru are trei coordonate: **câmpul** care este filtrat, **operandul** (cum se compară) și **valoarea** (față de ce se compară). Toate cele trei trebuie să se potrivească — folosirea unui operand care nu se aplică unui tip de câmp va fi respinsă în timpul sincronizării. ```ts -import { ViewFilterOperand } from 'twenty-shared/types'; +import { ViewFilterOperand } from 'twenty-sdk/define'; filters: [ { diff --git a/packages/twenty-docs/l/ro/developers/extend/apps/logic/logic-functions.mdx b/packages/twenty-docs/l/ro/developers/extend/apps/logic/logic-functions.mdx index 74462829e1..effae52fdb 100644 --- a/packages/twenty-docs/l/ro/developers/extend/apps/logic/logic-functions.mdx +++ b/packages/twenty-docs/l/ro/developers/extend/apps/logic/logic-functions.mdx @@ -51,8 +51,12 @@ export default defineLogicFunction({ ``` Tipuri de declanșatoare disponibile: -* **httpRoute**: Expune funcția pe o cale și metodă HTTP **sub endpoint-ul `/s/`**: -> de ex. `path: '/post-card/create'` este apelabil la `https://your-twenty-server.com/s/post-card/create` +* **httpRoute**: Expune funcţia pe o cale HTTP şi pe o metodă în **funcţiunea URL-ul bazei de lucru** - valoarea de douăzeci de injectări ca `TWENTY_FUNCTIONS_URL` (pe douăzeci Cloud, un domeniu specializat pentru spațiul de lucru): +> de ex. `path: '/post-card/create'` este apelabil la `https://your-workspace.withtwenty.com/post-card/create` + + +Prefixul moștenirii `/s/` (`https://your-twenty-server.com/s/post-card/create`) este **învechit pe 20 de Cloud** și va fi dezactivat pe **2026-07-24**. Rămâne disponibil pentru instanțe auto-găzduite și locale care nu configurează un domeniu de funcții izolate - utilizați `TWENTY_FUNCTIONS_URL` când este setat, şi întoarceţi-vă la `\/s/\` altfel. + Pentru a apela o funcție logică declanșată de o rută dintr-o componentă front-end (headless), consultă [Apelarea unei funcții logice](/l/ro/developers/extend/apps/layout/front-components#calling-a-logic-function). diff --git a/packages/twenty-docs/l/ro/developers/extend/apps/logic/overview.mdx b/packages/twenty-docs/l/ro/developers/extend/apps/logic/overview.mdx index a02b7c1889..0f2707527f 100644 --- a/packages/twenty-docs/l/ro/developers/extend/apps/logic/overview.mdx +++ b/packages/twenty-docs/l/ro/developers/extend/apps/logic/overview.mdx @@ -42,7 +42,7 @@ O funcție de logică alege unul sau mai multe declanșatoare — fiecare intrar | Declanșator | Când rulează | Setare | | ----------------------------- | ----------------------------------------------------------------- | ------------------------------- | -| **Rută HTTP** | O cerere ajunge la endpointul tău `/s/\` | `httpRouteTriggerSettings` | +| **Rută HTTP** | O solicitare accesează URL-ul public al funcției tale | `httpRouteTriggerSettings` | | **Cron** | O expresie CRON se potrivește | `cronTriggerSettings` | | **Eveniment de bază de date** | O înregistrare din workspace este creată, actualizată sau ștearsă | `databaseEventTriggerSettings` | | **Instrument IA** | O funcționalitate IA din Twenty decide să apeleze funcția ta | `toolTriggerSettings` | diff --git a/packages/twenty-docs/l/ro/developers/extend/apps/operations/cli.mdx b/packages/twenty-docs/l/ro/developers/extend/apps/operations/cli.mdx index a82cb8138e..a371e313da 100644 --- a/packages/twenty-docs/l/ro/developers/extend/apps/operations/cli.mdx +++ b/packages/twenty-docs/l/ro/developers/extend/apps/operations/cli.mdx @@ -4,7 +4,25 @@ description: comenzi `yarn twenty` pentru executarea funcțiilor, transmiterea icon: terminal --- -Dincolo de `dev`, `dev:build`, `dev:add` și `dev:typecheck`, `yarn twenty` CLI oferă comenzi pentru executarea funcțiilor, vizualizarea jurnalelor și gestionarea instalărilor de aplicații. +Interfața CLI `yarn twenty` este punctul tău de acces pentru tot ce ține de aplicație. Lista completă de comenzi: + +| Comandă | Ce face | Documentat în | +| ----------------------------------------------- | ----------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- | +| `dev` | Monitorizează fișierele sursă și sincronizează în timp real modificările | [Ghid de pornire rapidă](/l/ro/developers/extend/apps/getting-started/quick-start) | +| `plan` | Previzualizează modificările metadatelor fără a le aplica | [Sincronizare și recuperare](/l/ro/developers/extend/apps/operations/sync-and-recovery#previewing-changes-plan) | +| `aplică` | Aplică modificările metadatelor după afișarea planului | [Sincronizare și recuperare](/l/ro/developers/extend/apps/operations/sync-and-recovery) | +| `dev:build` | Compilează aplicația și generează clientul API (`--tarball` pentru a împacheta un `.tgz`) | [Publicare](/l/ro/developers/extend/apps/operations/publishing) | +| `dev:typecheck` | Rulează verificarea tipurilor TypeScript | [Testare](/l/ro/developers/extend/apps/operations/testing) | +| `dev:add` | Creează scheletul unei entități noi | [Generarea scheletului](/l/ro/developers/extend/apps/getting-started/scaffolding) | +| `dev:generate-client` | Regenerează clientul API tipizat | această pagină | +| `dev:function:exec` / `dev:function:logs` | Execută funcții și transmite în flux jurnalele acestora | această pagină | +| `dev:translations-extract` | Extrage șirurile traducibile în cataloagele din `locales/` | [Traduceri](/l/ro/developers/extend/apps/translations/overview) | +| `dev:catalog-sync` | Declanșează o sincronizare a catalogului marketplace-ului | [Publicare](/l/ro/developers/extend/apps/operations/publishing#how-marketplace-discovery-works) | +| `app:publish` / `app:install` / `app:uninstall` | Ciclul de viață al versiunilor | [Publicare](/l/ro/developers/extend/apps/operations/publishing) și această pagină | +| `docker:*` | Administrează containerul serverului Twenty local | [Server local](/l/ro/developers/extend/apps/getting-started/local-server) | +| `remote:*` | Administrează conexiunile la server | această pagină | + +Fiecare comandă acceptă `-r, --remote \` pentru a viza un anumit server la distanță în locul celui implicit. ## Executarea funcțiilor (`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 ``` ## Vizualizarea jurnalelor funcțiilor (`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 ``` Acreditările dvs. sunt stocate în `~/.twenty/config.json`. diff --git a/packages/twenty-docs/l/ro/developers/extend/apps/operations/publishing.mdx b/packages/twenty-docs/l/ro/developers/extend/apps/operations/publishing.mdx index ec4e95c769..a1cd8a4125 100644 --- a/packages/twenty-docs/l/ro/developers/extend/apps/operations/publishing.mdx +++ b/packages/twenty-docs/l/ro/developers/extend/apps/operations/publishing.mdx @@ -229,7 +229,7 @@ yarn twenty dev:catalog-sync # yarn twenty dev:catalog-sync --remote production ``` -Metadatele afișate în marketplace provin din configurația `defineApplication()` — câmpuri precum `displayName`, `description`, `author`, `category`, `logoUrl`, `screenshots`, `aboutDescription`, `websiteUrl` și `termsUrl`. +Metadatele afișate în marketplace provin din configurația `defineApplication()` — vezi secțiunea [Metadate pentru marketplace](#marketplace-metadata) de mai sus. Dacă aplicația ta nu definește un `aboutDescription` în `defineApplication()`, piața va folosi automat fișierul `README.md` al pachetului tău de pe npm drept conținut pentru pagina Despre. Acest lucru înseamnă că poți menține un singur README atât pentru npm, cât și pentru piața Twenty. Dacă vrei o descriere diferită în piață, setează explicit `aboutDescription`. diff --git a/packages/twenty-docs/l/ro/developers/extend/apps/operations/sync-and-recovery.mdx b/packages/twenty-docs/l/ro/developers/extend/apps/operations/sync-and-recovery.mdx index 06770e0cf7..a7ff99e180 100644 --- a/packages/twenty-docs/l/ro/developers/extend/apps/operations/sync-and-recovery.mdx +++ b/packages/twenty-docs/l/ro/developers/extend/apps/operations/sync-and-recovery.mdx @@ -15,33 +15,44 @@ Pentru iterațiile locale de zi cu zi vei dori aproape întotdeauna `yarn twenty | Vrei să… | Comandă | Notițe | | -------------------------------------------------------------- | ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------- | | Iterează local cu sincronizare în timp real | `yarn twenty dev` | Monitorizează fișierele și sincronizează la fiecare modificare. | -| Sincronizează o singură dată și iese (CI, scripturi, hook-uri) | `yarn twenty dev --once` | O singură compilare + sincronizare, apoi iese. | -| Previzualizează modificările **fără a le aplica** | `yarn twenty dev --once --dry-run` | Calculează și afișează diff-ul; nu scrie nimic. | +| Sincronizează o singură dată și iese (CI, scripturi, hook-uri) | `yarn twenty apply` | O singură compilare + sincronizare, apoi iese. Adaugă `--force` pentru a omite confirmarea schimbărilor distructive. | +| Previzualizează modificările **fără a le aplica** | `yarn twenty plan` | Calculează și afișează diff-ul; nu scrie nimic. | | Elimină aplicația din spațiul de lucru | `yarn twenty app:uninstall` | Adaugă `--yes` pentru a sări peste prompt. | | Trimite un tarball către un server | `yarn twenty app:publish --private` | Necesită o versiune `package.json` **strict mai mare** — vezi [Publicare](/l/ro/developers/extend/apps/operations/publishing). | | Publică în marketplace (npm) | `yarn twenty app:publish` | — | | Instalează / actualizează o versiune deja implementată | `yarn twenty app:install` | Instalează versiunea implementată în prezent. | | Șterge serverul local și pornește de la zero | `yarn twenty docker:reset` | Șterge **toate** datele locale — ultimă soluție. | + +`yarn twenty dev --once` și `yarn twenty dev --once --dry-run` funcționează în continuare ca aliasuri depreciate pentru `yarn twenty apply` și `yarn twenty plan`. + + ### Sincronizarea locală nu are nevoie de incrementarea versiunii Regula de `version` strict crescătoare (`VERSION_ALREADY_EXISTS` la deploy, `APP_ALREADY_INSTALLED` / `CANNOT_DOWNGRADE_APPLICATION` la instalare) se aplică pentru **`app:publish` / `app:install`** — calea de release. `yarn twenty dev` sincronizează manifestul pe loc și nu necesită niciodată schimbarea versiunii, astfel încât nu trebuie să atingi `package.json` pentru a itera. Dacă ajungi să crești versiunea ca să testezi o modificare locală, folosești calea de release atunci când ai nevoie de bucla de dezvoltare. ## Citirea rezultatului sincronizării -Fiecare sincronizare afișează modificările de metadate pe care le-a aplicat (sau le-ar aplica, cu `--dry-run`): +Fiecare sincronizare afișează modificările de metadate pe care le-a aplicat (sau le-ar aplica, cu `plan`), în stil Terraform — un bloc per entitate cu atributele sale, apoi o linie de rezumat: ```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) ``` Acesta este primul tău instrument de diagnostic: îți spune exact ce obiecte, câmpuri și layout-uri s-au schimbat, astfel încât să poți confirma că o sincronizare a făcut ce te așteptai înainte să verifici interfața. +Schimbările distructive (`to destroy`) sunt listate împreună cu ceea ce elimină (de ex. `objectMetadata "auditNote" — drops the table and all its rows`) și necesită confirmare interactivă sau `--force` în scripturi. + Când o sincronizare eșuează pe o singură entitate, eroarea numește entitatea problematică și `universalIdentifier`-ul acesteia, de exemplu: ```text @@ -50,39 +61,42 @@ Migration action 'create' for 'fieldMetadata' (universalIdentifier: 2020...4337) Folosește acel identificator pentru a găsi entitatea în manifest (și, dacă este nevoie, în spațiul de lucru) în loc să ghicești care intră în conflict. -## Previzualizarea modificărilor (dry run) +## Previzualizarea modificărilor (plan) -`yarn twenty dev --once --dry-run` construiește manifestul, cere serverului planul de migrare și îl afișează — **fără a aplica nimic**. Este modalitatea sigură de a răspunde la întrebarea „ce ar schimba această sincronizare?” înainte de a te angaja la ea. +`yarn twenty plan` construiește manifestul, cere serverului planul de migrare și îl afișează — **fără a aplica nimic**. Este modalitatea sigură de a răspunde la întrebarea „ce ar schimba această sincronizare?” înainte de a te angaja la ea. ```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: * **Nu scrie nimic** — fără migrare de metadate, fără actualizare a înregistrării aplicației, fără modificări ale rolului sau filei implicite și fără generare de client API. * Returnează **același diff** pe care l-ar aplica o sincronizare reală, astfel încât poți revizui dinainte entitățile create/actualizate/șterse. * Este util înaintea unei modificări riscante, când revizuiești o modificare generată de AI sau într-un script care ar trebui să eșueze dacă o modificare neașteptată este pe cale să fie aplicată. -Un dry run previzualizează doar modificările de **metadate** și necesită ca aplicația să fi fost sincronizată cel puțin o dată (astfel încât spațiul de lucru să știe de ea). Dacă îl rulezi pentru o aplicație care nu a fost niciodată sincronizată, serverul va raporta că aplicația nu este instalată — rulează mai întâi o dată `yarn twenty dev`. +Un plan previzualizează doar modificările de **metadate** și necesită ca aplicația să fi fost sincronizată cel puțin o dată (astfel încât spațiul de lucru să știe de ea). Dacă îl rulezi pentru o aplicație care nu a fost niciodată sincronizată, serverul va raporta că aplicația nu este instalată — rulează mai întâi o dată `yarn twenty dev`. ## Plan de recuperare în trepte Când metadatele locale par greșite, escaladează în această ordine și oprește-te de îndată ce ești deblocat. Fiecare pas este mai disruptiv decât precedentul. -1. **Resincronizează.** Rulează din nou `yarn twenty dev --once`. Sincronizările sunt idempotente — rularea din nou a unui manifest curat este sigură și rezolvă adesea o problemă temporară. -2. **Previzualizează planul.** Rulează `yarn twenty dev --once --dry-run` pentru a vedea exact ce intenționează să schimbe următoarea sincronizare, fără a o aplica. +1. **Resincronizează.** Rulează din nou `yarn twenty apply`. Sincronizările sunt idempotente — rularea din nou a unui manifest curat este sigură și rezolvă adesea o problemă temporară. +2. **Previzualizează planul.** Rulează `yarn twenty plan` pentru a vedea exact ce intenționează să schimbe următoarea sincronizare, fără a o aplica. 3. **Citește eroarea nominalizată.** Dacă o sincronizare eșuează, notează tipul de metadate și `universalIdentifier`-ul din mesaj (vezi mai sus) și localizează acea entitate în manifest. Un conflict indică de obicei un identificator duplicat sau reutilizat. 4. **Dezinstalează și reinstalează.** `yarn twenty app:uninstall`, apoi sincronizează din nou (`yarn twenty dev`). Acest lucru reconstruiește metadatele aplicației de la zero, păstrând în același timp restul spațiului de lucru intact. 5. **Resetare completă (ultimă soluție).** `yarn twenty docker:reset`, apoi reinițializează datele și resincronizează. diff --git a/packages/twenty-docs/l/ro/developers/extend/apps/operations/testing.mdx b/packages/twenty-docs/l/ro/developers/extend/apps/operations/testing.mdx index 88c1f007d3..d315ff0682 100644 --- a/packages/twenty-docs/l/ro/developers/extend/apps/operations/testing.mdx +++ b/packages/twenty-docs/l/ro/developers/extend/apps/operations/testing.mdx @@ -78,6 +78,13 @@ Creați un `vitest.config.ts` în rădăcina aplicației: 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ți un fișier de configurare care verifică faptul că serverul este accesibil înainte de rularea testelor: +Creează un fișier global de configurare inițială care verifică faptul că serverul este accesibil, scrie un fișier de configurare de test pentru SDK (`~/.twenty/config.test.json`) și sincronizează aplicația înainte ca testele să ruleze: -```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-uri SDK programatice Subcalea `twenty-sdk/cli` exportă funcții pe care le puteți apela direct din codul de test: -| Funcție | Descriere | -| -------------- | --------------------------------------------------------- | -| `appBuild` | Construiți aplicația și, opțional, împachetați un tarball | -| `appDeploy` | Încărcați un tarball pe server | -| `appInstall` | Instalați aplicația în spațiul de lucru activ | -| `appUninstall` | Dezinstalați aplicația din spațiul de lucru activ | +| Funcție | Descriere | +| -------------- | -------------------------------------------------------------------------------------- | +| `appBuild` | Construiți aplicația și, opțional, împachetați un tarball | +| `appDeploy` | Încărcați un tarball pe server | +| `appDevOnce` | Construiește și sincronizează aplicația o singură dată (la fel ca `yarn twenty apply`) | +| `appInstall` | Instalați aplicația în spațiul de lucru activ | +| `appUninstall` | Dezinstalați aplicația din spațiul de lucru activ | Fiecare funcție returnează un obiect rezultat cu `success: boolean` și fie `data`, fie `error`. @@ -238,64 +253,10 @@ Puteți rula și verificarea tipurilor pe aplicație fără a rula testele: yarn twenty dev:typecheck ``` -Aceasta rulează `tsc --noEmit` și raportează orice erori de tip. +Aceasta rulează `tsc --noEmit` împotriva fișierului `tsconfig.json` al aplicației și raportează orice erori de tip. Aplicațiile generate cu scaffold includ, de asemenea, un script `yarn typecheck` care acoperă și fișierele de test (`tsconfig.spec.json`). ## CI cu GitHub Actions -Scaffolderul generează un workflow GitHub Actions gata de utilizare în `.github/workflows/ci.yml`. Rulează automat testele de integrare la fiecare push pe `main` și la pull request-uri. +Scaffolderul generează un workflow gata de utilizare la `.github/workflows/ci.yml`. La fiecare push pe `main` și la fiecare pull request, acesta pornește un server Twenty efemer în runner (prin acțiunea `twentyhq/twenty/.github/actions/spawn-twenty-app-dev-test`), apoi rulează `yarn lint`, `yarn typecheck`, `yarn test:unit` și `yarn test` cu `TWENTY_API_URL` / `TWENTY_API_KEY` îndreptate către acel server. Nu sunt necesare secrete și poți fixa versiunea serverului prin variabila de mediu `TWENTY_VERSION` din partea de sus a workflow-ului. -Workflow-ul: - -1. Preia codul -2. Pornește un server Twenty temporar folosind acțiunea `twentyhq/twenty/.github/actions/spawn-twenty-docker-image` -3. Instalează dependențele cu `yarn install --immutable` -4. Rulează `yarn test` cu `TWENTY_API_URL` și `TWENTY_API_KEY` injectate din rezultatele acțiunii - -```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 }} -``` - -Nu trebuie să configurați niciun secret — acțiunea `spawn-twenty-docker-image` pornește un server Twenty efemer direct în runner și oferă detaliile de conectare. Secretul `GITHUB_TOKEN` este furnizat automat de GitHub. - -Pentru a fixa o versiune Twenty specifică în loc de `latest`, modificați variabila de mediu `TWENTY_VERSION` din partea de sus a workflow-ului. +Vezi [Publicare → CI/CD automatizat](/l/ro/developers/extend/apps/operations/publishing#automated-cicd-scaffolded-workflows) pentru un ghid complet al ambelor workflow-uri generate cu scaffold (`ci.yml` și pipeline-ul de deploy `cd.yml`). diff --git a/packages/twenty-docs/l/ro/developers/extend/apps/tutorials/document-generator/building-the-ui.mdx b/packages/twenty-docs/l/ro/developers/extend/apps/tutorials/document-generator/building-the-ui.mdx index 836b52ec83..0288f56f37 100644 --- a/packages/twenty-docs/l/ro/developers/extend/apps/tutorials/document-generator/building-the-ui.mdx +++ b/packages/twenty-docs/l/ro/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/ro/developers/extend/apps/tutorials/document-generator/http-routes.mdx b/packages/twenty-docs/l/ro/developers/extend/apps/tutorials/document-generator/http-routes.mdx index 5cffbd9c8f..3c136850c3 100644 --- a/packages/twenty-docs/l/ro/developers/extend/apps/tutorials/document-generator/http-routes.mdx +++ b/packages/twenty-docs/l/ro/developers/extend/apps/tutorials/document-generator/http-routes.mdx @@ -9,8 +9,15 @@ Același gestionar poate răspunde și la solicitările HTTP. Vom adăuga două * a **POST** final apeluri interfață pentru a genera un document, și * un obiectiv public **GET** care face un document ca o pagină web printabilă. -Ambele folosesc `httpRouteTriggerSettings`. Rutele aplicațiilor sunt servite sub `/s` pe -Douăzeci de servere (ex. `http://localhost:2020/s/documents/generate`). +Ambele folosesc `httpRouteTriggerSettings`. Pe server-ul local dev, rutele aplicației sunt servite +sub prefixul `/s` (ex. `http://localhost:2020/s/documents/generate`). + + +În 22 de Cloud, rutele sunt servite pe domeniul funcțiilor dedicate din spațiul de lucru +— URL-ul Douăzeci injectează ca `TWENTY_FUNCTIONS_URL`, fără prefixul `/s`. Prefixul +este învechit acolo şi rămâne doar pentru instanţele auto-găzduite şi locale. +Vedeți [Apelarea unei funcții logice](/l/ro/developers/extend/apps/layout/front-components#calling-a-logic-function). + ## Ruta POST – generarea la cerere diff --git a/packages/twenty-docs/l/ro/developers/extend/apps/tutorials/document-generator/publishing.mdx b/packages/twenty-docs/l/ro/developers/extend/apps/tutorials/document-generator/publishing.mdx index ba3e5dc9fa..47215de7c2 100644 --- a/packages/twenty-docs/l/ro/developers/extend/apps/tutorials/document-generator/publishing.mdx +++ b/packages/twenty-docs/l/ro/developers/extend/apps/tutorials/document-generator/publishing.mdx @@ -76,11 +76,11 @@ Rulează aceleași porți CI face: 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 ``` -Derularea uscată tipărește exact ce s-ar schimba pe server fără a o aplica — -un bun control sanitar final. Vezi +Planul afișează exact ce s-ar schimba pe server, fără a aplica modificările — +o bună verificare finală. Vezi [Testing](/l/ro/developers/extend/apps/operations/testing) şi [Sincronizare şi Recuperare](/l/ro/developers/extend/apps/operations/sync-and-recovery). diff --git a/packages/twenty-docs/l/tr/developers/extend/apps/config/install-hooks.mdx b/packages/twenty-docs/l/tr/developers/extend/apps/config/install-hooks.mdx index 8a69a2127c..2afa56e076 100644 --- a/packages/twenty-docs/l/tr/developers/extend/apps/config/install-hooks.mdx +++ b/packages/twenty-docs/l/tr/developers/extend/apps/config/install-hooks.mdx @@ -4,7 +4,7 @@ description: Kurulumdan önce veya sonra mantığı çalıştırın — veri toh icon: wrench --- -Kurulum kancaları, kurulum veya yükseltme yaşam döngüsü sırasında çalışan özel mantık işlevleridir. Bunlar, normal [mantık işlevleri](/l/tr/developers/extend/apps/logic/logic-functions) ile aynı işleyici çalışma zamanını paylaşır ve bir `InstallPayload` alırlar, ancak kendi tanımlama işlevleri — `definePostInstallLogicFunction()` ve `definePreInstallLogicFunction()` — ile bildirilirler ve normal tetikleyici modelinin (HTTP, cron, veritabanı olayları) dışında yaşarlar. +Kurulum kancaları, kurulum veya yükseltme yaşam döngüsü sırasında çalışan özel mantık işlevleridir. Bunlar, normal [mantık işlevleri](/l/tr/developers/extend/apps/logic/logic-functions) ile aynı işleyici çalışma zamanını paylaşır ve bir `InstallPayload` alırlar (`{ previousVersion?: string; newVersion: string }` — yeni bir kurulumda `previousVersion` `undefined` olur), ancak kendi define işlevleriyle bildirilirler ve normal tetikleyici modelinin (HTTP, cron, veritabanı olayları) dışında yer alırlar. Her uygulama **en fazla bir kurulum öncesi** ve **en fazla bir kurulum sonrası** işlev tanımlayabilir. Her ikisinden de birden fazla tespit edilirse manifest oluşturma hataya düşer. @@ -19,111 +19,59 @@ Her uygulama **en fazla bir kurulum öncesi** ve **en fazla bir kurulum sonrası └─────────────────────────────────────────────────────────────┘ ``` - - +## Bir bakışta -Kurulum sonrası işlev, uygulamanız bir çalışma alanına kurulmasını tamamladıktan sonra otomatik olarak çalışır. Sunucu, uygulamanın meta verileri senkronize edildikten ve SDK istemcisi oluşturulduktan **sonra** bunu yürütür; böylece çalışma alanı tamamen kullanıma hazırdır ve yeni şema kullanıma alınmıştır. Tipik kullanım örnekleri arasında varsayılan verilerin tohumlanması, başlangıç kayıtlarının oluşturulması, çalışma alanı ayarlarının yapılandırılması veya üçüncü taraf hizmetlerde kaynak sağlanması yer alır. +| | `definePreInstallLogicFunction` | `definePostInstallLogicFunction` | +| ---------------------- | ------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- | +| Çalıştırmalar | Üstveri geçişinden önce — **önceki** şema ve veriler hâlâ sağlamdır | Geçişten ve SDK oluşturmasından sonra — **yeni** şema devrededir | +| Yürütme | Her zaman senkron; kurulumu bloke eder | Varsayılan olarak async (kuyruğa alınır, 3 yeniden deneme); `shouldRunSynchronously: true` ile isteğe bağlı senkron | +| Başarısızlık durumunda | Kurulum, herhangi bir şema değişikliğinden önce **iptal edilir** | Async: en fazla 3 kez yeniden denenir. Sync: çağıran `POST_INSTALL_ERROR` alır (şema değişiklikleri **geri alınmaz**) | +| Tipik kullanım | Bir geçişin kaybedeceği verileri yedeklemek veya düzeltmek; fırlatarak riskli bir yükseltmeyi reddetmek | Varsayılan verileri tohumlamak, çalışma alanını yapılandırmak, harici kaynakları kaydetmek | -```ts src/logic-functions/post-install.ts -import { definePostInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define'; +**Kural:** varsayılan olarak post-install kullanın. Yalnızca geçişin kendisi yıkıcıysa ve önceki durum yok olmadan önce onu yakalamanız gerekiyorsa kurulum öncesine başvurun. -const handler = async (payload: InstallPayload): Promise => { - console.log('Post install logic function executed successfully!', payload.previousVersion); -}; +| Şunu yapmak istiyorsunuz... | Kullan | +| --------------------------------------------------------------------------------- | ------------------------------------------------------------------- | +| Verileri tohumlamak, çalışma alanını yapılandırmak, harici kaynakları kaydetmek | `post-install` | +| Kurulum yanıtını engellememesi gereken uzun süreli işleri yürütmek | `post-install` (varsayılan async mod, worker yeniden denemeleriyle) | +| Kurulum döndükten hemen sonra çağıranın güveneceği hızlı kurulumu gerçekleştirmek | `post-install` ile `shouldRunSynchronously: true` | +| Yaklaşan geçişin kaybedeceği verileri okumak veya yedeklemek | `pre-install` | +| Mevcut verileri bozacak bir yükseltmeyi reddetmek | `pre-install` (işleyiciden hata fırlatmak) | +| Her yükseltmede uzlaştırma çalıştırmak | `shouldRunOnVersionUpgrade: true` ile her iki kancadan biri | -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, -}); -``` +## Her iki kanca tarafından paylaşılan davranış -Ayrıca kurulum sonrası işlevi istediğiniz zaman CLI kullanarak manuel olarak çalıştırabilirsiniz: +* Yapılandırma, tetikleyici ayarları çıkarılmış bir `defineLogicFunction` yapılandırmasıdır ve buna ek olarak `shouldRunOnVersionUpgrade` içerir. +* **Ne zaman çalışır**: varsayılan olarak yalnızca ilk kurulumlarda. Yükseltmelerde de çalışması için `shouldRunOnVersionUpgrade: true` olarak ayarlayın. Yükseltme yoluna göre dallanmak için `previousVersion` / `newVersion` kullanın. +* **İdempotans önemlidir**: async post-install yeniden denenebilir ve `shouldRunOnVersionUpgrade` açıkken her iki kanca da yükseltmelerde yeniden çalıştırılır. +* Alışıldık mantık işlevi ortamı (`APPLICATION_ID`, `APP_ACCESS_TOKEN`, `API_URL`) enjekte edilir, böylece Twenty API'sini uygulamanızın jetonuyla çağırabilirsiniz. +* Kanca, derleme zamanında otomatik olarak uygulama manifestine (`preInstallLogicFunction` / `postInstallLogicFunction`) eklenir — [`defineApplication()`](/l/tr/developers/extend/apps/config/application) içinde referans verilecek bir şey yoktur. +* Varsayılan `timeoutSeconds`, veri tohumlama gibi daha uzun kurulum görevlerine izin vermek için 300 olarak ayarlanmıştır. +* **Geliştirme modunda yürütülmez**: `yarn twenty dev` kurulum akışını atlar ve dosyaları doğrudan eşitler, bu nedenle kancalar burada asla çalışmaz. Bunları bunun yerine manuel olarak tetikleyin: ```bash filename="Terminal" yarn twenty dev:function:exec --postInstall -``` - -Önemli noktalar: -* Kurulum sonrası işlevler `definePostInstallLogicFunction()` kullanır — tetikleyici ayarlarını atlayan (`cronTriggerSettings`, `databaseEventTriggerSettings`, `httpRouteTriggerSettings`, `toolTriggerSettings`, `workflowActionTriggerSettings`) özel bir varyanttır. -* İşleyici, `{ previousVersion?: string; newVersion: string }` içeren bir `InstallPayload` alır — `newVersion`, yüklenen sürümdür; `previousVersion` ise daha önce yüklü olan sürümdür (veya ilk kurulumda `undefined`). Bu değerleri ilk kurulumları yükseltmelerden ayırt etmek ve sürüme özgü geçiş (migration) mantığını çalıştırmak için kullanın. -* **Kanca ne zaman çalışır**: varsayılan olarak yalnızca ilk kurulumlarda. Uygulama önceki bir sürümden yükseltildiğinde de çalışmasını istiyorsanız `shouldRunOnVersionUpgrade: true` geçin. Belirtilmediğinde, bayrak varsayılan olarak `false` olur ve yükseltmeler kancayı atlar. -* **Yürütme modeli — varsayılan olarak eşzamansız, isteğe bağlı senkron**: `shouldRunSynchronously` bayrağı kurulum sonrası işlemin *nasıl* yürütüldüğünü kontrol eder. - * `shouldRunSynchronously: false` *(varsayılan)* — kanca, `retryLimit: 3` ile **mesaj kuyruğuna alınır** ve bir worker içinde eşzamansız çalışır. İş kuyruğa alınır alınmaz kurulum yanıtı döner; dolayısıyla yavaşlayan veya hata veren bir işleyici çağıranı engellemez. Worker en fazla üç kez yeniden deneyecektir. **Bunu uzun süre çalışan işler için kullanın** — büyük veri kümelerini tohumlama, yavaş üçüncü taraf API'lerini çağırma, harici kaynakları sağlama; makul bir HTTP yanıt süresini aşabilecek her şey. - * `shouldRunSynchronously: true` — kanca **kurulum akışı sırasında satır içi** olarak yürütülür (kurulum öncesi ile aynı yürütücü). İşleyici bitene kadar kurulum isteği engellenir; hata fırlatırsa, kurulum çağıranı bir `POST_INSTALL_ERROR` alır. Otomatik yeniden deneme yok. **Bunu, yanıt dönmeden mutlaka tamamlanması gereken hızlı işler için kullanın** — örneğin, kullanıcıya bir doğrulama hatası iletmek veya kurulum çağrısı döner dönmez istemcinin ihtiyaç duyacağı hızlı bir kurulum yapmak. Kurulum sonrası çalıştığında, üstveri (metadata) geçişinin zaten uygulanmış olduğunu unutmayın; bu nedenle, senkron moddaki bir hata şema değişikliklerini **geri almaz** — yalnızca hatayı görünür kılar. -* İşleyicinizin idempotent olduğundan emin olun. Eşzamansız modda kuyruk en fazla üç kez yeniden deneyebilir; her iki modda da `shouldRunOnVersionUpgrade: true` iken yükseltmelerde kanca tekrar çalışabilir. -* Ortam değişkenleri `APPLICATION_ID`, `APP_ACCESS_TOKEN` ve `API_URL` işleyici içinde kullanılabilir (diğer mantık işlevlerinde olduğu gibi), böylece uygulamanıza özel kapsamda bir uygulama erişim belirteciyle Twenty API'sini çağırabilirsiniz. -* Uygulama başına yalnızca bir kurulum sonrası işlevine izin verilir. Birden fazla tespit edilirse manifest oluşturma hataya düşer. -* İşlevin `universalIdentifier`, `shouldRunOnVersionUpgrade` ve `shouldRunSynchronously` değerleri, derleme sırasında uygulama manifestine `postInstallLogicFunction` alanı altında otomatik olarak eklenir — bunlara [`defineApplication()`](/l/tr/developers/extend/apps/config/application) içinde atıfta bulunmanıza gerek yoktur. -* Varsayılan zaman aşımı, veri tohumlama gibi daha uzun kurulum görevlerine izin vermek için 300 saniye (5 dakika) olarak ayarlanmıştır. -* **Geliştirme modunda çalıştırılmaz**: bir uygulama yerel olarak kaydedildiğinde (`yarn twenty dev` aracılığıyla), sunucu kurulum akışını tamamen atlar ve dosyaları doğrudan CLI watcher üzerinden eşitler — bu nedenle, `shouldRunSynchronously` ne olursa olsun, kurulum sonrası geliştirme modunda hiç çalışmaz. Çalışan bir çalışma alanında bunu elle tetiklemek için `yarn twenty dev:function:exec --postInstall` kullanın. - - - - -Kurulum öncesi işlev, kurulum sırasında otomatik olarak çalışır ve **çalışma alanı üstveri (metadata) geçişi uygulanmadan önce** yürütülür. Kurulum sonrası ile (`InstallPayload`) aynı yük (payload) biçimini paylaşır, ancak kurulum akışında daha erken konumlandığından yaklaşan geçişin bağlı olduğu durumu hazırlayabilir — tipik kullanımlar arasında verileri yedeklemek, yeni şemayla uyumluluğu doğrulamak veya yeniden yapılandırılacak ya da kaldırılacak kayıtları arşivlemek yer alır. - -```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, -}); -``` - -Ayrıca kurulum öncesi işlevi istediğiniz zaman CLI kullanarak manuel olarak çalıştırabilirsiniz: - -```bash filename="Terminal" yarn twenty dev:function:exec --preInstall ``` -Önemli noktalar: -* Kurulum öncesi işlevler `definePreInstallLogicFunction()` kullanır — kurulum sonrasıyla aynı özel yapılandırma, sadece yaşam döngüsünde farklı bir yuvaya eklenir. -* Hem kurulum öncesi hem de kurulum sonrası işleyiciler aynı `InstallPayload` türünü alır: `{ previousVersion?: string; newVersion: string }`. Bunu bir kez içe aktarın ve her iki kanca için yeniden kullanın. -* **Kanca ne zaman çalışır**: çalışma alanı üstveri (metadata) geçişinden hemen önce konumlandırılır (`synchronizeFromManifest`). Çalıştırmadan önce, sunucu yalnızca ekleyici bir "indirgenmiş eşitleme" yürütür; bu, çalışma alanı üstverisinde **yeni** sürümün kurulum öncesi işlevini kaydeder — başka hiçbir şeye dokunulmaz — ve ardından bunu yürütür. Bu eşitleme yalnızca ekleyici olduğundan, işleyiciniz çalıştığında önceki sürümün nesneleri, alanları ve verileri hâlâ sağlamdır: geçiş öncesi durumu güvenle okuyabilir ve yedekleyebilirsiniz. -* **Yürütme modeli**: kurulum öncesi **senkron** olarak yürütülür ve **kurulumu bloklar**. İşleyici bir hata fırlatırsa, herhangi bir şema değişikliği uygulanmadan önce kurulum iptal edilir — çalışma alanı, tutarlı bir durumda önceki sürümde kalır. Bu kasıtlıdır: kurulum öncesi, riskli bir yükseltmeyi reddetmek için son şansınızdır. -* Kurulum sonrası ile aynı şekilde, uygulama başına yalnızca bir kurulum öncesi işlevine izin verilir. Derleme sırasında uygulama manifestine `preInstallLogicFunction` altında otomatik olarak eklenir. -* **Geliştirme modunda çalıştırılmaz**: kurulum sonrasında olduğu gibi — yerel olarak kaydedilen uygulamalarda kurulum akışı tamamen atlanır, bu nedenle `yarn twenty dev` altında kurulum öncesi hiç çalışmaz. Bunu elle tetiklemek için `yarn twenty dev:function:exec --preInstall` kullanın. + + - - - -Her iki kanca da aynı kurulum akışının parçasıdır ve aynı `InstallPayload`'ı alır. Fark, çalışma alanı üstveri (metadata) geçişine göre **ne zaman** çalıştıklarıdır ve bu, güvenle erişebilecekleri verileri değiştirir. - -Kurulum öncesi her zaman **senkron**dur (kurulumu bloke eder ve iptal edebilir). Kurulum sonrası **varsayılan olarak asenkron**dur — otomatik yeniden denemelerle bir worker üzerinde kuyruğa alınır — ancak `shouldRunSynchronously: true` ile senkron yürütmeye geçebilir. Her modun ne zaman kullanılacağı için yukarıdaki `definePostInstallLogicFunction` akordeonuna bakın. - -**Yeni şemanın mevcut olmasını gerektiren her şey için `post-install` kullanın.** Bu yaygın durumdur: - -* Yeni eklenen nesne ve alanlara karşı varsayılan verileri tohumlama (ilk kayıtları, varsayılan görünümleri, demo içeriği oluşturma). -* Uygulamanın kimlik bilgileri artık mevcut olduğuna göre, üçüncü taraf hizmetlerle webhook'ları kaydetmek. -* Eşitlenmiş üstveriye (metadata) bağlı kurulumu tamamlamak için kendi API'nizi çağırmak. -* Her yükseltmede durumu uzlaştırması gereken idempotent "bu mevcut olsun" mantığı — `shouldRunOnVersionUpgrade: true` ile birleştirin. - -Örnek — kurulumdan sonra varsayılan bir `PostCard` kaydı tohumlama: +Uygulamanızın kurulumu tamamlandıktan sonra bir kez çalışır: üstveri eşitlenmiştir, SDK istemcisi oluşturulmuştur, yeni şema sorgulanabilir durumdadır. Örnek — ilk kurulumlarda varsayılan bir kaydı tohumlamak: ```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, }); ``` -**Bir geçiş mevcut verileri aksi takdirde silecek veya bozacaksa `pre-install` kullanın.** Kurulum öncesi *önceki* şemaya karşı çalıştığı ve hatalandığında yükseltmeyi geri aldığı için, riskli olan her şey için doğru yerdir: +`shouldRunSynchronously` bayrağı yürütme modelini kontrol eder: -* **Kaldırılmak veya yeniden yapılandırılmak üzere olan verileri yedekleme** — örn. v2'de bir alanı kaldırıyorsunuz ve geçiş çalışmadan önce değerlerini başka bir alana kopyalamanız veya depolamaya aktarmanız gerekiyor. -* **Yeni bir kısıtın geçersiz kılacağı kayıtları arşivleme** — örn. bir alan `NOT NULL` oluyor ve önce null değerli satırları silmeniz veya düzeltmeniz gerekiyor. -* **Uyumluluğu doğrulama ve mevcut veriler temiz bir şekilde geçirilemiyorsa yükseltmeyi reddetme** — işleyiciden hata fırlatın ve kurulum, herhangi bir değişiklik uygulanmadan iptal edilir. Bu, uyumsuzluğu geçişin ortasında keşfetmekten daha güvenlidir. -* İlişkilendirmeyi kaybettirecek bir şema değişikliğinden önce **verileri yeniden adlandırma veya yeniden anahtarlama**. +* `false` *(varsayılan)* — mesaj kuyruğuna alınır (`retryLimit: 3`) ve bir worker tarafından çalıştırılır. Kurulum yanıtı, iş kuyruğa alınır alınmaz döner. **Uzun süreli işler için kullanın** — büyük veri kümelerinin tohumlanması, yavaş üçüncü taraf API'leri. +* `true` — kurulum akışı sırasında satır içi olarak yürütülür. Kurulum isteği, işleyici bitene kadar bloke olur; fırlatılan bir hata, çağırana `POST_INSTALL_ERROR` olarak yansır (yeniden deneme yoktur). **Hızlı ve yanıt dönmeden önce mutlaka tamamlanması gereken işler için kullanın.** Bu noktada geçiş zaten uygulanmıştır, bu nedenle bir hata şema değişikliklerini geri almaz — yalnızca hatayı görünür kılar. -Örnek — yıkıcı bir geçişten önce kayıtları arşivleme: + + + +Üstveri geçişinden önce, **önceki** şemaya karşı çalışır — bir geçişin kaybedeceği verileri yedeklemek veya riskli bir yükseltmeyi reddetmek için doğru yerdir. Çalıştırmadan önce, sunucu yalnızca ekleyici bir "indirgenmiş eşitleme" yürütür; bu, yalnızca yeni sürümün kurulum öncesi işlevini kaydeder, diğer her şey — önceki sürümün nesneleri, alanları ve verileri — işleyiciniz çalıştığında dokunulmadan kalır. + +Kurulum öncesi her zaman **senkron**dur ve kurulumu bloke eder. İşleyici bir hata fırlatırsa, herhangi bir şema değişikliğinden önce kurulum iptal edilir — çalışma alanı, tutarlı bir durumda önceki sürümde kalır. Bu kasıtlıdır: kurulum öncesi, riskli bir yükseltmeyi reddetmek için son şansınızdır. + +Örnek — geçiş onu düşürmeden önce eski bir alanın değerlerini kopyalamak: ```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({ }); ``` -**Kural olarak:** - -| Şunu yapmak istiyorsunuz... | Kullan | -| ------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------- | -| Varsayılan verileri tohumlamak, çalışma alanını yapılandırmak, harici kaynakları kaydetmek | `post-install` | -| Kurulum yanıtını engellememesi gereken uzun süreli tohumlama veya üçüncü taraf çağrılarını çalıştırmak | `post-install` (varsayılan — `shouldRunSynchronously: false`, worker yeniden denemeleriyle) | -| Kurulum çağrısı döner dönmez çağıranın güveneceği hızlı kurulumu çalıştırmak | `post-install` ile `shouldRunSynchronously: true` | -| Yaklaşan geçişin kaybedeceği verileri okumak veya yedeklemek | `pre-install` | -| Mevcut verileri bozacak bir yükseltmeyi reddetmek | `pre-install` (işleyiciden hata fırlatmak) | -| Her yükseltmede uzlaştırma çalıştırmak | `post-install` ile `shouldRunOnVersionUpgrade: true` | -| Yalnızca ilk kurulumda tek seferlik kurulum yapmak | `post-install` ile `shouldRunOnVersionUpgrade: false` (varsayılan) | - - -Emin değilseniz, varsayılan olarak **kurulum sonrası**nı tercih edin. Yalnızca geçişin kendisi yıkıcıysa ve önceki durum yok olmadan önce onu yakalamanız gerekiyorsa kurulum öncesine başvurun. - - diff --git a/packages/twenty-docs/l/tr/developers/extend/apps/data/objects.mdx b/packages/twenty-docs/l/tr/developers/extend/apps/data/objects.mdx index c11d9809fc..121c1bdf36 100644 --- a/packages/twenty-docs/l/tr/developers/extend/apps/data/objects.mdx +++ b/packages/twenty-docs/l/tr/developers/extend/apps/data/objects.mdx @@ -86,6 +86,22 @@ export default defineObject({ **Temel alanlar otomatik olarak eklenir.** Özel bir nesne tanımladığınızda Twenty, sizin için `id`, `name`, `createdAt`, `updatedAt`, `createdBy`, `updatedBy` ve `deletedAt` gibi standart alanlar oluşturur. Bunları `fields` dizinizde bildirmenize gerek yok — yalnızca özel alanlarınızı ekleyin. Aynı ada sahip bir alan bildirerek varsayılan bir alanı geçersiz kılabilirsiniz, ancak bu nadiren iyi bir fikirdir. +## Alan tipleri + +`twenty-sdk/define` içinden dışa aktarılan, `FieldType` değerlerinin tam kümesi: + +| Kategori | Türler | +| --------------------------- | ----------------------------------------------------------------------------------------------------------------------------- | +| Metin | `TEXT`, `RICH_TEXT`, `ARRAY` (string dizisi), `RAW_JSON` | +| Sayısal | `NUMBER` (`universalSettings.dataType`: `'float'` / `'int'` / `'bigint'`), `NUMERIC` (keyfi duyarlılık), `RATING`, `POSITION` | +| Tarihler | `DATE`, `DATE_TIME` | +| Seçim | `BOOLEAN`, `SELECT`, `MULTI_SELECT` | +| Bileşik | `FULL_NAME`, `ADDRESS`, `EMAILS`, `PHONES`, `LINKS`, `CURRENCY`, `ACTOR`, `FILES` | +| Tanımlayıcılar ve ilişkiler | `UUID`, `RELATION`, `MORPH_RELATION` (bkz. [İlişkiler](/l/tr/developers/extend/apps/data/relations)) | +| Sistem | `TS_VECTOR` (sunucu tarafından yönetilen tam metin arama vektörü) | + +Bileşik tipler birden çok alt alan depolar (ör. `FULL_NAME` = ad + soyad; `CURRENCY` = `amountMicros` + `currencyCode`). `SELECT` ve `MULTI_SELECT`, yukarıdaki örnekte olduğu gibi bir `options` dizisi gerektirir. + ## Varsayılan değerler Sabit (literal) dize varsayılanları, dize **içinde** tek tırnak içine alınmış olmalıdır — `defaultValue: "'Draft'"`, `defaultValue: "Draft"` değil. Bu nedenle yukarıdaki `status` alanı `` `'${PostCardStatus.DRAFT}'` `` kullanır. diff --git a/packages/twenty-docs/l/tr/developers/extend/apps/getting-started/project-structure.mdx b/packages/twenty-docs/l/tr/developers/extend/apps/getting-started/project-structure.mdx index cf97904ba4..647bd23fed 100644 --- a/packages/twenty-docs/l/tr/developers/extend/apps/getting-started/project-structure.mdx +++ b/packages/twenty-docs/l/tr/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 ``` ## Temel dosyalar -| Dosya / Klasör | Amaç | -| ---------------------------------------- | --------------------------------------------------------------------------- | -| `src/application-config.ts` | **Gerekli.** Uygulamanızın ana yapılandırma dosyası. | -| `src/default-role.ts` | Mantık işlevlerinizin neye erişebileceğini denetleyen varsayılan rol. | -| `src/constants/universal-identifiers.ts` | Otomatik oluşturulan UUID'ler ve meta veriler (görünen ad, açıklama). | -| `src/__tests__/` | Entegrasyon testleri (kurulum + örnek test). | -| `public/` | Uygulamanızla birlikte sunulan statik varlıklar (görüntüler, yazı tipleri). | +| Dosya / Klasör | Amaç | +| -------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- | +| `src/application-config.ts` | **Gerekli.** Uygulamanızın ana yapılandırma dosyası. | +| `src/default-role.ts` | Mantık işlevlerinizin neye erişebileceğini denetleyen varsayılan rol. | +| `src/constants/universal-identifiers.ts` | Otomatik oluşturulan UUID'ler ve meta veriler (görünen ad, açıklama). | +| `src/front-components/`, `src/navigation-menu-items/`, `src/page-layouts/` | Bir başlangıç karşılama sayfası: kenar çubuğundan erişilebilen, bağımsız bir sayfa düzeni tarafından oluşturulan bir ön bileşen. | +| `src/__tests__/` | Uygulamayı gerçek bir sunucuya karşı senkronize eden (genel kurulumu ile birlikte) bir birim testi artı bir entegrasyon testi. | +| `public/` | Uygulamanızla birlikte sunulan statik varlıklar (görüntüler, yazı tipleri). | +| `AGENTS.md` / `CLAUDE.md` | Uygulama üzerinde çalışan yapay zekâ kodlama aracıları için yönergeler. | **Dosya organizasyonu size kalmış.** Yukarıdaki klasörler birer konvansiyondur — SDK, dosyanın nerede olduğundan bağımsız olarak `export default defineEntity(...)` çağrılarını AST analizi yoluyla algılar. @@ -47,15 +60,18 @@ Her iki Twenty SDK paketi de `dependencies` değil, `devDependencies` altında o { "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" } } ``` +İskele oluşturucu, `twenty-sdk` ve `twenty-client-sdk` paketlerini kendi sürümüne sabitler — yükseltme yaparken bu ikisini senkron tutun. + * **`twenty-sdk`**, `twenty` CLI'yi ve derleme/iskelet oluşturma araçlarını sağlar. Yalnızca geliştirme ve derleme zamanında çalışır ve yayımlanmış uygulamanızın çalışma zamanında asla içe aktarılmaz. * **`twenty-client-sdk`** uygulama kodunuz tarafından (`CoreApiClient`, `MetadataApiClient`, `RestApiClient`) içe aktarılır, ancak Twenty bunu çalışma zamanında sağlar — mantık fonksiyonları bunu oluşturulan bir SDK katmanından alır ve ön yüz bileşenleri bunu sunucu tarafından sunulan modüllerden çözümler. Yüklediğiniz kopya yalnızca tür denetimi ve dağıtım zamanındaki derleme için kullanılır, bu yüzden dağıtılan paket ile birlikte gönderilmesine gerek yoktur. -`dependencies` altında bu paketlerden herhangi birinin tutulması, onu kurulu uygulamanın çalışma zamanı paketine dahil eder ve orada gereksiz yük haline getirir. `twenty build`, bunlardan herhangi biri hâlâ `dependencies` altında listelendiğinde bir uyarı verir. +`dependencies` altında bu paketlerden herhangi birinin tutulması, onu kurulu uygulamanın çalışma zamanı paketine dahil eder ve orada gereksiz yük haline getirir. `twenty dev:build`, bunlardan herhangi biri hâlâ `dependencies` altında listelendiğinde bir uyarı verir. Uygulamanızın kendi çalışma zamanı bağımlılıklarını (mantık fonksiyonlarınızın çalışma zamanında gerçekten içe aktardığı kütüphaneler) her zamanki gibi `dependencies` altına ekleyin. diff --git a/packages/twenty-docs/l/tr/developers/extend/apps/getting-started/quick-start.mdx b/packages/twenty-docs/l/tr/developers/extend/apps/getting-started/quick-start.mdx index 56a649099a..f7e04eb644 100644 --- a/packages/twenty-docs/l/tr/developers/extend/apps/getting-started/quick-start.mdx +++ b/packages/twenty-docs/l/tr/developers/extend/apps/getting-started/quick-start.mdx @@ -6,17 +6,17 @@ description: İlk Twenty uygulamanızı dakikalar içinde oluşturun. ## Ön Gereksinimler -* **Node.js 24+** — [Buradan indirin](https://nodejs.org/) +* **Node.js 24.5+** — [Buradan indirin](https://nodejs.org/) * **Yarn 4** — Corepack aracılığıyla Node ile birlikte gelir. Etkinleştirin: `corepack enable` * **Docker** — [Buradan indirin](https://www.docker.com/products/docker-desktop/). Yerel bir Twenty sunucusunu çalıştırmak için gereklidir. Zaten başka bir yerde Twenty çalıştırıyorsanız atlayın. Bir Twenty uygulaması oluşturmanın üç aşaması vardır. İskelet oluşturucu bunları tek bir sorunsuz akış komutuna indirger, ancak her aşama ayrı bir kavramdır — bir şeyler başarısız olduğunda hangi aşamada olduğunuzu bilmek neyi düzeltmeniz gerektiğini söyler. -| Aşama | Ne yaparsınız | Araç | Sonuç | -| ---------------------------- | ------------------------------------------------- | ----------------------------- | ---------------------------------- | -| **1. İskelet Oluşturma** | Uygulamanın kaynak kodunu oluşturun | `npx create-twenty-app` | Diskte bir TypeScript projesi | -| **2. Bir sunucu çalıştırma** | Eşitleme yapacağınız bir Twenty sunucusu başlatın | Docker + `yarn twenty server` | Çalışan bir Twenty örneği | -| **3. Eşitleme** | Kodunuzu sunucuya canlı olarak eşitleyin | `yarn twenty dev` | Değişiklikleriniz arayüzde görünür | +| Aşama | Ne yaparsınız | Araç | Sonuç | +| ---------------------------- | ------------------------------------------------- | ----------------------------------- | ---------------------------------- | +| **1. İskelet Oluşturma** | Uygulamanın kaynak kodunu oluşturun | `npx create-twenty-app` | Diskte bir TypeScript projesi | +| **2. Bir sunucu çalıştırma** | Eşitleme yapacağınız bir Twenty sunucusu başlatın | Docker + `yarn twenty docker:start` | Çalışan bir Twenty örneği | +| **3. Eşitleme** | Kodunuzu sunucuya canlı olarak eşitleyin | `yarn twenty dev` | Değişiklikleriniz arayüzde görünür | --- @@ -28,7 +28,7 @@ Bir Twenty uygulaması oluşturmanın üç aşaması vardır. İskelet oluşturu npx create-twenty-app@latest my-twenty-app ``` -Sizden bir ad ve açıklama istenir — varsayılanlar için **Enter** tuşuna basın. Bu, `my-twenty-app/` içinde bir başlangıç `application-config.ts`, varsayılan bir rol, bir CI iş akışı ve bir entegrasyon testi ile bir TypeScript projesi oluşturur. +İskele oluşturucu etkileşimsizdir: dizin adı uygulama adı olur. Oluşturulan üstveriyi özelleştirmek için `--display-name` ve `--description` parametrelerini iletin (bunu daha sonra `src/constants/universal-identifiers.ts` içinde de düzenleyebilirsiniz). Bu, `my-twenty-app/` içinde bir başlangıç `application-config.ts`, varsayılan bir rol, CI/CD iş akışları ve bir entegrasyon testi ile bir TypeScript projesi oluşturur. **Bu aşamadan sonra:** makinenizde uygulamanın kaynak kodu bulunur. Henüz çalışmıyor — bu, 2. Aşama. @@ -38,28 +38,14 @@ Sizden bir ad ve açıklama istenir — varsayılanlar için **Enter** tuşuna b Uygulamanızın eşitleme yapacağı bir Twenty sunucusuna ihtiyacı vardır. Sunucu, Docker içinde yerel olarak çalışan tam bir Twenty örneğidir — UI, GraphQL API, PostgreSQL. Yerel kodunuz tanımlarını bu sunucuya yükler; bu da onların arayüzde görünmesini sağlar. -İskelet oluşturucu sizin için bir tane başlatmayı teklif eder: +İskele oluşturucu bunu sizin için başlatır: Docker çalışırken `twentycrm/twenty-app-dev` imajını çeker, onu `2020` portunda başlatır ve CLI'yı önceden doldurulmuş demo çalışma alanına (`tim@apple.dev`) karşı kimlik doğrular — oturum açmanız gerekmez. -> **Yerel bir Twenty örneği kurmak ister misiniz?** - -* **Evet (önerilir)** — `twentycrm/twenty-app-dev` Docker imajını çeker ve `2020` portunda başlatır. Önce Docker'ın çalıştığından emin olun. -* **Hayır** — Zaten bağlanmak istediğiniz bir Twenty sunucunuz varsa bunu seçin. Bunu daha sonra `yarn twenty remote:add` ile bağlayabilirsiniz. - -
- Yerel örnek başlatılsın mı? -
- -Sunucu çalışır duruma geldiğinde, oturum açmak için bir tarayıcı açılır. Önceden eklenmiş demo hesabını kullanın: - -* **E-posta:** `tim@apple.dev` -* **Parola:** `tim@apple.dev` +Bunun yerine mevcut bir Twenty sunucusuna bağlanmak için `--url \` parametresini iletin. Uzak sunucular OAuth ile kimlik doğrular: oturum açabilmeniz ve **Authorize**'a tıklayabilmeniz için bir tarayıcı açılır; bu, CLI'ye çalışma alanınıza erişim sağlar. (Yerelde de `--authentication-method oauth` ile OAuth kullanmayı tercih edebilirsiniz — `tim@apple.dev` / `tim@apple.dev` ile oturum açın.)
Twenty oturum açma ekranı
-Sonraki ekranda **Authorize**'a tıklayın — bu işlem CLI'nin çalışma alanınıza erişmesine izin verir. -
Twenty CLI yetkilendirme ekranı
@@ -117,27 +103,31 @@ Daha ayrıntılı çıktı (derleme günlükleri, eşitleme istekleri, hata izle ### CI ve betikler için tek seferlik eşitleme -Tek bir derleme + eşitleme çalıştırıp çıkmak için `--once` parametresini geçin — aynı ardışık düzen, izleyici yok: +Bir izleyici olmadan aynı boru hattını bir kez çalıştırmak için `plan` ve `apply` komutlarını kullanın: ```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 ``` -| Komut | Davranış | Ne zaman kullanılmalı | -| ---------------------------------- | ----------------------------------------------------------------------- | --------------------------------------------------------------------------- | -| `yarn twenty dev` | Her değişiklikte izler ve yeniden eşitler. Siz durdurana kadar çalışır. | Etkileşimli yerel geliştirme. | -| `yarn twenty dev --once` | Tek derleme + eşitleme; başarıda `0`, başarısızlıkta `1` ile çıkar. | CI, pre-commit kancaları, AI ajanları, betiklenmiş iş akışları. | -| `yarn twenty dev --once --dry-run` | Meta veri değişikliklerini **uygulamadan oluşturur ve yazdırır**. | Bir senkronizasyonun, ona başlamadan önce neleri değiştireceğini incelemek. | +| Komut | Davranış | Ne zaman kullanılmalı | +| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------- | +| `yarn twenty dev` | Her değişiklikte izler ve yeniden eşitler. Siz durdurana kadar çalışır. | Etkileşimli yerel geliştirme. | +| `yarn twenty apply` | Tek derleme + eşitleme; başarıda `0`, başarısızlıkta `1` ile çıkar. Yıkıcı değişiklikler için onay ister (atlamak için `--force` parametresini iletin). | CI, pre-commit kancaları, AI ajanları, betiklenmiş iş akışları. | +| `yarn twenty plan` | Meta veri değişikliklerini **uygulamadan oluşturur ve yazdırır**. | Bir senkronizasyonun, ona başlamadan önce neleri değiştireceğini incelemek. | -Her iki kipin de kimliği doğrulanmış bir uzak sunucuya ihtiyaç duyar. `--dry-run` hakkında daha fazla bilgi için [Senkronizasyon ve kurtarma](/l/tr/developers/extend/apps/operations/sync-and-recovery#previewing-changes-dry-run) bölümüne bakın. +Tüm kiplerin kimliği doğrulanmış bir uzak sunucuya ihtiyacı vardır. `plan` hakkında daha fazla bilgi için [Senkronizasyon ve kurtarma](/l/tr/developers/extend/apps/operations/sync-and-recovery#previewing-changes-plan) bölümüne bakın. + + +`yarn twenty dev --once` ve `yarn twenty dev --once --dry-run`, `yarn twenty apply` ve `yarn twenty plan` için kullanımdan kaldırılmış takma adlardır. + ### Geliştirme kipi seçenekleri | Bayrak | Açıklama | | ------------------------------------- | ------------------------------------------------------------------------------------------ | -| `--once` | Bir kez derleyip eşitleyin, ardından çıkın. | -| `--dry-run` | `--once` ile meta veri değişikliklerini uygulamadan önizleyin. Hiçbir şey yazmaz. | -| `--debounceMs \` | Dosya değişikliği geciktirme süresini milisaniye cinsinden ayarlayın (varsayılan: `2000`). | +| `--force` | Onay olmadan yıkıcı değişiklikleri (silme işlemlerini) uygular. | +| `--debounceMs \` | Dosya değişikliği geciktirme süresini milisaniye cinsinden ayarlayın (varsayılan: `1000`). | | `--verbose` / `--debug` | Ayrıntılı derleme günlüklerini, eşitleme isteklerini ve hata izlerini gösterin. | ## Oluşturabilecekleriniz diff --git a/packages/twenty-docs/l/tr/developers/extend/apps/getting-started/scaffolding.mdx b/packages/twenty-docs/l/tr/developers/extend/apps/getting-started/scaffolding.mdx index a5fb866886..825e2adbce 100644 --- a/packages/twenty-docs/l/tr/developers/extend/apps/getting-started/scaffolding.mdx +++ b/packages/twenty-docs/l/tr/developers/extend/apps/getting-started/scaffolding.mdx @@ -34,6 +34,10 @@ yarn twenty dev:add frontComponent | Görünüm | `yarn twenty dev:add view` | `src/views/\.ts` | | Gezinme menüsü öğesi | `yarn twenty dev:add navigationMenuItem` | `src/navigation-menu-items/\.ts` | | Sayfa düzeni | `yarn twenty dev:add pageLayout` | `src/page-layouts/\.ts` | +| Sayfa düzeni sekmesi | `yarn twenty dev:add pageLayoutTab` | `src/page-layout-tabs/\.ts` | +| Komut menüsü öğesi | `yarn twenty dev:add commandMenuItem` | `src/command-menu-items/\.ts` | +| Görünüm alanı | `yarn twenty dev:add viewField` | `src/view-fields/\.ts` | +| Bağlantı sağlayıcısı | `yarn twenty dev:add connectionProvider` | `src/connection-providers/\.ts` | ## İskelet oluşturucunun ürettikleri diff --git a/packages/twenty-docs/l/tr/developers/extend/apps/getting-started/troubleshooting.mdx b/packages/twenty-docs/l/tr/developers/extend/apps/getting-started/troubleshooting.mdx index 53c086cc88..a789c903f2 100644 --- a/packages/twenty-docs/l/tr/developers/extend/apps/getting-started/troubleshooting.mdx +++ b/packages/twenty-docs/l/tr/developers/extend/apps/getting-started/troubleshooting.mdx @@ -5,10 +5,10 @@ icon: wrench --- * **Docker hataları** — `yarn twenty docker:start` öncesinde Docker Desktop'ın (veya daemon'un) çalıştığından emin olun. Hata iletisi, işletim sisteminiz için doğru başlatma komutunu gösterecektir. -* **Yanlış Node sürümü** — 24+ gerekir. `node -v` ile kontrol edin. +* **Yanlış Node sürümü** — 24.5+ gerekiyor (`engines.node: ^24.5.0`). `node -v` ile kontrol edin. * **Yarn 4 eksik** — `corepack enable` çalıştırın. * **Bağımlılıklar bozuk** — `rm -rf node_modules && yarn install`. * **`twenty-sdk` v2.8.0 sürümüne yükselttikten sonra oluşan hatalar** — v2.8.0 sürümünde `dependencies` içinden `devDependencies` içine taşındı. [Proje Yapısı → Bağımlılıklar](/l/tr/developers/extend/apps/getting-started/project-structure#dependencies) bölümüne bakın. -* **`twenty build`, `dependencies` altındaki `twenty-client-sdk` konusunda uyarır** — Bu paket, çalışma zamanında Twenty tarafından sağlanır, bu nedenle `twenty-sdk` ile birlikte `devDependencies` altına taşınmalıdır. [Proje Yapısı → Bağımlılıklar](/l/tr/developers/extend/apps/getting-started/project-structure#dependencies) bölümüne bakın. +* **`dependencies` altındaki `twenty-client-sdk` hakkında `twenty dev:build` uyarısı** — Bu paket, çalışma zamanında Twenty tarafından sağlanır, bu nedenle `twenty-sdk` ile birlikte `devDependencies` altına taşınmalıdır. [Proje Yapısı → Bağımlılıklar](/l/tr/developers/extend/apps/getting-started/project-structure#dependencies) bölümüne bakın. Takıldınız mı? [Twenty Discord](https://discord.com/channels/1130383047699738754/1130386664812982322) üzerinde yardım isteyin. diff --git a/packages/twenty-docs/l/tr/developers/extend/apps/layout/command-menu-items.mdx b/packages/twenty-docs/l/tr/developers/extend/apps/layout/command-menu-items.mdx index dac2ba29d6..d2fe5c8eb6 100644 --- a/packages/twenty-docs/l/tr/developers/extend/apps/layout/command-menu-items.mdx +++ b/packages/twenty-docs/l/tr/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({ ## Yapılandırma alanları -| Alan | Zorunlu | Açıklama | -| --------------------------------------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `universalIdentifier` | Evet | Komut için kalıcı benzersiz kimlik | -| `label` | Evet | Komut menüsünde (Cmd+K) gösterilen tam etiket | -| `frontComponentUniversalIdentifier` | Evet | Bu komutun açtığı ön bileşenin `universalIdentifier` değeri | -| `shortLabel` | Hayır | Sabitlenmiş hızlı işlem düğmesinde görüntülenen daha kısa etiket | -| `icon` | Hayır | Etiketin yanında görüntülenen simge adı (örn. `'IconBolt'`, `'IconSend'`) | -| `isPinned` | Hayır | `true` olduğunda, komutu sayfanın sağ üst köşesinde bir hızlı işlem düğmesi olarak gösterir | -| `availabilityType` | Hayır | Komutun nerede görüneceğini kontrol eder: `'GLOBAL'` (her zaman kullanılabilir), `'RECORD_SELECTION'` (yalnızca kayıtlar seçiliyken) veya `'FALLBACK'` (başka hiçbir komut eşleşmediğinde gösterilir) | -| `availabilityObjectUniversalIdentifier` | Hayır | Komutu belirli bir nesne türünün sayfalarıyla sınırlandırın (örn. yalnızca Company kayıtlarında) | -| `conditionalAvailabilityExpression` | Hayır | Görünürlüğü dinamik olarak kontrol eden bir boolean ifade (aşağıya bakın) | +| Alan | Zorunlu | Açıklama | +| --------------------------------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `universalIdentifier` | Evet | Komut için kalıcı benzersiz kimlik | +| `label` | Evet | Komut menüsünde (Cmd+K) gösterilen tam etiket | +| `frontComponentUniversalIdentifier` | Evet | Bu komutun açtığı ön bileşenin `universalIdentifier` değeri | +| `shortLabel` | Hayır | Sabitlenmiş hızlı işlem düğmesinde görüntülenen daha kısa etiket | +| `icon` | Hayır | **Kullanımdan kaldırıldı** — uygulama simgesi tercih edildiği için yok sayılır; ayarlanırsa oluşturma sırasında bir uyarı verir | +| `isPinned` | Hayır | `true` olduğunda, komutu sayfanın sağ üst köşesinde bir hızlı işlem düğmesi olarak gösterir | +| `availabilityType` | Hayır | Komutun nerede görüneceğini kontrol eder: `'GLOBAL'` (her zaman kullanılabilir), `'GLOBAL_OBJECT_CONTEXT'` (yalnızca bir nesne bağlamı olan sayfalarda — indeks ve kayıt sayfaları), `'RECORD_SELECTION'` (yalnızca kayıtlar seçiliyken) veya `'FALLBACK'` (başka hiçbir komut eşleşmediğinde gösterilir) | +| `availabilityObjectUniversalIdentifier` | Hayır | Komutu belirli bir nesne türünün sayfalarıyla sınırlandırın (örn. yalnızca Company kayıtlarında) | +| `conditionalAvailabilityExpression` | Hayır | Görünürlüğü dinamik olarak kontrol eden bir boolean ifade (aşağıya bakın) | ## Arayüzsüz komutlar Bir [arayüzsüz ön bileşen](/l/tr/developers/extend/apps/layout/front-components#headless-vs-non-headless) ile eşleştirilmiş bir komut menüsü öğesi, tek tıklamayla bir eylem sunmanın — kod çalıştırma, gezinme veya onaylayıp yürütme — yaygın kullanılan biçimidir. Ön Bileşenler sayfası, eylem-ve-kaldırma modelini yöneten [SDK Command bileşenlerini](/l/tr/developers/extend/apps/layout/front-components#sdk-command-components) (`Command`, `CommandLink`, `CommandModal`, `CommandOpenSidePanelPage`) kapsar. -Tipik bir akış: - -```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, -}); -``` +Tipik bir akış: başsız bir bileşen `` oluşturur (bkz. [tam örnek](/l/tr/developers/extend/apps/layout/front-components#sdk-command-components)) ve komut menüsü öğesi ona işaret eder: ```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/tr/developers/extend/apps/layout/front-components.mdx b/packages/twenty-docs/l/tr/developers/extend/apps/layout/front-components.mdx index f5a0fef52a..52a8253a59 100644 --- a/packages/twenty-docs/l/tr/developers/extend/apps/layout/front-components.mdx +++ b/packages/twenty-docs/l/tr/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` ile senkronize ettikten sonra (veya tek seferlik bir `yarn twenty dev --once` çalıştırdıktan sonra), hızlı işlem sayfanın sağ üst köşesinde görünür: +`yarn twenty dev` ile senkronize ettikten sonra (veya tek seferlik bir `yarn twenty apply` çalıştırdıktan sonra), hızlı işlem sayfanın sağ üst köşesinde görünür:
Sağ üst köşedeki hızlı işlem düğmesi @@ -88,11 +87,11 @@ Komutların ötesinde, bir ön uç bileşenini bir **sayfa düzeninde** widget 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 @@ Bileşen `null` döndürdüğü için, Twenty bunun için bir kapsayıcı oluşt `twenty-sdk` paketi, headless ön uç bileşenler için tasarlanmış dört Command yardımcı bileşeni sağlar. Her bileşen bağlandığında bir eylem yürütür, hataları bir snackbar bildirimi göstererek ele alır ve tamamlandığında ön bileşeni otomatik olarak kaldırır. -Bunları `twenty-sdk/command` içinden içe aktarın: +Bunları `twenty-sdk/front-component` içinden içe aktarın: * **`Command`** — `execute` prop'u aracılığıyla async bir geri çağrıyı çalıştırır. * **`CommandLink`** — Bir uygulama yoluna gider. Props: `to`, `params`, `queryParams`, `options`. @@ -127,8 +126,8 @@ Bunları `twenty-sdk/command` içinden içe aktarın: ```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 @@ Ve yürütmeden önce onay istemek için `CommandModal` kullanan bir örnek: ```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` ile bildirilen bir mantık işlevi, rota yolunda HTTP üzerinden erişilebilir durumdadır. Twenty, işlevlerinizin sunulduğu temel URL’yi, çağrıyı kimlik doğrulayan `TWENTY_APP_ACCESS_TOKEN` ile birlikte, worker’a `TWENTY_FUNCTIONS_URL` olarak enjekte eder. Kendi işlevlerinizi çağırmak için henüz özel bir SDK istemcisi yoktur, bu yüzden onları basit bir `fetch` ile çağırın: -> **Twenty Cloud üzerinde, HTTP ile tetiklenen mantık işlevleri, çalışma alanı başına ayrılmış özel bir etki alanında** `https://\.twenty.com\` adresinde sunulur — bu, `TWENTY_FUNCTIONS_URL`'ün tam olarak çözümlendiği değerdir. Harici çağrıcılar için, tam URL’yi işlevin **HTTP trigger** ayarlarından veya uygulamanın **Settings** sekmesinden kopyalayın. +> **Twenty Cloud üzerinde, HTTP ile tetiklenen mantık işlevleri, çalışma alanı başına ayrılmış özel bir etki alanında** `https://\.withtwenty.com\` adresinde sunulur — bu, `TWENTY_FUNCTIONS_URL`'ün tam olarak çözümlendiği değerdir. Harici çağrıcılar için, tam URL’yi işlevin **HTTP trigger** ayarlarından veya uygulamanın **Settings** sekmesinden kopyalayın. Eski `/s/` fonksiyon rotası **kullanımdan kaldırılmıştır (deprecated)** ve **2026-07-24 tarihinde devre dışı bırakılacaktır**. Bunun yerine yukarıdaki `TWENTY_FUNCTIONS_URL` değerini kullanın ve o tarihten önce sabit (hard-coded) tüm `/s/` URL’lerini taşıyın. `/s/` rotası, self-hosting için kullanılabilir olmaya devam eder. @@ -212,7 +210,7 @@ Başsız bir ön bileşen, çağrıyı `Command` bileşeni aracılığıyla moun ```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 @@ Bileşeninizin içinde, geçerli kullanıcıya, kayda ve bileşen örneğine eri 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 @@ Bir eylem tamamlandıktan sonra bir snackbar göstermek ve yan paneli kapatmak i ```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({ Birden çok seçili kaydı yönetmek için `useSelectedRecordIds()` kullanın. Bu, toplu işlemler için kullanışlıdır: ```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, - }, +}); +``` + +Bunu, kayıt seçimleriyle kısıtlanmış bir [komut menüsü öğesi](/l/tr/developers/extend/apps/layout/command-menu-items) ile yüzeye çıkarın: + +```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/tr/developers/extend/apps/layout/navigation-menu-items.mdx b/packages/twenty-docs/l/tr/developers/extend/apps/layout/navigation-menu-items.mdx index 3e9e897353..2c312a24db 100644 --- a/packages/twenty-docs/l/tr/developers/extend/apps/layout/navigation-menu-items.mdx +++ b/packages/twenty-docs/l/tr/developers/extend/apps/layout/navigation-menu-items.mdx @@ -35,6 +35,8 @@ export default defineNavigationMenuItem({ * `position`, kenar çubuğundaki sıralamayı kontrol eder. +* Enum ayrıca dahili olarak kullanıcı tarafından oluşturulan kayıt favorileri için kullanılan `NavigationMenuItemType.RECORD` öğesini de içerir — bir kayıt başvuracak bir alan olmadığı için bir uygulama manifestinden kullanılamaz. + * `icon` ve `color` isteğe bağlıdır ve öğenin görünümünü özelleştirir. * `folderUniversalIdentifier`, herhangi bir öğede de mevcuttur ve onu bir `FOLDER` türü üst öğenin içine yerleştirmek için kullanılır. diff --git a/packages/twenty-docs/l/tr/developers/extend/apps/layout/views.mdx b/packages/twenty-docs/l/tr/developers/extend/apps/layout/views.mdx index 1b2b6882d0..70d7f4e01e 100644 --- a/packages/twenty-docs/l/tr/developers/extend/apps/layout/views.mdx +++ b/packages/twenty-docs/l/tr/developers/extend/apps/layout/views.mdx @@ -33,17 +33,32 @@ export default defineView({ ## Önemli noktalar * `objectUniversalIdentifier`, bu görünümün hangi nesneye uygulanacağını belirtir. Bu, tanımladığınız özel bir nesne veya standart bir Twenty nesnesi olabilir. -* `key`, görünüm türünü belirler — `ViewKey.INDEX`, nesne için ana liste görünümüdür. +* `key: ViewKey.INDEX` görünümü nesnenin ana liste görünümü olarak işaretler (bir `OBJECT` gezinme öğesinin açtığı görünüm). * `fields`, hangi sütunların görüneceğini ve hangi sırayla görüneceğini kontrol eder. Her alan bir `fieldMetadataUniversalIdentifier` öğesine referans verir. -* Daha gelişmiş yapılandırmalar için `filters`, `filterGroups`, `groups` ve `fieldGroups` da tanımlayabilirsiniz. +* Daha gelişmiş yapılandırmalar için `filters`, `filterGroups`, `sorts`, `groups` ve `fieldGroups` da tanımlayabilirsiniz. * `position`, aynı nesne için birden fazla görünüm olduğunda sıralamayı kontrol eder. +## İsteğe bağlı özellikler + +| Özellik | Değerler | Açıklama | +| ----------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | +| `type` | `ViewType.TABLE` (varsayılan), `ViewType.KANBAN`, `ViewType.CALENDAR` | Kayıtların nasıl düzenlendiği. (`FIELDS_WIDGET` / `TABLE_WIDGET` da mevcuttur ancak sayfa düzeni bileşenleri tarafından dahili olarak kullanılır.) | +| `visibility` | `ViewVisibility.WORKSPACE` (varsayılan), `ViewVisibility.UNLISTED` | Görünümün tüm çalışma alanı için listelenip listelenmediği veya seçicilerden gizlenip gizlenmediği. | +| `openRecordIn` | `ViewOpenRecordIn.SIDE_PANEL` (varsayılan), `ViewOpenRecordIn.RECORD_PAGE` | Bir kayda tıklandığında onun nerede açıldığı. | +| `sorts` | `{ fieldMetadataUniversalIdentifier, direction: ViewSortDirection.ASC \| DESC }[]` | Varsayılan sıralama düzeni. | +| `isCompact` | `boolean` | Sıkıştırılmış satır gösterimi. | +| `mainGroupByFieldMetadataUniversalIdentifier` + `shouldHideEmptyGroups` | — | Kayıtları bir alana göre gruplayın (ör. kanban sütunları). | +| `kanbanAggregateOperation`, `kanbanAggregateOperationFieldMetadataUniversalIdentifier`, `kanbanColumnWidth` | `AggregateOperations.*` | Kanban sütunu toplamları ve boyutlandırması. | +| `calendarLayout`, `calendarFieldMetadataUniversalIdentifier` | `ViewCalendarLayout.DAY` / `WEEK` / `MONTH` | Takvim görünümleri: düzen ve kayıtları konumlandıran tarih alanı. | + +Yukarıdaki tüm enum"lar `twenty-sdk/define` içinden dışa aktarılır. + ## Filtreler Bir görünüm, önceden uygulanmış filtrelerle gelebilir. Her filtrenin üç koordinatı vardır: filtrelenen **alan**, **işleç** (nasıl karşılaştırılacağı) ve **değer** (neyle karşılaştırılacağı). Üçünün de hizalı olması gerekir — bir alan türüne uygulanmayan bir işlecin kullanılması, senkronizasyon sırasında reddedilir. ```ts -import { ViewFilterOperand } from 'twenty-shared/types'; +import { ViewFilterOperand } from 'twenty-sdk/define'; filters: [ { diff --git a/packages/twenty-docs/l/tr/developers/extend/apps/logic/logic-functions.mdx b/packages/twenty-docs/l/tr/developers/extend/apps/logic/logic-functions.mdx index f65c1d46ce..391532e275 100644 --- a/packages/twenty-docs/l/tr/developers/extend/apps/logic/logic-functions.mdx +++ b/packages/twenty-docs/l/tr/developers/extend/apps/logic/logic-functions.mdx @@ -51,8 +51,12 @@ export default defineLogicFunction({ ``` Kullanılabilir tetikleyici türleri: -* **httpRoute**: İşlevinizi bir HTTP yolu ve yöntemiyle **`/s/` uç noktasının altında** kullanıma sunar: -> örn. `path: '/post-card/create'` `https://your-twenty-server.com/s/post-card/create` adresinden çağrılabilir +* **httpRoute**: İşlevinizi çalışma alanınızın **işlevler temel URL'sinde** bir HTTP yolunda ve yönteminde açığa çıkarır — Twenty'nin `TWENTY_FUNCTIONS_URL` olarak enjekte ettiği değer (Twenty Cloud'da, çalışma alanı başına ayrılmış özel bir alan adı): +> örn. `path: '/post-card/create'` `https://your-workspace.withtwenty.com/post-card/create` adresinden çağrılabilir + + +Eski `/s/` ön ekli rota (`https://your-twenty-server.com/s/post-card/create`) **Twenty Cloud üzerinde kullanım dışıdır (deprecated)** ve **2026-07-24** tarihinde devre dışı bırakılacaktır. Yalıtılmış bir işlev alanı yapılandırmayan kendi kendine barındırılan ve yerel örnekler için kullanılabilir durumda kalır — ayarlanmışsa `TWENTY_FUNCTIONS_URL` kullanın ve aksi takdirde `\/s/\` rotasına geri dönün. + Arayüzsüz bir ön uç bileşeninden rota tarafından tetiklenen mantık fonksiyonunu çağırmak için bkz. [Mantık fonksiyonu çağırma](/l/tr/developers/extend/apps/layout/front-components#calling-a-logic-function). diff --git a/packages/twenty-docs/l/tr/developers/extend/apps/logic/overview.mdx b/packages/twenty-docs/l/tr/developers/extend/apps/logic/overview.mdx index 04d855b7c0..9b9dbd7bcc 100644 --- a/packages/twenty-docs/l/tr/developers/extend/apps/logic/overview.mdx +++ b/packages/twenty-docs/l/tr/developers/extend/apps/logic/overview.mdx @@ -42,7 +42,7 @@ Bir mantık fonksiyonu bir veya daha fazla tetikleyici seçer — aşağıdaki h | Tetikleyici | Ne zaman çalışır | Ayar | | --------------------- | ---------------------------------------------------------------------------- | ------------------------------- | -| **HTTP rotası** | Bir istek `/s/\` endpoint'inize ulaşır | `httpRouteTriggerSettings` | +| **HTTP rotası** | Bir istek, işlevinizin genel URL'sine ulaşır | `httpRouteTriggerSettings` | | **Cron** | Bir CRON ifadesi eşleştiğinde | `cronTriggerSettings` | | **Veritabanı olayı** | Bir çalışma alanı kaydı oluşturulduğunda, güncellendiğinde veya silindiğinde | `databaseEventTriggerSettings` | | **Yapay zeka aracı** | Bir Twenty yapay zeka özelliği, fonksiyonunuzu çağırmaya karar verdiğinde | `toolTriggerSettings` | diff --git a/packages/twenty-docs/l/tr/developers/extend/apps/operations/cli.mdx b/packages/twenty-docs/l/tr/developers/extend/apps/operations/cli.mdx index e9cd627aad..4d5b41b73f 100644 --- a/packages/twenty-docs/l/tr/developers/extend/apps/operations/cli.mdx +++ b/packages/twenty-docs/l/tr/developers/extend/apps/operations/cli.mdx @@ -4,7 +4,25 @@ description: Fonksiyonları çalıştırmak, günlükleri akış olarak izlemek, icon: terminal --- -`dev`, `dev:build`, `dev:add` ve `dev:typecheck` dışında, `yarn twenty` CLI, fonksiyonları çalıştırma, günlükleri görüntüleme ve uygulama kurulumlarını yönetme komutları sağlar. +`yarn twenty` CLI, uygulama ile ilgili her şey için arayüzünüzdür. Tam komut listesi: + +| Komut | Ne yapar | Şurada belgelenmiştir | +| ----------------------------------------------- | ------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- | +| `dev` | Kaynak dosyaları izleyin ve değişiklikleri canlı olarak eşitleyin | [Hızlı Başlangıç](/l/tr/developers/extend/apps/getting-started/quick-start) | +| `plan` | Meta veri değişikliklerini uygulamadan önizleyin | [Senkronizasyon ve kurtarma](/l/tr/developers/extend/apps/operations/sync-and-recovery#previewing-changes-plan) | +| `apply` | Planı gösterdikten sonra meta veri değişikliklerini uygulayın | [Senkronizasyon ve kurtarma](/l/tr/developers/extend/apps/operations/sync-and-recovery) | +| `dev:build` | Uygulamayı derleyin ve API istemcisini oluşturun (`.tgz` paketlemek için `--tarball`) | [Yayınlama](/l/tr/developers/extend/apps/operations/publishing) | +| `dev:typecheck` | TypeScript tür kontrolünü çalıştırın | [Testing](/l/tr/developers/extend/apps/operations/testing) | +| `dev:add` | Yeni bir varlık iskeleti oluşturun | [İskele oluşturma](/l/tr/developers/extend/apps/getting-started/scaffolding) | +| `dev:generate-client` | Türlendirilmiş API istemcisini yeniden oluşturun | bu sayfa | +| `dev:function:exec` / `dev:function:logs` | Fonksiyonları çalıştırın ve günlüklerini akış halinde alın | bu sayfa | +| `dev:translations-extract` | Çevrilebilir dizeleri `locales/` kataloglarına çıkarın | [Çeviriler](/l/tr/developers/extend/apps/translations/overview) | +| `dev:catalog-sync` | Pazar yeri katalog eşitlemesini tetikleyin | [Yayınlama](/l/tr/developers/extend/apps/operations/publishing#how-marketplace-discovery-works) | +| `app:publish` / `app:install` / `app:uninstall` | Yayın yaşam döngüsü | [Yayınlama](/l/tr/developers/extend/apps/operations/publishing) ve bu sayfa | +| `docker:*` | Yerel Twenty sunucu konteynerini yönetin | [Yerel Sunucu](/l/tr/developers/extend/apps/getting-started/local-server) | +| `remote:*` | Sunucu bağlantılarını yönetin | bu sayfa | + +Her komut, varsayılan yerine belirli bir uzak sunucuyu hedeflemek için `-r, --remote \` kabul eder. ## Fonksiyonları çalıştırma (`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 ``` ## Fonksiyon günlüklerini görüntüleme (`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 ``` Kimlik bilgileriniz `~/.twenty/config.json` içinde saklanır. diff --git a/packages/twenty-docs/l/tr/developers/extend/apps/operations/publishing.mdx b/packages/twenty-docs/l/tr/developers/extend/apps/operations/publishing.mdx index 64fa308ec4..fd09dec684 100644 --- a/packages/twenty-docs/l/tr/developers/extend/apps/operations/publishing.mdx +++ b/packages/twenty-docs/l/tr/developers/extend/apps/operations/publishing.mdx @@ -229,7 +229,7 @@ yarn twenty dev:catalog-sync # yarn twenty dev:catalog-sync --remote production ``` -Pazar yerinde gösterilen meta veriler, `defineApplication()` yapılandırmanızdan gelir — `displayName`, `description`, `author`, `category`, `logoUrl`, `screenshots`, `aboutDescription`, `websiteUrl` ve `termsUrl` gibi alanlar. +Pazaryerinde gösterilen meta veriler, `defineApplication()` yapılandırmanızdan gelir — yukarıdaki [Pazaryeri meta verileri](#marketplace-metadata) bölümüne bakın. Uygulamanız `defineApplication()` içinde bir `aboutDescription` tanımlamıyorsa, pazaryeri, hakkında sayfasının içeriği olarak paketinizin npm'deki `README.md` dosyasını otomatik olarak kullanır. Bu, hem npm hem de Twenty pazaryeri için tek bir README dosyası kullanabileceğiniz anlamına gelir. Pazaryerinde farklı bir açıklama istiyorsanız, `aboutDescription` değerini açıkça ayarlayın. diff --git a/packages/twenty-docs/l/tr/developers/extend/apps/operations/sync-and-recovery.mdx b/packages/twenty-docs/l/tr/developers/extend/apps/operations/sync-and-recovery.mdx index edf4da4c7a..a7e153e42a 100644 --- a/packages/twenty-docs/l/tr/developers/extend/apps/operations/sync-and-recovery.mdx +++ b/packages/twenty-docs/l/tr/developers/extend/apps/operations/sync-and-recovery.mdx @@ -15,33 +15,44 @@ Günlük yerel yinelemelerde neredeyse her zaman `yarn twenty dev` kullanmak ist | Şunu yapmak istiyorsunuz… | Komut | Notlar | | ---------------------------------------------------------- | ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | | Canlı senkronizasyonla yerelde yineleyin | `yarn twenty dev` | Dosyalarınızı izler ve her değişiklikte senkronize eder. | -| Bir kez senkronize et ve çık (CI, betikler, kancalar) | `yarn twenty dev --once` | Tek bir derleme + senkronizasyon yapar ve ardından çıkar. | -| Değişiklikleri **uygulamadan** önizleyin | `yarn twenty dev --once --dry-run` | Farkı hesaplar ve yazdırır; hiçbir şey yazmaz. | +| Bir kez senkronize et ve çık (CI, betikler, kancalar) | `yarn twenty apply` | Tek bir derleme + senkronizasyon yapar ve ardından çıkar. Yıkıcı değişiklik onayını atlamak için `--force` ekleyin. | +| Değişiklikleri **uygulamadan** önizleyin | `yarn twenty plan` | Farkı hesaplar ve yazdırır; hiçbir şey yazmaz. | | Uygulamayı çalışma alanından kaldırın | `yarn twenty app:uninstall` | İstemi atlamak için `--yes` ekleyin. | | Bir tarball'ı sunucuya gönderin | `yarn twenty app:publish --private` | `package.json` içinde **kesin olarak daha yüksek** bir sürüm gerektirir — bkz. [Publishing](/l/tr/developers/extend/apps/operations/publishing). | | Pazaryerine (npm) yayımlayın | `yarn twenty app:publish` | — | | Dağıtılmış bir sürümü yükleyin / yükseltin | `yarn twenty app:install` | Şu anda dağıtılmış olan sürümü yükler. | | Yerel sunucuyu silin ve temiz bir şekilde yeniden başlatın | `yarn twenty docker:reset` | Yerel verilerin **tamamını** siler — son çare. | + +`yarn twenty dev --once` ve `yarn twenty dev --once --dry-run`, `yarn twenty apply` ve `yarn twenty plan` için kullanımdan kaldırılmış takma adlar olsalar da hâlâ çalışırlar. + + ### Yerel senkronizasyon için sürüm artırmaya gerek yoktur Sıkı artan `version` kuralı (dağıtımda `VERSION_ALREADY_EXISTS`, yüklemede `APP_ALREADY_INSTALLED` / `CANNOT_DOWNGRADE_APPLICATION`) **`app:publish` / `app:install`** için — yani yayın yolu için — geçerlidir. `yarn twenty dev`, manifestinizi yerinde senkronize eder ve hiçbir zaman bir sürüm değişikliği gerektirmez, bu yüzden yineleme yapmak için `package.json` dosyasına dokunmanız gerekmez. Yerel bir değişikliği test etmek için kendinizi sürümü artırırken buluyorsanız, ihtiyacınız olan geliştirme döngüsü yerine yayın yolunu kullanıyorsunuz demektir. ## Senkronizasyon çıktısını okuma -Her senkronizasyon, uyguladığı (veya `--dry-run` ile uygulayacağı) üst veri değişikliklerini yazdırır: +Her senkronizasyon, uyguladığı (veya `plan` ile uygulayacağı) üst veri değişikliklerini, Terraform tarzında — varlık başına bir blok olacak şekilde, öznitelikleriyle birlikte ve ardından bir özet satırıyla yazdırır: ```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) ``` Bu, ilk tanı aracınızdır: tam olarak hangi nesnelerin, alanların ve düzenlerin değiştiğini size bildirir; böylece bir senkronizasyonun beklediğiniz gibi davranıp davranmadığını, arayüzü kontrol etmeden önce doğrulayabilirsiniz. +Yıkıcı değişiklikler (`to destroy`), neyi kaldırdıklarıyla birlikte listelenir (ör. `objectMetadata "auditNote" — drops the table and all its rows`) ve etkileşimli onay gerektirir ya da betiklerde `--force` kullanılmasını gerektirir. + Bir senkronizasyon tek bir varlıkta başarısız olduğunda, hata mesajı sorunlu varlığı ve onun `universalIdentifier` değerini adlandırır, örneğin: ```text @@ -50,39 +61,42 @@ Migration action 'create' for 'fieldMetadata' (universalIdentifier: 2020...4337) Bu tanımlayıcıyı, çakışanın hangisi olduğunu tahmin etmek yerine, manifestinizdeki (ve gerekirse çalışma alanındaki) varlığı bulmak için kullanın. -## Değişiklikleri önizleme (dry run) +## Değişiklikleri önizleme (plan) -`yarn twenty dev --once --dry-run`, manifestinizi derler, sunucudan geçiş planını ister ve onu yazdırır — **hiçbir şeyi uygulamadan**. Bu, ona taahhüt etmeden önce "Bu senkronizasyon neyi değiştirir?" sorusunu yanıtlamanın güvenli yoludur. +`yarn twenty plan`, manifestinizi derler, sunucudan geçiş planını ister ve onu — **hiçbir şeyi uygulamadan** — yazdırır. Bu, ona taahhüt etmeden önce "Bu senkronizasyon neyi değiştirir?" sorusunu yanıtlamanın güvenli yoludur. ```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 ``` -Bir dry run şunları yapar: +Bir plan: * **Hiçbir şey yazmaz** — üst veri geçişi, uygulama kaydı güncellemesi, varsayılan rol/sekme değişiklikleri ve API istemcisi oluşturma işlemleri yapılmaz. * Gerçek bir senkronizasyonun uygulayacağı **aynı farkı** döndürür; böylece oluşturulan/güncellenen/silinen varlıkları en baştan inceleyebilirsiniz. * Riskli bir değişiklikten önce, bir yapay zekâ tarafından oluşturulan değişikliği gözden geçirirken veya beklenmedik bir değişiklik gerçekleşmek üzereyse betiğin başarısız olması gereken durumlarda kullanışlıdır. -Bir dry run yalnızca **üst veri** değişikliklerini önizler ve uygulamanın en az bir kez senkronize edilmiş olmasını gerektirir (böylece çalışma alanı ondan haberdar olur). Hiç senkronize edilmemiş bir uygulamaya karşı çalıştırırsanız, sunucu uygulamanın yüklü olmadığını bildirir — önce bir kez `yarn twenty dev` çalıştırın. +Bir plan yalnızca **üst veri** değişikliklerini önizler ve uygulamanın en az bir kez senkronize edilmiş olmasını gerektirir (böylece çalışma alanı ondan haberdar olur). Hiç senkronize edilmemiş bir uygulamaya karşı çalıştırırsanız, sunucu uygulamanın yüklü olmadığını bildirir — önce bir kez `yarn twenty dev` çalıştırın. ## Kurtarma merdiveni Yerel üst veriler hatalı görünüyorsa, bu adımları sırayla uygulayın ve engeliniz kalkar kalkmaz durun. Her adım bir öncekinden daha yıkıcıdır. -1. **Yeniden senkronize edin.** `yarn twenty dev --once` komutunu tekrar çalıştırın. Senkronizasyonlar idempotenttir — temiz bir manifesti yeniden çalıştırmak güvenlidir ve çoğu zaman geçici bir aksaklığı giderir. -2. **Planı önizleyin.** Bir sonraki senkronizasyonun tam olarak neyi değiştirmeyi amaçladığını, uygulamadan görmek için `yarn twenty dev --once --dry-run` çalıştırın. +1. **Yeniden senkronize edin.** `yarn twenty apply` komutunu tekrar çalıştırın. Senkronizasyonlar idempotenttir — temiz bir manifesti yeniden çalıştırmak güvenlidir ve çoğu zaman geçici bir aksaklığı giderir. +2. **Planı önizleyin.** Bir sonraki senkronizasyonun tam olarak neyi değiştirmeyi amaçladığını, uygulamadan görmek için `yarn twenty plan` komutunu çalıştırın. 3. **Adlandırılmış hatayı okuyun.** Bir senkronizasyon başarısız olursa, iletideki üst veri türünü ve `universalIdentifier` değerini not alın (yukarıya bakın) ve manifestinizdeki o varlığı bulun. Bir çakışma genellikle yinelenen veya tekrar kullanılan bir tanımlayıcıya işaret eder. 4. **Kaldırın ve yeniden yükleyin.** `yarn twenty app:uninstall` komutunu çalıştırın, ardından yeniden senkronize edin (`yarn twenty dev`). Bu, uygulamanın üst verilerini temiz bir başlangıçtan yeniden oluşturur ve çalışma alanınızın geri kalanını olduğu gibi bırakır. 5. **Tam sıfırlama (son çare).** `yarn twenty docker:reset` komutunu çalıştırın, ardından yeniden tohumlayın ve yeniden senkronize edin. diff --git a/packages/twenty-docs/l/tr/developers/extend/apps/operations/testing.mdx b/packages/twenty-docs/l/tr/developers/extend/apps/operations/testing.mdx index c15a2634cd..7d87cbbaa7 100644 --- a/packages/twenty-docs/l/tr/developers/extend/apps/operations/testing.mdx +++ b/packages/twenty-docs/l/tr/developers/extend/apps/operations/testing.mdx @@ -78,6 +78,13 @@ Uygulamanızın kök dizininde bir `vitest.config.ts` oluşturun: 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, }, }, }); ``` -Testler çalışmadan önce sunucuya erişilebildiğini doğrulayan bir kurulum dosyası oluşturun: +Sunucunun erişilebilir olduğunu doğrulayan, SDK için bir test yapılandırması (`~/.twenty/config.test.json`) yazan ve testler çalışmadan önce uygulamayı eşitleyen genel bir kurulum dosyası oluşturun: -```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 }); +} ``` ## Programatik SDK API'leri `twenty-sdk/cli` alt yolu, test kodundan doğrudan çağırabileceğiniz fonksiyonları dışa aktarır: -| Fonksiyon | Açıklama | -| -------------- | ----------------------------------------------------------------- | -| `appBuild` | Uygulamayı derleyin ve isteğe bağlı olarak bir tarball paketleyin | -| `appDeploy` | Bir tarball'ı sunucuya yükleyin | -| `appInstall` | Uygulamayı etkin çalışma alanına yükleyin | -| `appUninstall` | Uygulamayı etkin çalışma alanından kaldırın | +| Fonksiyon | Açıklama | +| -------------- | --------------------------------------------------------------------------- | +| `appBuild` | Uygulamayı derleyin ve isteğe bağlı olarak bir tarball paketleyin | +| `appDeploy` | Bir tarball'ı sunucuya yükleyin | +| `appDevOnce` | Uygulamayı bir kez oluşturun ve eşitleyin (`yarn twenty apply` ile aynıdır) | +| `appInstall` | Uygulamayı etkin çalışma alanına yükleyin | +| `appUninstall` | Uygulamayı etkin çalışma alanından kaldırın | Her fonksiyon, `success: boolean` ile birlikte `data` veya `error` içeren bir sonuç nesnesi döndürür. @@ -238,64 +253,10 @@ Ayrıca testleri çalıştırmadan uygulamanızda tip denetimi çalıştırabili yarn twenty dev:typecheck ``` -Bu, `tsc --noEmit` komutunu çalıştırır ve tüm tip hatalarını raporlar. +Bu, uygulamanızın `tsconfig.json` dosyasına karşı `tsc --noEmit` komutunu çalıştırır ve tüm tip hatalarını raporlar. İskelet olarak oluşturulan uygulamalar ayrıca test dosyalarını da kapsayan (`tsconfig.spec.json`) bir `yarn typecheck` betiği ile birlikte gelir. ## GitHub Actions ile CI -İskelet oluşturucu, `.github/workflows/ci.yml` konumunda kullanıma hazır bir GitHub Actions iş akışı üretir. Entegrasyon testlerinizi `main` dalına yapılan her itmede ve çekme isteklerinde otomatik olarak çalıştırır. +İskelet oluşturucu, `.github/workflows/ci.yml` konumunda kullanıma hazır bir iş akışı üretir. `main` dalına yapılan her itmede ve her çekme isteğinde, çalıştırıcı içinde geçici bir Twenty sunucusu başlatır (`twentyhq/twenty/.github/actions/spawn-twenty-app-dev-test` eylemi aracılığıyla) ve ardından `yarn lint`, `yarn typecheck`, `yarn test:unit` ve `yarn test` komutlarını, `TWENTY_API_URL` / `TWENTY_API_KEY` bu sunucuyu işaret edecek şekilde çalıştırır. Herhangi bir gizli bilgi gerekmez ve iş akışının en üstündeki `TWENTY_VERSION` ortam değişkeni aracılığıyla sunucu sürümünü sabitleyebilirsiniz. -İş akışı: - -1. Kodunuzu çalışma alanına alır -2. `twentyhq/twenty/.github/actions/spawn-twenty-docker-image` eylemini kullanarak geçici bir Twenty sunucusu başlatır -3. `yarn install --immutable` ile bağımlılıkları kurar -4. Eylem çıktılarından enjekte edilen `TWENTY_API_URL` ve `TWENTY_API_KEY` ile `yarn test` çalıştırır - -```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 }} -``` - -Herhangi bir gizli değişken yapılandırmanız gerekmez — `spawn-twenty-docker-image` eylemi, koşucu içinde doğrudan geçici bir Twenty sunucusu başlatır ve bağlantı ayrıntılarını çıktı olarak verir. `GITHUB_TOKEN` gizli değişkeni GitHub tarafından otomatik olarak sağlanır. - -`latest` yerine belirli bir Twenty sürümünü sabitlemek için iş akışının başındaki `TWENTY_VERSION` ortam değişkenini değiştirin. +Hem iskelet iş akışlarının (`ci.yml` ve `cd.yml` dağıtım hattı) tam adım adım anlatımı için [Yayınlama → Otomatik CI/CD](/l/tr/developers/extend/apps/operations/publishing#automated-cicd-scaffolded-workflows) bölümüne bakın. diff --git a/packages/twenty-docs/l/tr/developers/extend/apps/tutorials/document-generator/building-the-ui.mdx b/packages/twenty-docs/l/tr/developers/extend/apps/tutorials/document-generator/building-the-ui.mdx index a0e38a42f6..5b79143243 100644 --- a/packages/twenty-docs/l/tr/developers/extend/apps/tutorials/document-generator/building-the-ui.mdx +++ b/packages/twenty-docs/l/tr/developers/extend/apps/tutorials/document-generator/building-the-ui.mdx @@ -85,9 +85,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 }), @@ -168,7 +170,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/tr/developers/extend/apps/tutorials/document-generator/http-routes.mdx b/packages/twenty-docs/l/tr/developers/extend/apps/tutorials/document-generator/http-routes.mdx index ced8a79a30..48d487fb0b 100644 --- a/packages/twenty-docs/l/tr/developers/extend/apps/tutorials/document-generator/http-routes.mdx +++ b/packages/twenty-docs/l/tr/developers/extend/apps/tutorials/document-generator/http-routes.mdx @@ -9,7 +9,12 @@ Aynı işleyici HTTP isteklerine de yanıt verebilir. İki rota ekleyeceğiz: * belge oluşturmak için arayüzün çağırdığı bir **POST** uç noktası ve * belgeyi yazdırılabilir bir web sayfası olarak oluşturan herkese açık bir **GET** uç noktası. -Her ikisi de `httpRouteTriggerSettings` kullanır. Uygulama rotaları Twenty sunucunuzda `/s` altında sunulur (ör. `http://localhost:2020/s/documents/generate`). +Her ikisi de `httpRouteTriggerSettings` kullanır. Yerel geliştirme sunucusunda, uygulama yönlendirmeleri `/s` öneki altında sunulur (örneğin, `http://localhost:2020/s/documents/generate`). + + +Twenty Cloud üzerinde yönlendirmeler, çalışma alanına ayrılmış fonksiyon alan adında sunulur — Twenty'nin `/s` öneki olmadan `TWENTY_FUNCTIONS_URL` olarak eklediği URL'de. `/s` öneki orada kullanım dışıdır ve yalnızca kendi barındırılan ve yerel örneklerde kalmıştır. +[Mantık fonksiyonunu çağırma](/l/tr/developers/extend/apps/layout/front-components#calling-a-logic-function) bölümüne bakın. + ## POST rotası — istek üzerine oluşturma diff --git a/packages/twenty-docs/l/tr/developers/extend/apps/tutorials/document-generator/publishing.mdx b/packages/twenty-docs/l/tr/developers/extend/apps/tutorials/document-generator/publishing.mdx index 82f84ccbca..cb93433980 100644 --- a/packages/twenty-docs/l/tr/developers/extend/apps/tutorials/document-generator/publishing.mdx +++ b/packages/twenty-docs/l/tr/developers/extend/apps/tutorials/document-generator/publishing.mdx @@ -69,10 +69,11 @@ CI ile aynı denetimleri çalıştırın: 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 ``` -Deneme çalıştırması, sunucuda neyin değişeceğini uygulamadan tam olarak gösterir — iyi bir son sağlama kontrolüdür. Bkz. +Plan, sunucuda neyin değişeceğini uygulamadan tam olarak gösterir — +iyi bir son sağlama kontrolüdür. Bkz. [Testing](/l/tr/developers/extend/apps/operations/testing) ve [Syncing & recovery](/l/tr/developers/extend/apps/operations/sync-and-recovery). diff --git a/packages/twenty-docs/l/zh/developers/extend/apps/config/install-hooks.mdx b/packages/twenty-docs/l/zh/developers/extend/apps/config/install-hooks.mdx index ee11992bd1..5380992765 100644 --- a/packages/twenty-docs/l/zh/developers/extend/apps/config/install-hooks.mdx +++ b/packages/twenty-docs/l/zh/developers/extend/apps/config/install-hooks.mdx @@ -4,7 +4,7 @@ description: 在安装之前或之后运行逻辑——预置数据、备份记 icon: wrench --- -安装钩子是在安装或升级生命周期期间运行的特殊逻辑函数。 它们与常规的[逻辑函数](/l/zh/developers/extend/apps/logic/logic-functions)共享相同的处理程序运行时,并接收 `InstallPayload`,但它们使用自己的定义函数声明——`definePostInstallLogicFunction()` 和 `definePreInstallLogicFunction()`——并且存在于普通触发模型(HTTP、cron、数据库事件)之外。 +安装钩子是在安装或升级生命周期期间运行的特殊逻辑函数。 它们与常规的[逻辑函数](/l/zh/developers/extend/apps/logic/logic-functions)共享相同的处理程序运行时,并接收一个 `InstallPayload`(`{ previousVersion?: string; newVersion: string }`——在全新安装时 `previousVersion` 为 `undefined`),但它们使用自己的 define 函数声明,并且存在于普通触发模型(HTTP、cron、数据库事件)之外。 每个应用**最多只能定义一个安装前函数**和**最多一个安装后函数**。 如果检测到任一类型多于一个,清单构建将报错。 @@ -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。 仅当迁移本身具有破坏性,且你需要在其丢失之前拦截先前状态时,才使用安装前。 -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` 设为 true 以便在升级时也运行。 使用 `previousVersion` / `newVersion` 按升级路径分支处理。 +* **幂等性很重要**:异步 post-install 可能会被重试,而且当开启 `shouldRunOnVersionUpgrade` 时,任一钩子都会在升级时重新运行。 +* 会注入常规的逻辑函数环境(`APPLICATION_ID`、`APP_ACCESS_TOKEN`、`API_URL`),因此你可以使用应用的令牌调用 Twenty API。 +* 该钩子会在构建时自动附加到应用清单上(`preInstallLogicFunction` / `postInstallLogicFunction`)——在 [`defineApplication()`](/l/zh/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`,并在工作线程中异步运行。 作业一入列,安装响应即返回,因此缓慢或失败的处理程序不会阻塞调用方。 工作线程最多会重试三次。 **将其用于长时间运行的作业**——预填充大型数据集、调用缓慢的第三方 API、预配外部资源,以及任何可能超出合理 HTTP 响应窗口的任务。 - * `shouldRunSynchronously: true` — 该钩子会在安装流程中**内联执行**(与安装前使用相同的执行器)。 安装请求将阻塞直至处理程序完成;若抛出异常,安装调用方将收到 `POST_INSTALL_ERROR`。 不进行自动重试。 **用于需要在响应前完成的快速工作**——例如向用户返回验证错误,或进行安装调用返回后客户端将立即依赖的快速设置。 请注意,运行安装后时,元数据迁移已应用完成,因此同步模式下的失败**不会**回滚架构更改——它只会暴露错误。 -* 确保你的处理程序是幂等的。 在异步模式下,队列最多可重试三次;在任一模式下,当 `shouldRunOnVersionUpgrade: true` 时,该钩子在升级时可能再次运行。 -* 在处理程序内可使用环境变量 `APPLICATION_ID`、`APP_ACCESS_TOKEN` 和 `API_URL`(与其他逻辑函数相同),因此你可以使用作用域限定到你应用的应用访问令牌调用 Twenty API。 -* 每个应用仅允许一个安装后函数。 如果检测到多个,清单构建将报错。 -* 构建期间,函数的 `universalIdentifier`、`shouldRunOnVersionUpgrade` 和 `shouldRunSynchronously` 会自动附加到应用清单的 `postInstallLogicFunction` 字段下——你无需在 [`defineApplication()`](/l/zh/developers/extend/apps/config/application) 中引用它们。 -* 默认超时时间设置为 300 秒(5 分钟),以便支持更长的设置任务,如数据填充。 -* **开发模式下不执行**:当应用在本地注册(通过 `yarn twenty dev`)时,服务器会完全跳过安装流程,并通过 CLI 监视器直接同步文件——因此无论 `shouldRunSynchronously` 如何,安装后在开发模式下都不会运行。 使用 `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`)之前。 在执行之前,服务器会运行一次纯增量的“精简同步”,将**新**版本的安装前函数注册到工作区元数据中——不会触及其他任何内容——然后再执行它。 由于此次同步仅为增量操作,当你的处理程序运行时,上一版本的对象、字段和数据仍完好无损:你可以安全地读取并备份迁移前的状态。 -* **执行模型**:安装前以**同步**方式执行,并且**会阻塞安装**。 如果处理程序抛出异常,安装会在任何架构更改应用之前被中止——工作区将保持在上一版本且处于一致状态。 这是有意为之:安装前是你拒绝高风险升级的最后机会。 -* 与安装后相同,每个应用仅允许一个安装前函数。 在构建期间,它会自动附加到应用清单的 `preInstallLogicFunction` 下。 -* **开发模式下不执行**:与安装后相同——对于本地注册的应用将完全跳过安装流程,因此在 `yarn twenty dev` 下不会运行安装前。 使用 `yarn twenty dev:function:exec --preInstall` 手动触发它。 + + - - - -两个钩子都属于同一安装流程,并接收相同的 `InstallPayload`。 区别在于它们相对于工作区元数据迁移**何时**运行,这会影响它们可以安全访问的数据范围。 - -安装前始终为**同步**(会阻塞安装并可中止它)。 安装后**默认异步**——在工作线程中入列并自动重试——但可通过 `shouldRunSynchronously: true` 选择同步执行。 关于各模式的使用场景,请参见上方的 `definePostInstallLogicFunction` 折叠面板。 - -**对于需要新架构已存在的任何事项,请使用 `post-install`。** 这是最常见的情况: - -* 针对新添加的对象和字段预填充默认数据(创建初始记录、默认视图、演示内容)。 -* 在应用已有凭据的前提下,向第三方服务注册 Webhook。 -* 调用你自己的 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`,你需要先删除或修正具有空值的行。 -* **验证兼容性;若当前数据无法干净迁移则拒绝升级**——从处理程序中抛出异常,安装将中止且不会应用任何更改。 这比在迁移中途才发现不兼容要更安全。 -* **在会导致关联丢失的架构更改之前**对数据进行重命名或重新设置键。 +* `false` *(默认)*——放入消息队列(`retryLimit: 3`)并由工作线程运行。 安装响应会在任务被放入队列后立即返回。 **用于长时间运行的任务**——例如预填充大型数据集、调用缓慢的第三方 API。 +* `true`——在安装流程中内联执行。 安装请求会阻塞直至处理程序完成;抛出的错误会以 `POST_INSTALL_ERROR` 的形式暴露给调用方(不重试)。 **用于必须在返回响应前完成的快速任务。** 此时迁移已应用,因此失败不会回滚模式更改——只会将错误暴露出来。 -示例——在破坏性迁移之前归档记录: + + + +在元数据迁移之前、针对**先前**模式运行——适合在迁移会删除数据前对其进行备份,或拒绝存在风险的升级。 在执行之前,服务器会运行一次纯增量的“精简同步”,仅注册新版本的 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`,由工作线程重试) | -| 运行安装调用返回后调用方将立即依赖的快速设置 | `post-install`,配合 `shouldRunSynchronously: true` | -| 读取或备份即将被迁移丢失的数据 | `pre-install` | -| 拒绝会损坏现有数据的升级 | `pre-install`(从处理程序中抛出异常) | -| 在每次升级时执行对账 | `post-install` 配合 `shouldRunOnVersionUpgrade: true` | -| 仅在首次安装时执行一次性设置 | `post-install` 配合 `shouldRunOnVersionUpgrade: false`(默认) | - - -如有不确定,默认选择**安装后(post-install)**。 仅当迁移本身具有破坏性,且你需要在其丢失之前拦截先前状态时,才使用安装前。 - - diff --git a/packages/twenty-docs/l/zh/developers/extend/apps/data/objects.mdx b/packages/twenty-docs/l/zh/developers/extend/apps/data/objects.mdx index e1ea7108f4..5b14f05e64 100644 --- a/packages/twenty-docs/l/zh/developers/extend/apps/data/objects.mdx +++ b/packages/twenty-docs/l/zh/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/zh/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/zh/developers/extend/apps/getting-started/project-structure.mdx b/packages/twenty-docs/l/zh/developers/extend/apps/getting-started/project-structure.mdx index 2a3fda647a..935a2a3570 100644 --- a/packages/twenty-docs/l/zh/developers/extend/apps/getting-started/project-structure.mdx +++ b/packages/twenty-docs/l/zh/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 层获取它,前端组件则从服务器提供的模块中解析它。 你安装的副本仅用于类型检查和部署时的构建,因此不需要被打包进已部署的 bundle 中。 -把任意一个软件包放在 `dependencies` 下,都会把它拉入已安装应用的运行时 bundle 中,在那里只是累赘。 当任一软件包仍然列在 `dependencies` 下时,`twenty build` 会发出警告。 +把任意一个软件包放在 `dependencies` 下,都会把它拉入已安装应用的运行时 bundle 中,在那里只是累赘。 当任一软件包仍然列在 `dependencies` 下时,`twenty dev:build` 会发出警告。 像往常一样,把你的应用自身的运行时依赖项(逻辑函数在运行时实际导入的库)添加到 `dependencies` 下。 diff --git a/packages/twenty-docs/l/zh/developers/extend/apps/getting-started/quick-start.mdx b/packages/twenty-docs/l/zh/developers/extend/apps/getting-started/quick-start.mdx index 2f097adfce..7f372b3ec8 100644 --- a/packages/twenty-docs/l/zh/developers/extend/apps/getting-started/quick-start.mdx +++ b/packages/twenty-docs/l/zh/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 @@ description: 几分钟内创建你的第一个 Twenty 应用。 npx create-twenty-app@latest my-twenty-app ``` -系统会提示你输入名称和描述——按下 **Enter** 采用默认值。 这将在 `my-twenty-app/` 中生成一个 TypeScript 项目,包含一个入门版的 `application-config.ts`、一个默认角色、一个 CI 工作流,以及一个集成测试。 +脚手架是非交互式的:目录名称会成为应用名称。 传递 `--display-name` 和 `--description` 来自定义生成的元数据(你也可以稍后在 `src/constants/universal-identifiers.ts` 中进行编辑)。 这将在 `my-twenty-app/` 中生成一个 TypeScript 项目,包含一个入门版的 `application-config.ts`、一个默认角色、CI/CD 工作流,以及一个集成测试。 **完成此阶段后:** 你的机器上已有该应用的源代码。 它还未运行——那是第 2 阶段的内容。 @@ -38,28 +38,14 @@ npx create-twenty-app@latest my-twenty-app 你的应用需要一个 Twenty 服务器来进行同步。 该服务器是一个完整的 Twenty 实例——包含 UI、GraphQL API、PostgreSQL——在本地的 Docker 中运行。 你的本地代码会将其定义上传到该服务器,从而使其显示在 UI 中。 -脚手架工具会为你提供启动它的选项: +脚手架会为你启动一个:在 Docker 正在运行的情况下,它会拉取 `twentycrm/twenty-app-dev` 镜像,在端口 `2020` 上启动,并将 CLI 认证到预置的演示工作区(`tim@apple.dev`)——无需登录。 -> **是否要设置本地 Twenty 实例?** - -* **是(推荐)** — 将拉取 `twentycrm/twenty-app-dev` Docker 镜像,并在端口 `2020` 上启动它。 请先确保 Docker 正在运行。 -* **否** — 如果你已经有一个想要连接的 Twenty 服务器,请选择此项。 你可以稍后通过 `yarn twenty remote:add` 将其连接起来。 - -
- 是否启动本地实例? -
- -服务器启动后,浏览器会打开登录页面。 使用预置的演示账户: - -* **邮箱:** `tim@apple.dev` -* **密码:** `tim@apple.dev` +若要改为连接到现有的 Twenty 服务器,请传入 `--url \`。 远程服务器通过 OAuth 进行身份验证:会打开一个浏览器窗口,你可以登录并点击 **Authorize**,从而授予 CLI 访问你工作区的权限。 (你也可以在本地选择使用 OAuth,方式是添加 `--authentication-method oauth` —— 使用 `tim@apple.dev` / `tim@apple.dev` 登录。)
Twenty 登录界面
-在下一屏点击 **Authorize** —— 这将授予 CLI 访问你工作区的权限。 -
Twenty CLI 授权界面
@@ -117,28 +103,32 @@ yarn twenty dev ### 用于 CI 和脚本的一次性同步 -传入 `--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/zh/developers/extend/apps/operations/sync-and-recovery#previewing-changes-dry-run)。 +所有模式都需要经过身份验证的远程服务器。 有关 `plan` 的更多信息,请参见 [同步与恢复](/l/zh/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/zh/developers/extend/apps/getting-started/scaffolding.mdx b/packages/twenty-docs/l/zh/developers/extend/apps/getting-started/scaffolding.mdx index 7f3ab05054..88056ce626 100644 --- a/packages/twenty-docs/l/zh/developers/extend/apps/getting-started/scaffolding.mdx +++ b/packages/twenty-docs/l/zh/developers/extend/apps/getting-started/scaffolding.mdx @@ -22,18 +22,22 @@ yarn twenty dev:add frontComponent ## 可用的实体类型 -| 实体类型 | 命令 | 生成的文件 | -| ----- | ---------------------------------------- | ------------------------------------------------------- | -| 对象 | `yarn twenty dev:add object` | `src/objects/\.ts` | -| 字段 | `yarn twenty dev:add field` | `src/fields/\.ts` | -| 逻辑函数 | `yarn twenty dev:add logicFunction` | `src/logic-functions/\.ts` | -| 前端组件 | `yarn twenty dev:add frontComponent` | `src/front-components/\.tsx` | -| 角色 | `yarn twenty dev:add role` | `src/roles/\.ts` | -| 技能 | `yarn twenty dev:add skill` | `src/skills/\.ts` | -| 代理 | `yarn twenty dev:add agent` | `src/agents/\.ts` | -| 视图 | `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 object` | `src/objects/\.ts` | +| 字段 | `yarn twenty dev:add field` | `src/fields/\.ts` | +| 逻辑函数 | `yarn twenty dev:add logicFunction` | `src/logic-functions/\.ts` | +| 前端组件 | `yarn twenty dev:add frontComponent` | `src/front-components/\.tsx` | +| 角色 | `yarn twenty dev:add role` | `src/roles/\.ts` | +| 技能 | `yarn twenty dev:add skill` | `src/skills/\.ts` | +| 代理 | `yarn twenty dev:add agent` | `src/agents/\.ts` | +| 视图 | `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/zh/developers/extend/apps/getting-started/troubleshooting.mdx b/packages/twenty-docs/l/zh/developers/extend/apps/getting-started/troubleshooting.mdx index 54785db587..74166d7830 100644 --- a/packages/twenty-docs/l/zh/developers/extend/apps/getting-started/troubleshooting.mdx +++ b/packages/twenty-docs/l/zh/developers/extend/apps/getting-started/troubleshooting.mdx @@ -5,10 +5,10 @@ icon: wrench --- * **Docker 错误** — 在运行 `yarn twenty docker:start` 之前,请确保 Docker Desktop(或守护进程)已在运行。 错误消息会显示适用于你的操作系统的正确启动命令。 -* **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`。 请参阅[项目结构 → 依赖项](/l/zh/developers/extend/apps/getting-started/project-structure#dependencies)。 -* **`twenty build` 会对 `dependencies` 下的 `twenty-client-sdk` 发出警告** — 它由 Twenty 在运行时提供,因此应与 `twenty-sdk` 一起移动到 `devDependencies` 中。 请参阅[项目结构 → 依赖项](/l/zh/developers/extend/apps/getting-started/project-structure#dependencies)。 +* **`twenty dev:build` 会对 `dependencies` 下的 `twenty-client-sdk` 发出警告** — 它由 Twenty 在运行时提供,因此应与 `twenty-sdk` 一起移动到 `devDependencies` 中。 请参阅[项目结构 → 依赖项](/l/zh/developers/extend/apps/getting-started/project-structure#dependencies)。 卡住了吗? 在 [Twenty 的 Discord](https://discord.com/channels/1130383047699738754/1130386664812982322) 上提问。 diff --git a/packages/twenty-docs/l/zh/developers/extend/apps/layout/command-menu-items.mdx b/packages/twenty-docs/l/zh/developers/extend/apps/layout/command-menu-items.mdx index 7b9817d292..dddabfad5a 100644 --- a/packages/twenty-docs/l/zh/developers/extend/apps/layout/command-menu-items.mdx +++ b/packages/twenty-docs/l/zh/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/zh/developers/extend/apps/layout/front-components#headless-vs-non-headless)配对的命令菜单项,是交付一键操作(运行代码、导航,或确认并执行)的惯用方式。 Front Components 页面介绍了处理“执行操作并卸载”模式的 [SDK Command 组件](/l/zh/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, -}); -``` +一个典型流程:一个 headless 组件会渲染 ``(参见[完整示例](/l/zh/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/zh/developers/extend/apps/layout/front-components.mdx b/packages/twenty-docs/l/zh/developers/extend/apps/layout/front-components.mdx index b667f18c42..525a4f370d 100644 --- a/packages/twenty-docs/l/zh/developers/extend/apps/layout/front-components.mdx +++ b/packages/twenty-docs/l/zh/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 辅助组件。 每个组件都会在挂载时执行一个操作,通过显示 snackbar 通知来处理错误,并在完成后自动卸载该前端组件。 -从 `twenty-sdk/command` 导入它们: +从 `twenty-sdk/front-component` 导入它们: * **`Command`** — 通过 `execute` 属性运行异步回调。 * **`CommandLink`** — 导航到某个应用路径。 属性:`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` 注入到 worker 中,同时注入用于对调用进行身份验证的 `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/zh/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/zh/developers/extend/apps/layout/navigation-menu-items.mdx b/packages/twenty-docs/l/zh/developers/extend/apps/layout/navigation-menu-items.mdx index 6c66057fb9..82bff27864 100644 --- a/packages/twenty-docs/l/zh/developers/extend/apps/layout/navigation-menu-items.mdx +++ b/packages/twenty-docs/l/zh/developers/extend/apps/layout/navigation-menu-items.mdx @@ -35,6 +35,8 @@ export default defineNavigationMenuItem({ * `position` 控制在侧边栏中的排序。 +* 该枚举还包含 `NavigationMenuItemType.RECORD`,在内部用于用户创建的记录收藏——在应用 manifest 中不可用(没有用于引用记录的字段)。 + * `icon` 和 `color` 是可选的,用于自定义条目的外观。 * `folderUniversalIdentifier` 也可用于任意条目,将其嵌套到一个 `FOLDER` 类型的父级中。 diff --git a/packages/twenty-docs/l/zh/developers/extend/apps/layout/views.mdx b/packages/twenty-docs/l/zh/developers/extend/apps/layout/views.mdx index f83c1e9e45..50e597c421 100644 --- a/packages/twenty-docs/l/zh/developers/extend/apps/layout/views.mdx +++ b/packages/twenty-docs/l/zh/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` | 日历视图:布局以及用于定位记录的日期字段。 | + +上述所有枚举都从 `twenty-sdk/define` 导出。 + ## 过滤器 视图可以附带预先应用的过滤器。 每个过滤器有三个坐标:被筛选的**字段**、**运算符**(如何比较)以及**值**(与之比较的内容)。 这三者必须全部对齐——在同步时,使用不适用于字段类型的运算符将会被拒绝。 ```ts -import { ViewFilterOperand } from 'twenty-shared/types'; +import { ViewFilterOperand } from 'twenty-sdk/define'; filters: [ { diff --git a/packages/twenty-docs/l/zh/developers/extend/apps/logic/logic-functions.mdx b/packages/twenty-docs/l/zh/developers/extend/apps/logic/logic-functions.mdx index 4461f7b4c6..309a967904 100644 --- a/packages/twenty-docs/l/zh/developers/extend/apps/logic/logic-functions.mdx +++ b/packages/twenty-docs/l/zh/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**:在你工作区的 HTTP 路径和方法上显示你的函数。**函数基础的 URL** ——`TWENTY_FUNCTIONS_URL` (在20个云上) a 专用工作区: +> 例如 `path: '/post-card/create'` 可在 `https://your-workspace.withtwenty.com/post-card/create` 调用 + + +旧的 `/s/` 前缀路由 (`https://your-twentserver.com/s/post-card/create`) **在 20 Cloud** 上被废弃,并将在 **2026-07-24**被停用。 它仍可用于不配置一个孤立函数域的自托管和本地实例——在设置时使用 `TWENTY_FUNCTIONS_URL` 。 然后回到\/s/\。 + 要从(无头)前端组件调用由路由触发的逻辑函数,请参见[调用逻辑函数](/l/zh/developers/extend/apps/layout/front-components#calling-a-logic-function)。 diff --git a/packages/twenty-docs/l/zh/developers/extend/apps/logic/overview.mdx b/packages/twenty-docs/l/zh/developers/extend/apps/logic/overview.mdx index 6148bc6292..7426812d3b 100644 --- a/packages/twenty-docs/l/zh/developers/extend/apps/logic/overview.mdx +++ b/packages/twenty-docs/l/zh/developers/extend/apps/logic/overview.mdx @@ -40,13 +40,13 @@ Twenty 应用的 **逻辑层** 是实际*运行*的代码——用于响应 HTTP 逻辑函数选择一个或多个触发器——下面的每一项都是 `defineLogicFunction()` 上的一个独立字段: -| 触发器 | 触发时机 | 设置 | -| ----------- | --------------------------------------- | ------------------------------- | -| **HTTP 路由** | 请求命中你的 `/s/\` 端点 | `httpRouteTriggerSettings` | -| **Cron** | 匹配到一个 CRON 表达式时 | `cronTriggerSettings` | -| **数据库事件** | 当工作区记录被创建、更新或删除时 | `databaseEventTriggerSettings` | -| **AI 工具** | 某个 Twenty AI 功能决定调用你的函数时 | `toolTriggerSettings` | -| **工作流动作** | 当工作流步骤调用你的函数时 | `workflowActionTriggerSettings` | +| 触发器 | 触发时机 | 设置 | +| ----------- | ------------------------ | ------------------------------- | +| **HTTP 路由** | 一个请求点击你的公開的 URL | `httpRouteTriggerSettings` | +| **Cron** | 匹配到一个 CRON 表达式时 | `cronTriggerSettings` | +| **数据库事件** | 当工作区记录被创建、更新或删除时 | `databaseEventTriggerSettings` | +| **AI 工具** | 某个 Twenty AI 功能决定调用你的函数时 | `toolTriggerSettings` | +| **工作流动作** | 当工作流步骤调用你的函数时 | `workflowActionTriggerSettings` | 函数在隔离的 Node.js 进程沙箱中运行,并通过限定在 [`defineApplication()`](/l/zh/developers/extend/apps/config/application) 上声明角色范围内的类型化 API 客户端访问工作区。 diff --git a/packages/twenty-docs/l/zh/developers/extend/apps/operations/cli.mdx b/packages/twenty-docs/l/zh/developers/extend/apps/operations/cli.mdx index 4a9cff423c..3c302c5099 100644 --- a/packages/twenty-docs/l/zh/developers/extend/apps/operations/cli.mdx +++ b/packages/twenty-docs/l/zh/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/zh/developers/extend/apps/getting-started/quick-start) | +| `计划` | 在不应用变更的情况下预览元数据更改 | [同步与恢复](/l/zh/developers/extend/apps/operations/sync-and-recovery#previewing-changes-plan) | +| `应用` | 在显示计划后应用元数据更改 | [同步与恢复](/l/zh/developers/extend/apps/operations/sync-and-recovery) | +| `dev:build` | 编译应用并生成 API 客户端(使用 `--tarball` 打包为 `.tgz`) | [发布](/l/zh/developers/extend/apps/operations/publishing) | +| `dev:typecheck` | 运行 TypeScript 类型检查 | [测试](/l/zh/developers/extend/apps/operations/testing) | +| `dev:add` | 搭建一个新的实体脚手架 | [脚手架](/l/zh/developers/extend/apps/getting-started/scaffolding) | +| `dev:generate-client` | 重新生成类型化 API 客户端 | 本页面 | +| `dev:function:exec` / `dev:function:logs` | 执行函数并流式输出其日志 | 本页面 | +| `dev:translations-extract` | 将可翻译字符串提取到 `locales/` 目录中的目录文件中 | [翻译](/l/zh/developers/extend/apps/translations/overview) | +| `dev:catalog-sync` | 触发一次市场目录同步 | [发布](/l/zh/developers/extend/apps/operations/publishing#how-marketplace-discovery-works) | +| `app:publish` / `app:install` / `app:uninstall` | 发布生命周期 | [发布](/l/zh/developers/extend/apps/operations/publishing) 和本页面 | +| `docker:*` | 管理本地 Twenty 服务器容器 | [本地服务器](/l/zh/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/zh/developers/extend/apps/operations/publishing.mdx b/packages/twenty-docs/l/zh/developers/extend/apps/operations/publishing.mdx index 8fc413ba7f..5bc8a5480d 100644 --- a/packages/twenty-docs/l/zh/developers/extend/apps/operations/publishing.mdx +++ b/packages/twenty-docs/l/zh/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 元数据](#marketplace-metadata)。 如果您的应用未在 `defineApplication()` 中定义 `aboutDescription`,市场将自动使用 npm 上您的软件包的 `README.md` 作为关于页面内容。 这意味着您可以为 npm 和 Twenty 市场维护同一个 README。 如果您希望在市场中使用不同的描述,请显式设置 `aboutDescription`。 diff --git a/packages/twenty-docs/l/zh/developers/extend/apps/operations/sync-and-recovery.mdx b/packages/twenty-docs/l/zh/developers/extend/apps/operations/sync-and-recovery.mdx index f6d6644b32..4565b3e793 100644 --- a/packages/twenty-docs/l/zh/developers/extend/apps/operations/sync-and-recovery.mdx +++ b/packages/twenty-docs/l/zh/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` | 计算并打印 diff;不写入任何内容。 | +| 同步一次后退出(CI、脚本、钩子) | `yarn twenty apply` | 执行一次构建和同步,然后退出。 添加 `--force` 以跳过破坏性变更确认。 | +| 在**不应用变更**的前提下预览更改 | `yarn twenty plan` | 计算并打印 diff;不写入任何内容。 | | 从工作区中移除该应用 | `yarn twenty app:uninstall` | 添加 `--yes` 以跳过提示。 | | 将 tar 包发送到服务器 | `yarn twenty app:publish --private` | 需要一个**严格更高的** `package.json` 版本——参见 [Publishing](/l/zh/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` 就地同步你的 manifest,且从不要求修改版本,因此你无需为了迭代去改动 `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) ``` 这是你的首要诊断工具:它会准确告诉你哪些对象、字段和布局发生了变化,这样你就可以在查看 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) 使用该标识符在 manifest 中(以及在需要时在工作区中)定位该实体,而不是猜测哪个实体发生了冲突。 -## 预览更改(干跑 / dry run) +## 预览更改(计划) -`yarn twenty dev --once --dry-run` 会构建你的 manifest,向服务器请求迁移计划并打印出来——**不会实际应用任何内容**。 这是在真正执行前,以安全方式回答“这次同步会改动什么?”的办法。 +`yarn twenty plan` 会构建你的 manifest,向服务器请求迁移计划并打印出来——**不会实际应用任何内容**。 这是在真正执行前,以安全方式回答“这次同步会改动什么?”的办法。 ```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`。 +计划只会预览**元数据**更改,并且要求应用至少已经同步过一次(这样工作区才知道它的存在)。 如果你在一个从未同步过的应用上运行它,服务器会报告该应用尚未安装——先运行一次 `yarn twenty dev`。 ## 恢复梯子 当本地元数据看起来不对时,按以下顺序逐步升级,并在问题解决后立即停止。 每一步都比前一步更具破坏性。 -1. **重新同步。** 再次运行 `yarn twenty dev --once`。 同步是幂等的——在干净的 manifest 上重新运行是安全的,并且通常可以解决瞬时故障。 -2. **预览计划。** 运行 `yarn twenty dev --once --dry-run`,在不应用变更的前提下,准确查看下一次同步打算修改什么。 +1. **重新同步。** 再次运行 `yarn twenty apply`。 同步是幂等的——在干净的 manifest 上重新运行是安全的,并且通常可以解决瞬时故障。 +2. **预览计划。** 运行 `yarn twenty plan`,在不应用变更的前提下,准确查看下一次同步打算修改什么。 3. **阅读具名错误。** 如果同步失败,记录消息中的元数据类型和 `universalIdentifier`(见上),并在 manifest 中定位该实体。 冲突通常指向重复或被重复使用的标识符。 4. **卸载并重新安装。** 先执行 `yarn twenty app:uninstall`,然后再次同步(`yarn twenty dev`)。 这会在保留你工作区其余部分不变的情况下,从零重建该应用的元数据。 5. **完全重置(最后手段)。** 执行 `yarn twenty docker:reset`,然后重新播种并重新同步。 diff --git a/packages/twenty-docs/l/zh/developers/extend/apps/operations/testing.mdx b/packages/twenty-docs/l/zh/developers/extend/apps/operations/testing.mdx index 0a36f7841e..b165f1f685 100644 --- a/packages/twenty-docs/l/zh/developers/extend/apps/operations/testing.mdx +++ b/packages/twenty-docs/l/zh/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` | 构建应用,并可选地打包为 tar 包 | -| `appDeploy` | 将 tar 包上传到服务器 | -| `appInstall` | 在活动工作区安装该应用 | -| `appUninstall` | 从活动工作区卸载该应用 | +| 函数 | 描述 | +| -------------- | ----------------------------------- | +| `appBuild` | 构建应用,并可选地打包为 tar 包 | +| `appDeploy` | 将 tar 包上传到服务器 | +| `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`,并报告所有类型错误。 脚手架生成的应用还会提供一个 `yarn typecheck` 脚本,它也会覆盖测试文件(`tsconfig.spec.json`)。 ## 使用 GitHub Actions 进行 CI -脚手架工具会在 `.github/workflows/ci.yml` 生成一个开箱即用的 GitHub Actions 工作流。 它会在每次向 `main` 推送以及拉取请求上自动运行你的集成测试。 +脚手架工具会在 `.github/workflows/ci.yml` 生成一个开箱即用的工作流。 在每次向 `main` 推送代码以及每个拉取请求上,它都会在 runner 中启动一个临时的 Twenty 服务器(通过 `twentyhq/twenty/.github/actions/spawn-twenty-app-dev-test` action),然后运行 `yarn lint`、`yarn typecheck`、`yarn test:unit` 和 `yarn test`,并将 `TWENTY_API_URL` / `TWENTY_API_KEY` 指向该服务器。 无需任何机密信息,你可以在工作流顶部通过 `TWENTY_VERSION` 环境变量固定服务器版本。 -工作流: - -1. 检出你的代码 -2. 使用 `twentyhq/twenty/.github/actions/spawn-twenty-docker-image` 动作启动一个临时的 Twenty 服务器 -3. 使用 `yarn install --immutable` 安装依赖 -4. 运行 `yarn test`,并从该动作的输出中注入 `TWENTY_API_URL` 和 `TWENTY_API_KEY` - -```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 会自动提供 `GITHUB_TOKEN` 机密。 - -若要固定为特定的 Twenty 版本而不是 `latest`,请在工作流顶部修改 `TWENTY_VERSION` 环境变量。 +完整的脚手架工作流(`ci.yml` 和 `cd.yml` 部署流水线)的演练说明,请参见 [发布 → 自动化 CI/CD](/l/zh/developers/extend/apps/operations/publishing#automated-cicd-scaffolded-workflows)。 diff --git a/packages/twenty-docs/l/zh/developers/extend/apps/tutorials/document-generator/building-the-ui.mdx b/packages/twenty-docs/l/zh/developers/extend/apps/tutorials/document-generator/building-the-ui.mdx index 33ce7d7773..c68b2c1614 100644 --- a/packages/twenty-docs/l/zh/developers/extend/apps/tutorials/document-generator/building-the-ui.mdx +++ b/packages/twenty-docs/l/zh/developers/extend/apps/tutorials/document-generator/building-the-ui.mdx @@ -87,9 +87,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 }), @@ -176,7 +178,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/zh/developers/extend/apps/tutorials/document-generator/http-routes.mdx b/packages/twenty-docs/l/zh/developers/extend/apps/tutorials/document-generator/http-routes.mdx index 6bd5712c69..3d740b6248 100644 --- a/packages/twenty-docs/l/zh/developers/extend/apps/tutorials/document-generator/http-routes.mdx +++ b/packages/twenty-docs/l/zh/developers/extend/apps/tutorials/document-generator/http-routes.mdx @@ -9,8 +9,12 @@ description: 通过 HTTP 触发函数并将文档渲染为 web 页面。 * a **POST** 让UI 调用来生成文档的端点,和 * 一个公开的 **GET** 端点,将文档作为可打印的网页。 -两者都使用 `httpRouteTriggerSettings` 。 App rough are served under `/s` under your -20 server (e.g. `http://localhost:2020/s/documents/generate`). +两者都使用 `httpRouteTriggerSettings` 。 在本地开发服务器上,应用路由在带有 `/s` 前缀的路径下提供服务(例如 `http://localhost:2020/s/documents/generate`)。 + + +在 Twenty Cloud 上,路由通过工作区专用的函数域名提供服务——即 Twenty 注入的、作为 `TWENTY_FUNCTIONS_URL` 的 URL,且没有 `/s` 前缀。 在那里,`/s` 前缀已被弃用,仅保留用于自托管和本地实例。 +参见[调用逻辑函数](/l/zh/developers/extend/apps/layout/front-components#calling-a-logic-function)。 + ## POST 路由 — 按需生成 diff --git a/packages/twenty-docs/l/zh/developers/extend/apps/tutorials/document-generator/publishing.mdx b/packages/twenty-docs/l/zh/developers/extend/apps/tutorials/document-generator/publishing.mdx index 58d0f33205..06d289b927 100644 --- a/packages/twenty-docs/l/zh/developers/extend/apps/tutorials/document-generator/publishing.mdx +++ b/packages/twenty-docs/l/zh/developers/extend/apps/tutorials/document-generator/publishing.mdx @@ -76,11 +76,11 @@ export default defineApplication({ 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/zh/developers/extend/apps/operations/testing) 和 [同步和恢复](/l/zh/developers/extend/apps/operations/sync-and-recovery)。