snapshot of https://github.com/twentyhq/twenty.git (main, 2026-09-17; blobs > 131072B omitted)
Some checks failed
App Prod-Parity E2E Dispatch / dispatch (push) Failing after 10s
CD Storybook UI / build (push) Failing after 3m25s
CD Storybook UI / dispatch (push) Has been skipped
CD deploy main / website-changed-files (push) Successful in 18m8s
CI Agent Skills / changed-files-check (push) Successful in 18m9s
CI Create App E2E minimal / changed-files-check (push) Successful in 24s
CI Create App / changed-files-check (push) Successful in 26s
CI Docs / changed-files-check (push) Successful in 26s
CI Emails / changed-files-check (push) Successful in 28s
CI Example App Hello World / changed-files-check (push) Successful in 23s
CI Front / changed-files-check (push) Has been skipped
CI Example App Postcard / changed-files-check (push) Successful in 27s
CI UI / changed-files-check (push) Has been skipped
CI Utils Test / changed-files-check (push) Successful in 45s
CI Twenty Apps / discover (push) Successful in 1m3s
CD deploy main / deploy-main (push) Failing after 3s
CI Agent Skills / agent-skills-validate (push) Has been skipped
CI Create App / create-app-test (lint) (push) Has been skipped
CI Emails / emails-test (push) Has been skipped
CI Front / front-task (lingui:compile) (push) Has been skipped
CI Front / front-task (lint) (push) Has been skipped
CI Example App Postcard / example-app-postcard (push) Has been skipped
CI UI / ui-task (lint) (push) Has been skipped
CI UI / ui-task (pack-size) (push) Has been skipped
CI UI / ui-task (size) (push) Has been skipped
CI UI / ui-task (test) (push) Has been skipped
CI UI / ui-task (typecheck) (push) Has been skipped
CI Utils Test / utils-test (push) Has been skipped
CI Twenty Apps / ${{ matrix.app.name }} (push) Has been skipped
CI Emails / ci-emails-status-check (push) Successful in 2s
CI Example App Hello World / ci-example-app-hello-world-status-check (push) Successful in 2s
CI Example App Postcard / ci-example-app-postcard-status-check (push) Successful in 3s
CI UI / ui-sb-test (push) Has been skipped
CI Utils Test / ci-utils-test-status-check (push) Successful in 2s
CI Twenty Apps / ci-twenty-apps-status-check (push) Successful in 2s
CI UI / ci-ui-status-check (push) Successful in 2s
CI Desktop / desktop (push) Failing after 1m8s
Push docs to Crowdin / Push documentation to Crowdin (push) Failing after 55s
Push translations to Crowdin / Extract and upload translations (push) Failing after 5s
Push website translations to Crowdin / Extract and upload website translations (push) Failing after 5s
CI Create App / create-app-test (test) (push) Has been skipped
CI Create App / create-app-test (typecheck) (push) Has been skipped
CI Docs / docs-lint (push) Has been skipped
CI Example App Hello World / example-app-hello-world (push) Has been skipped
CI Front / front-task (lingui:extract) (push) Has been skipped
CI Front / front-task (test) (push) Has been skipped
CI Front / front-task (typecheck) (push) Has been skipped
CI UI / ui-sb-build (push) Failing after 1m32s
CI Twenty Apps / ${{ matrix.app.name }} (${{ matrix.server-source }}) (push) Has been skipped
CI Agent Skills / agent-skills-publish (push) Has been skipped
CI Create App / ci-create-app-status-check (push) Successful in 2s
CI Agent Skills / agent-skills-remote-source (push) Has been skipped
CI Create App E2E minimal / create-app-e2e-minimal (push) Has been cancelled
CI E2E Main / e2e-test (push) Has been cancelled
CI Front / front-sb-build (push) Has been cancelled
CI Front / front-build (push) Has been cancelled
CI Create App E2E minimal / ci-create-app-e2e-minimal-status-check (push) Has been cancelled
CI E2E Main / ci-e2e-main-status-check (push) Has been cancelled
CI E2E Main / notify-main-ci-failure (push) Has been cancelled
CI E2E Main / qa-scout-comment (push) Has been cancelled
CI Front / front-sb-test (push) Has been cancelled
CI Front / front-sb-screenshots (push) Has been cancelled
CI Front / ci-front-status-check (push) Has been cancelled
DPA Sub-Processor Sync / sync-subprocessors (push) Failing after 3s
AI Catalog Sync / sync-catalog (push) Failing after 2s

This commit is contained in:
upstream-mirror
2026-09-17 23:28:13 +05:00
commit e7da97194a
32688 changed files with 4208681 additions and 0 deletions

View File

@@ -0,0 +1,131 @@
---
title: CLI
description: أوامر yarn twenty لتنفيذ الدوال، وبثّ السجلات، وإدارة تثبيتات التطبيقات، والتبديل بين الريموتات.
icon: terminal
---
تُعد واجهة سطر الأوامر `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/operations/translations) |
| `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 \<name>` لاستهداف خادم بعيد معيَّن بدلاً من الخادم الافتراضي.
## تنفيذ الدوال (`yarn twenty dev:function:exec`)
تشغيل دالة منطقية يدويًا دون تشغيلها عبر HTTP أو cron أو حدث قاعدة بيانات:
```bash filename="Terminal"
# Execute by function name
yarn twenty dev:function:exec -n create-new-post-card
# Execute by universalIdentifier
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 install and uninstall hooks
yarn twenty dev:function:exec --postInstall
yarn twenty dev:function:exec --preInstall
yarn twenty dev:function:exec --uninstall
```
## عرض سجلات الدوال (`yarn twenty dev:function:logs`)
بثّ سجلات التنفيذ لدوال تطبيقك المنطقية:
```bash filename="Terminal"
# Stream all function logs
yarn twenty dev:function:logs
# Filter by function name
yarn twenty dev:function:logs -n create-new-post-card
# Filter by universalIdentifier
yarn twenty dev:function:logs -u e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf
```
<Note>
يختلف هذا عن `yarn twenty docker:logs`، الذي يعرض سجلات حاوية Docker. يعرض `yarn twenty dev:function:logs` سجلات تنفيذ دوال تطبيقك من خادم Twenty.
</Note>
## توليد العميل محدد الأنواع (`yarn twenty dev:generate-client`)
أعد توليد عميل واجهة برمجة التطبيقات محدد الأنواع (`twenty-client-sdk`) من مخطط الجهة البعيدة النشطة، دون بناء تطبيق أو مزامنته. استخدمه للحصول على عميل محدد الأنواع في أي مشروع — مثل خدمة خلفية موجودة في مستودع منفصل — يتواصل مع مثيل Twenty الخاص بك:
```bash filename="Terminal"
# In your project (no Twenty app definition required)
yarn add twenty-sdk twenty-client-sdk
# Connect to the Twenty instance to generate the client from
yarn twenty remote:add
# Generate the typed client into node_modules/twenty-client-sdk
yarn twenty dev:generate-client
```
ثم استورد العميل في شيفرتك:
```typescript
import { CoreApiClient } from 'twenty-client-sdk/core';
```
أعد تشغيل الأمر كلما تغيّر نموذج البيانات لديك لتحديث الأنواع المُولَّدة.
<Note>
يتم إنشاء العميل البرمجي داخل `node_modules`، لذا لا يُدرج مع شيفرتك في الالتزامات (commits). شغّل `yarn twenty dev:generate-client` بعد كل عملية تثبيت (على سبيل المثال في سكربت `postinstall` أو في CI).
</Note>
## إلغاء تثبيت تطبيق (`yarn twenty app:uninstall`)
أزل تطبيقك من مساحة العمل النشطة:
```bash filename="Terminal"
yarn twenty app:uninstall
# Skip the confirmation prompt
yarn twenty app:uninstall --yes
```
## إدارة الريموتات
**الريموت** هو خادم Twenty يتصل به تطبيقك. أثناء الإعداد، تُنشئ أداة إنشاء الهيكل واحدًا لك تلقائيًا. يمكنك إضافة ريموتات أخرى أو التبديل بينها في أي وقت.
```bash filename="Terminal"
# Add a new remote (opens a browser for OAuth login)
yarn twenty remote:add
# Connect to a local Twenty server (auto-detects port 2020 or 3000)
yarn twenty remote:add --local
# Add a remote non-interactively (useful for CI)
yarn twenty remote:add --url https://your-twenty-server.com --api-key $TWENTY_API_KEY --as my-remote
# List all configured remotes
yarn twenty remote:list
# Set the active remote
yarn twenty remote:use <name>
# Check that the active remote's authentication is still valid
yarn twenty remote:status
# Remove a remote
yarn twenty remote:remove <name>
```
تُخزَّن بيانات اعتمادك في `~/.twenty/config.json`.

View File

@@ -0,0 +1,32 @@
---
title: نظرة عامة
description: قم ببناء تطبيقك واختباره وإصداره — أوامر CLI، واختبارات التكامل، وCI، والنشر إلى خادم أو إلى npm.
icon: rocket
---
**طبقة العمليات** هي كل ما تفعله *على* تطبيقك وليس *به*: استدعاء أوامر CLI، وتشغيل اختبارات التكامل على خادم Twenty حقيقي، وإعداد CI، وإطلاق الإصدارات — إما كملف tarball يُنشر على خادم واحد أو كحزمة npm مُدرجة في المتجر.
```text
develop ─▶ test ─▶ build ─▶ deploy / publish
─────── ──── ───── ─────────────────
yarn yarn yarn yarn twenty app:publish --private (tarball → one server)
twenty test twenty
dev dev:build yarn twenty app:publish (npm → marketplace)
```
## في هذا القسم
<CardGroup cols={2}>
<Card title="CLI" icon="terminal" href="/l/ar/developers/extend/apps/operations/cli">
مرجع `yarn twenty` — exec، logs، uninstall، remotes.
</Card>
<Card title="المزامنة والاستعادة" icon="compass" href="/l/ar/developers/extend/apps/operations/sync-and-recovery">
أي أمر يُستخدم ومتى، وكيفية قراءة فروق المزامنة، وسلّم الاستعادة.
</Card>
<Card title="الاختبار" icon="flask" href="/l/ar/developers/extend/apps/operations/testing">
إعداد Vitest، اختبارات التكامل، فحص الأنواع، سير عمل CI.
</Card>
<Card title="النشر" icon="upload" href="/l/ar/developers/extend/apps/operations/publishing">
البناء، نشر ملف tarball، النشر إلى npm، التثبيت.
</Card>
</CardGroup>

View File

@@ -0,0 +1,293 @@
---
title: النشر
icon: upload
description: وزّع تطبيق Twenty الخاص بك على سوق Twenty أو انشره داخليًا.
---
## نظرة عامة
بمجرد أن يكون تطبيقك [مبنيًا ومختبرًا محليًا](/l/ar/developers/extend/apps/getting-started/concepts)، لديك مساران لتوزيعه:
* **نشر أرشيف tar** — ارفع تطبيقك مباشرةً إلى خادم Twenty محدد للاستخدام الداخلي أو الخاص.
* **النشر على npm** — أدرج تطبيقك في سوق Twenty ليتسنى لأي مساحة عمل اكتشافه وتثبيته.
كلا المسارين يبدآن من نفس خطوة **build**.
## بناء تطبيقك
شغّل أمر build لتجميع تطبيقك وإنشاء ملف `manifest.json` جاهز للتوزيع:
```bash filename="Terminal"
yarn twenty dev:build
```
يقوم هذا بتجميع مصادر TypeScript، وتحويل دوال المنطق ومكوّنات الواجهة الأمامية، وكتابة كل شيء إلى `.twenty/output/`. أضِف `--tarball` لإنتاج حزمة `.tgz` أيضًا للتوزيع اليدوي أو لأمر publish.
## النشر إلى خادم (tarball)
بالنسبة للتطبيقات التي لا تريد إتاحتها للعامة — مثل الأدوات المملوكة، أو عمليات التكامل الخاصة بالمؤسسات فقط، أو الإصدارات التجريبية — يمكنك نشر tarball مباشرةً إلى خادم Twenty.
### المتطلبات الأساسية
قبل النشر، تحتاج إلى remote مُعدّ يشير إلى خادم الهدف. تُخزّن remotes عنوان URL للخادم وبيانات اعتماد المصادقة محليًا في `~/.twenty/config.json`.
أضِف remote:
```bash filename="Terminal"
yarn twenty remote:add --url https://your-twenty-server.com --as production
```
### النشر
بناء تطبيقك ورفعه إلى الخادم في خطوة واحدة:
```bash filename="Terminal"
yarn twenty app:publish --private
# To deploy to a specific remote:
# yarn twenty app:publish --private --remote production
```
### مشاركة تطبيق منشور
تطبيقات tarball لا تُدرَج في السوق العامة، لذا لن تكتشفها مساحات العمل الأخرى على الخادم نفسه عبر الاستعراض. لمشاركة تطبيق منشور:
1. اذهب إلى **الإعدادات > التطبيقات > التسجيلات** وافتح تطبيقك
2. في علامة التبويب **التوزيع**، انقر **نسخ رابط المشاركة**
3. شارك هذا الرابط مع المستخدمين في مساحات عمل أخرى — سيأخذهم مباشرةً إلى صفحة تثبيت التطبيق
يستخدم رابط المشاركة عنوان URL الأساسي للخادم (من دون أي نطاق فرعي لمساحة عمل)، لذا يعمل مع أي مساحة عمل على الخادم.
### إدارة الإصدارات
عند تحديث تطبيق tarball منشور مسبقًا، يشترط الخادم أن تكون قيمة `version` في `package.json` **أعلى قطعًا** (وفق ترتيب [الإصدار الدلالي](https://semver.org)) من الإصدار المنشور حاليًا. إعادة نشر الإصدار نفسه، أو دفع إصدار أدنى، يُرفَض قبل تخزين ملف tarball — سترى خطأ `VERSION_ALREADY_EXISTS` من CLI.
لطرح تحديث:
1. قم بزيادة الحقل `version` في ملف `package.json` (مثلًا: `1.2.3` → `1.2.4`، `1.3.0`، أو `2.0.0`)
2. شغّل `yarn twenty app:publish --private` (أو `yarn twenty app:publish --private --remote production`)
3. مساحات العمل التي تم تثبيت التطبيق فيها وتم تفعيل الترقية التلقائية له (في علامة تبويب الإعدادات الخاصة بالتطبيق) سيتم ترقيتها تلقائيًا في الخلفية؛ أما المساحات الأخرى فسترى أن الترقية متاحة في إعداداتها
<Note>
علامات ما قبل الإصدار تعمل كما هو متوقع: زيادة `1.0.0-rc.1` → `1.0.0-rc.2` مسموح بها، ويُعترَف بالإصدار النهائي مثل `1.0.0` على أنه أعلى من `1.0.0-rc.5`. يجب أن يكون الإصدار في `package.json` بنفسه سلسلة semver صالحة.
</Note>
{/* TODO: add screenshot of the Upgrade button */}
### توافق إصدار الخادم
إذا كان تطبيقك يستخدم ميزة تم تقديمها في إصدار معيّن من خادم Twenty (على سبيل المثال، موفّرو OAuth الذين أضيفوا في v2.3.0)، فيجب عليك التصريح بأدنى إصدار من الخادم يتطلبه تطبيقك باستخدام الحقل `engines.twenty` في `package.json`:
```json filename="package.json"
{
"name": "twenty-my-app",
"version": "1.0.0",
"engines": {
"node": "^24.5.0",
"twenty": ">=2.3.0"
}
}
```
القيمة عبارة عن [نطاق semver](https://github.com/npm/node-semver#ranges) قياسي. أنماط شائعة:
| النطاق | المعنى |
| ---------------------------------- | ------------------------------------------------ |
| `>=2.3.0` | أي خادم من 2.3.0 فصاعدًا |
| `>=2.3.0 \<3.0.0` | 2.3.0 أو أحدث، لكن أقل من الإصدار الرئيسي التالي |
| `^2.3.0` | مماثل لـ `>=2.3.0 \<3.0.0` |
**ماذا يحدث وقت النشر والتثبيت:**
* إذا تم تعيين `engines.twenty` ولم يستوفِ إصدار الخادم الهدف النطاق، فسيتم رفض النشر (tarball upload) أو التثبيت بخطأ `SERVER_VERSION_INCOMPATIBLE` ورسالة تُشير إلى كلٍ من النطاق المطلوب وإصدار الخادم الفعلي.
* إذا كان `engines.twenty` **غير مُعين**، فسيُقبل التطبيق على أي إصدار من الخادم (متوافق مع الإصدارات السابقة للتطبيقات الحالية).
* إذا لم يكن لدى الخادم قيمة `APP_VERSION` مُكوَّنة، فسيتم تخطي الفحص.
<Note>
الخادم هو الجهة المرجعية للفحص — إذ يتحقق من `engines.twenty` عند كلٍ من رفع tarball وتثبيت مساحة العمل. إذا قمت بنشر tarball خارج القناة المعتادة أو التثبيت من السوق، فسيظل الخادم يفرض التوافق.
</Note>
## CI/CD المؤتمتة (مهام سير عمل مُولَّدة بالقوالب)
التطبيقات المُولَّدة باستخدام `create-twenty-app` تأتي افتراضيًا مع ثلاث مهام سير عمل من GitHub Actions ضمن `.github/workflows/`. يعمل CI بدون أي إعداد، بينما يتطلّب CD سرًّا واحدًا فقط، ويتطلّب النشر إلى npm إعدادًا لمرة واحدة لـ npm trusted-publisher.
### CI — `ci.yml`
يشغّل اختبارات التكامل عند كل دفع إلى `main` وعند كل طلب سحب.
**ماذا يفعل:**
1. يجلب مصدر تطبيقك.
2. ينشئ مثيلاً اختبارياً معزولاً من Twenty باستخدام الإجراء المركّب `twentyhq/twenty/.github/actions/spawn-twenty-app-dev-test@main` (المكافئ في CI للأمر `yarn twenty docker:start --test`).
3. يُفعِّل Corepack، ويُعدّ Node.js من ملف `.nvmrc` لديك، ويثبّت التبعيات بواسطة `yarn install --immutable`.
4. يشغّل `yarn test`، ويمرّر `TWENTY_API_URL` و`TWENTY_API_KEY` من المثيل الذي تم إنشاؤه بحيث تتمكّن اختباراتك من التواصل مع خادم حقيقي.
**خيارات التكوين:**
* `TWENTY_VERSION` (متغيّر بيئة، القيمة الافتراضية `latest`) — ثبّت نسخة خادم Twenty المستخدمة في CI عبر تعديل هذا في `ci.yml`.
* يتم تجميع التشغيل المتزامن حسب `github.ref` ويلغي التشغيلات قيد التقدّم عند أي دفع جديد.
لا تتطلّب أي أسرار — مثيل الاختبار مؤقّت ويستمر فقط طوال مدّة المهمّة.
### CD — `cd.yml`
ينشر تطبيقك إلى خادم Twenty مُهيّأ عند كل دفع إلى `main`، وبشكل اختياري من طلب سحب عند تطبيق الوسم `deploy`.
**ماذا يفعل:**
1. يجلب رأس طلب السحب (للطلبات الموسومة) أو الالتزام المدفوع.
2. يشغّل `twentyhq/twenty/.github/actions/deploy-twenty-app@main` — وهو المكافئ في CI للأمر `yarn twenty app:publish --private`.
3. يشغّل `twentyhq/twenty/.github/actions/install-twenty-app@main` بحيث تُثبَّت النسخة المُنشَرة حديثًا في مساحة العمل المستهدفة.
**التكوين المطلوب:**
| الإعداد | حيث | الغرض |
| ----------------------- | --------------------------------------------------------------- | ---------------------------------------------------------------------------------------- |
| `TWENTY_DEPLOY_URL` | `env` في `cd.yml` (القيمة الافتراضية `http://localhost:3000`) | خادم Twenty الذي سيتم النشر إليه. غيّر هذا إلى عنوان URL لخادمك الحقيقي قبل أول استخدام. |
| `TWENTY_DEPLOY_API_KEY` | في مستودع GitHub **Settings → Secrets and variables → Actions** | مفتاح API يمتلك إذن النشر على الخادم المستهدف. |
<Note>
القيمة الافتراضية لـ `TWENTY_DEPLOY_URL` وهي `http://localhost:3000` مجرد عنصر نائب — لن تصل إلى أي شيء من مُشغِّل مستضاف لدى GitHub. حدّثها إلى عنوان URL العام لخادمك (أو استخدم مُشغِّلًا مستضافًا ذاتيًا مع وصول شبكي) قبل تمكين CD.
</Note>
**تشغيل نشر معاينة من طلب سحب:**
أضِف الوسم `deploy` إلى طلب سحب. الشرط `if:` في `cd.yml` سيشغّل المهمّة لذلك الطلب مستخدمًا التزام رأس الطلب، مما يتيح لك التحقّق من التغيير على الخادم المستهدف قبل الدمج.
### النشر — `publish.yml`
ينشر تطبيقك إلى npm مع إثبات المصدر (provenance) عند دفع وسم إصدار (مثلًا: `v1.0.0`)، أو عند تشغيل سير العمل يدويًا من علامة التبويب Actions.
**ماذا يفعل:**
1. يجلب شيفرة تطبيقك، ويُعد Node.js، ويُحدِّث npm (يتطلّب النشر الموثوق npm 11.5.1 أو أحدث).
2. يشغّل `yarn twenty app:publish`، الذي يبني التطبيق وينشر `.twenty/output` إلى npm. في بيئة CI يضيف تلقائيًا `--provenance` و`--access public`، لذا لا حاجة إلى أي أعلام في سير العمل.
**إعداد لمرة واحدة:**
على npmjs.com افتح الحزمة الخاصة بك > **Settings → Trusted Publisher** وسجّل هذا المستودع مع سير عمل `publish.yml` (راجع [وثائق النشر الموثوق من npm](https://docs.npmjs.com/trusted-publishers)). يُصادِق النشر مع بيانات provenance على مستودع GitHub الذي بنى الحزمة، وهو أيضًا ما يتيح لك المطالبة بملكية تطبيقك في سوق Twenty.
<Note>
npm يقبل إثبات المصدر (provenance) فقط من مستودعات المصدر **العامّة**. إذا قمت بالنشر من مستودع خاص، يرفض npm حزمة إثبات المصدر عبر OIDC برسالة `E422 ... خطأ Unsupported GitHub Actions source repository visibility: "private"`. لنشر الحزمة (publish) من مستودع خاص، يمكنك إيقاف استخدام إثبات المصدر (provenance) عبر تعيين `TWENTY_APP_PUBLISH_DISABLE_PROVENANCE: 'true'` في متغيّر `env` لخطوة النشر (publish step) (تلميح مضاف كتعليق موجود في ملف `publish.yml` المُنشأ تلقائيًا):
```yaml filename=".github/workflows/publish.yml"
- name: Publish to npm
env:
TWENTY_APP_PUBLISH_DISABLE_PROVENANCE: 'true'
run: yarn twenty app:publish
```
</Note>
### تثبيت الإجراءات القابلة لإعادة الاستخدام
يشير سيرَا العمل `ci.yml` و`cd.yml` إلى إجراءات قابلة لإعادة الاستخدام عند `@main`، لذا تُلتقط تحديثات الإجراءات في مستودع `twentyhq/twenty` تلقائيًا. إذا كنت تريد بناءات حتمية، فاستبدِل `@main` بقيمة SHA لالتزام أو بوسم إصدار في كل سطر `uses:`.
## النشر على npm
يُتيح النشر على npm إمكانية العثور على تطبيقك في سوق Twenty. يمكن لأي مساحة عمل في Twenty استعراض تطبيقات السوق وتثبيتها وترقيتها مباشرةً من واجهة المستخدم.
### المتطلبات
* حساب على [npm](https://www.npmjs.com)
* الكلمة المفتاحية `twenty-app` في مصفوفة `keywords` في `package.json` (أضفها يدويًا — فهي غير مضمنة افتراضيًا في قالب `create-twenty-app`)
```json filename="package.json"
{
"name": "twenty-app-postcard-sender",
"version": "1.0.0",
"keywords": ["twenty-app"]
}
```
### بيانات التعريف لسوق التطبيقات
يدعم إعداد `defineApplication()` حقولًا اختيارية تتحكم في كيفية ظهور تطبيقك في السوق. استخدم `logo` و`galleryImages` للإشارة إلى الصور من مجلد `public/`:
```ts src/application-config.ts
export default defineApplication({
universalIdentifier: '...',
displayName: 'My App',
description: 'A great app',
logo: 'public/logo.png',
galleryImages: [
'public/screenshot-1.png',
'public/screenshot-2.png',
],
});
```
اطّلع على [أكورديون defineApplication](/l/ar/developers/extend/apps/config/application#marketplace-metadata) في صفحة بناء التطبيقات للاطلاع على القائمة الكاملة لحقول السوق (`author` و`category` و`aboutDescription` و`websiteUrl` و`termsUrl` وغيرها).
#### أبعاد صور المعرض الموصى بها
يعرض السوق `galleryImages` داخل حاوية ثابتة بنسبة `8:5` (على سبيل المثال، `1600×1000 px`).
<Note>
تُعرض صور المعرض بأي نسبة عرض إلى ارتفاع بالكامل ولن يتم اقتطاعها مطلقًا، ولكن أي شيء أطول أو أضيق بكثير من `8:5` سيظهر مساحات فارغة على الجانبين.
</Note>
#### الحد الأقصى لحجم الصورة
يجب ألّا يتجاوز حجم ملف `logo` وكل ملف من `galleryImages` **10 MB**. سيتم تخطي الملفات الأكبر حجمًا عندما يقوم سوق النشر بإعادة استضافة أصولك المنشورة، وبالتالي لن يتم عرضها.
### النشر
```bash filename="Terminal"
yarn twenty app:publish
```
للنشر تحت dist-tag معيّن (مثلًا: `beta` أو `next`):
```bash filename="Terminal"
yarn twenty app:publish --tag beta
```
### كيف تعمل آلية الاكتشاف في السوق
يقوم خادم Twenty بمزامنة كتالوج السوق من سجل npm **كل ساعة**.
يمكنك تشغيل المزامنة فورًا بدلًا من الانتظار:
```bash filename="Terminal"
yarn twenty dev:catalog-sync
# To target a specific remote:
# yarn twenty dev:catalog-sync --remote production
```
تأتي البيانات الوصفية المعروضة في سوق التطبيقات من إعدادات `defineApplication()` الخاصة بك — راجع قسم [البيانات الوصفية في سوق التطبيقات](#marketplace-metadata) أعلاه.
<Note>
إذا لم يحدد تطبيقك `aboutDescription` في `defineApplication()`، فسيستخدم السوق تلقائيًا ملف `README.md` الخاص بحزمتك من npm كمحتوى لصفحة حول. هذا يعني أنه يمكنك الاحتفاظ بملف README واحد لكل من npm وسوق Twenty. إذا كنت تريد وصفًا مختلفًا في السوق، فقم بتعيين `aboutDescription` بشكل صريح.
</Note>
### النشر عبر CI
ينشر سير عمل `publish.yml` المُنشأ تلقائيًا والموصوف أعلاه إلى npm تلقائيًا عند علامات الإصدارات، مع بيانات provenance. نظرًا لأن `yarn twenty app:publish` يضيف `--provenance` و`--access public` نيابةً عنك عند تشغيله في CI، فلا يحتاج سير العمل إلى أي أعلام npm — فقط إعداد الناشر الموثوق لمرة واحدة.
بالنسبة لأنظمة CI الأخرى (GitLab CI وCircleCI وغيرها)، شغّل `yarn install` ثم `yarn twenty app:publish`. يُصدِر بيانات provenance عندما تتمكّن البيئة من إصدار رمز OIDC، ويتم تخطيه تلقائيًا خلاف ذلك.
<Note>
تضيف **npm provenance** شارة ثقة إلى إدراجك على npm، مما يتيح للمستخدمين التحقّق من أن الحزمة تم بناؤها من التزام محدد ضمن خط أنابيب CI عام. وهي أيضًا ما يتيح لك المطالبة بملكية تطبيقك في سوق Twenty. راجع [وثائق npm provenance](https://docs.npmjs.com/generating-provenance-statements) للتفاصيل.
</Note>
## تثبيت التطبيقات
بعد نشر التطبيق (npm) أو نشره (tarball)، يمكن لمساحات العمل تثبيته عبر واجهة المستخدم.
اذهب إلى صفحة **الإعدادات > التطبيقات** في Twenty، حيث يمكن استعراض تطبيقات السوق والتطبيقات المنشورة عبر tarball وتثبيتها.
{/* TODO: add screenshot of the UI when the app is registered */}
يمكنك أيضًا تثبيت التطبيقات من سطر الأوامر:
```bash filename="Terminal"
yarn twenty app:install
```
<Note>
يفرض الخادم اعتماد إصدارات semver عند التثبيت، بما يعكس القواعد المطبّقة عند النشر:
* تثبيت الإصدار نفسه المثبّت بالفعل في مساحة عملك يُرفَض بخطأ `APP_ALREADY_INSTALLED`.
* تثبيت إصدار أدنى من الإصدار المثبّت حاليًا يُرفَض بخطأ `CANNOT_DOWNGRADE_APPLICATION`.
لتثبيت إصدار أحدث، انشره (deploy) أو انشره إلى السجل (publish) أولًا، ثم أعد تشغيل `yarn twenty app:install`.
</Note>

View File

@@ -0,0 +1,126 @@
---
title: المزامنة والاستعادة
description: أي أمر تستخدمه ومتى، وكيفية قراءة مخرجات المزامنة، وسلّم استعادة لما يجب فعله عندما تنحرف البيانات الوصفية المحلية — قبل الوصول إلى إعادة تعيين كاملة.
icon: compass
---
يدور تطوير التطبيقات محليًا حول **المزامنة**: يقوم الـ CLI بإعادة إنشاء ملف manifest الخاص بك ويطبّق الخادم فقط الفرق بينه وبين البيانات الوصفية الموجودة بالفعل في مساحة العمل لديك. تغطي هذه الصفحة الأمر الذي ينبغي استخدامه، وكيفية قراءة ما غيّرته المزامنة، وما الذي يجب فعله — بالترتيب — عندما تبدو الحالة المحلية غير متسقة.
## أي أمر، ومتى
<Note>
للتكرار اليومي المحلي ستحتاج تقريبًا دائمًا إلى `yarn twenty dev`. يُستخدم النشر والإصدار لإطلاق الإصدارات، **وليس** للحلقة المحلية.
</Note>
| ترغب في… | أمر | الملاحظات |
| ------------------------------------------------ | ----------------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
| التكرار محليًا مع المزامنة الحية | `yarn twenty dev` | يراقب ملفاتك ويجري مزامنة عند كل تغيير. |
| مزامنة واحدة ثم إنهاء (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` | يحذف **كل** البيانات المحلية — كملاذ أخير. |
<Note>
الأوامر `yarn twenty dev --once` و`yarn twenty dev --once --dry-run` هي أسماء بديلة مهملة للأمرين `yarn twenty apply` و`yarn twenty plan`.
</Note>
### لا تحتاج المزامنة المحلية إلى زيادة في الإصدار
تنطبق قاعدة `version` المتزايدة بدقة (`VERSION_ALREADY_EXISTS` عند النشر، و`APP_ALREADY_INSTALLED` / `CANNOT_DOWNGRADE_APPLICATION` عند التثبيت) على **`app:publish` / `app:install`** — مسار الإصدارات. يقوم `yarn twenty dev` بمزامنة ملف manifest في مكانه ولا يتطلّب تغيير الإصدار أبدًا، لذا لست بحاجة إلى تعديل `package.json` للتكرار. إذا وجدت نفسك تزيد الإصدار لاختبار تغيير محلي، فأنت تستخدم مسار الإصدارات بينما ما تريده هو حلقة التطوير.
## قراءة مخرجات المزامنة
كل عملية مزامنة تطبع تغييرات البيانات الوصفية التي تم تطبيقها (أو التي سيتم تطبيقها عند استخدام `plan`)، على غرار Terraform — كتلة واحدة لكل كيان مع خصائصه، ثم سطر ملخص:
```text filename="Terminal"
# 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
Migration action 'create' for 'fieldMetadata' (universalIdentifier: 2020...4337) failed
```
استخدم ذلك المعرّف للعثور على الكيان في ملف manifest الخاص بك (وإن لزم الأمر، في مساحة العمل) بدلًا من تخمين أيّها يتعارض.
## معاينة التغييرات (plan)
يقوم `yarn twenty plan` ببناء ملف manifest الخاص بك، ويطلب من الخادم خطة الترحيل، ويطبعها — **بدون تطبيق أي شيء**. إنها الطريقة الآمنة للإجابة عن سؤال "ما الذي ستغيّره هذه المزامنة؟" قبل الالتزام بها.
```bash filename="Terminal"
yarn twenty plan
```
```text filename="Terminal"
Building manifest...
Computing metadata plan (read-only, nothing will be applied)...
# fieldMetadata "crewCapacity" will be created
+ ...
Plan: 1 to add, 1 to change, 0 to destroy.
✓ Plan complete for My App — no changes were applied
```
خطة:
* **لا يكتب أي شيء** — لا ترحيل لبيانات وصفية، ولا تحديث لسجل التطبيق، ولا تغييرات في الأدوار/التبويبات الافتراضية، ولا توليد لعميل API.
* يُرجع **نفس الفرق** الذي ستُطبِّقه مزامنة حقيقية، حتى تتمكن من مراجعة الكيانات التي سيتم إنشاؤها/تحديثها/حذفها مسبقًا.
* يكون مفيدًا قبل إجراء تغيير محفوف بالمخاطر، أو عند مراجعة تغيير تم إنشاؤه بواسطة الذكاء الاصطناعي، أو في سكربت يجب أن يفشل إذا كان تغيير غير متوقَّع على وشك الحدوث.
<Note>
تقوم الخطة فقط بمعاينة **تغييرات البيانات الوصفية**، ويتطلّب ذلك أن يكون التطبيق قد تمت مزامنته مرة واحدة على الأقل (حتى تعرف به مساحة العمل). إذا شغّلته ضد تطبيق لم تتم مزامنته من قبل، سيبلغ الخادم أن التطبيق غير مُثبّت — شغّل `yarn twenty dev` مرة واحدة أولًا.
</Note>
## سلّم الاستعادة
عندما تبدو البيانات الوصفية المحلية غير صحيحة، صعِّد الإجراءات بهذا الترتيب وتوقّف بمجرد زوال العائق. كل خطوة أكثر إرباكًا من التي قبلها.
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`، ثم أعد التهيئة والمزامنة.
<Warning>
يقوم `yarn twenty docker:reset` بحذف **كل** البيانات في النسخة المحلية لديك — كل مساحة عمل، وكل سجل، وكل تطبيق. استخدمه فقط بعد فشل الخطوات السابقة.
</Warning>
<Note>
هل واجهت خطأ في البيانات الوصفية؟ يرجى [فتح مشكلة](https://github.com/twentyhq/twenty/issues/new/choose) وإرفاق رسالة الترحيل الفاشلة (بما في ذلك نوع البيانات الوصفية و`universalIdentifier`)، ومخرجات `Metadata changes` من عملية المزامنة، والأوامر التي شغّلتها.
</Note>
## تجنّب إجراء مزامنات متزامنة على مساحة عمل واحدة
تطبّق المزامنة عمليات ترحيل للبيانات الوصفية. قد يؤدّي تشغيل عدة عمليات مزامنة أو نشر أو تثبيت ضد **نفس** مساحة العمل في الوقت نفسه — على سبيل المثال، عدّة نوافذ طرفية أو وكلاء ذكاء اصطناعي يتكرّرون بالتوازي — إلى تداخل عمليات الترحيل تلك وترك البيانات الوصفية في حالة مطبَّقة جزئيًا.
يقوم الخادم بتسلسل عمليات المزامنة لكل مساحة عمل لمنع ذلك، لكن ما زال ينبغي عليك تمرير عمليات البيانات الوصفية الحساسة عبر عملية **واحدة** بدلًا من تنفيذها بالتوازي. إذا كنت تنظّم التطوير باستخدام عدة وكلاء، فمرّر استدعاءات المزامنة/النشر/التثبيت عبر طابور واحد حتى تعمل واحدة فقط في الوقت نفسه.
## تمييز أنواع الإخفاقات
عندما يحدث خلل ما، يتيح لك فرق البيانات الوصفية والأخطاء المسمّاة تحديد موضع الفشل:
* **خطأ في إنشاء ملف manifest** — يفشل الـ CLI قبل إجراء المزامنة (`MANIFEST_BUILD_FAILED`، `TYPECHECK_FAILED`)؛ أصلِح كود التطبيق لديك.
* **خطأ في المزامنة / الترحيل** — تنجح عملية الإنشاء لكن يفشل تطبيق الفرق، مع تسمية الكيان و`universalIdentifier`؛ أصلِح البيانات الوصفية المتعارِضة.
* **خطأ في حجم التبعيات** — تفشل عملية المزامنة أو التثبيت لأن `dependencies` الخاصة ببيئة الإنتاج في التطبيق كبيرة جدًا بحيث لا يمكن تثبيتها كطبقة وقت التشغيل (`LOGIC_FUNCTION_DEPENDENCIES_SIZE_EXCEEDED`)؛ انقل الحزم التي لا تستوردها دوال المنطق لديك في وقت التشغيل (مكتبات واجهة المستخدم وأدوات التطوير) إلى `devDependencies`.
* **خطأ في وقت تشغيل كود التطبيق** — تتم المزامنة بنجاح، ولكن دوال المنطق أو المكوّنات لديك لا تعمل بشكل صحيح أثناء وقت التشغيل؛ تحقّق من [سجلات الدوال](/l/ar/developers/extend/apps/operations/cli).
* **حالة المثيل المحلي** — لا ينطبق أيّ مما سبق وما زالت مساحة العمل تبدو غير صحيحة؛ تابع النزول في سلّم الاستعادة.

View File

@@ -0,0 +1,264 @@
---
title: الاختبار
description: إعداد Vitest، واختبارات تكامل مقابل خادم Twenty حقيقي، والتحقق من الأنواع، والتكامل المستمر (CI) باستخدام GitHub Actions.
icon: flask
---
يوفّر SDK واجهات برمجة قابلة للتنفيذ برمجيًا تمكّنك من بناء تطبيقك ونشره وتثبيته وإلغاء تثبيته من شيفرة الاختبار. بالاقتران مع [Vitest](https://vitest.dev/) وعملاء واجهة البرمجة مضبوطي الأنواع، يمكنك كتابة اختبارات تكامل تتحقّق من أن تطبيقك يعمل من البداية إلى النهاية مقابل خادم Twenty حقيقي.
## استخدام حِزَم npm
يمكنك تثبيت واستخدام أي حزمة npm في تطبيقك. يتم تجميع كلٍ من الدوال المنطقية والمكوّنات الأمامية باستخدام [esbuild](https://esbuild.github.io/)، والذي يُضمّن تبعياتها ضمن المخرجات — لا حاجة إلى `node_modules` وقت التشغيل.
### تثبيت حزمة
```bash filename="Terminal"
yarn add axios
```
ثم استوردها في شيفرتك:
```ts src/logic-functions/fetch-data.ts
import { defineLogicFunction } from 'twenty-sdk/define';
import axios from 'axios';
const handler = async (): Promise<any> => {
const { data } = await axios.get('https://api.example.com/data');
return { data };
};
export default defineLogicFunction({
universalIdentifier: '...',
name: 'fetch-data',
description: 'Fetches data from an external API',
timeoutSeconds: 10,
handler,
});
```
وينطبق الأمر نفسه على المكوّنات الأمامية:
```tsx src/front-components/chart.tsx
import { defineFrontComponent } from 'twenty-sdk/define';
import { format } from 'date-fns';
const DateWidget = () => {
return <p>Today is {format(new Date(), 'MMMM do, yyyy')}</p>;
};
export default defineFrontComponent({
universalIdentifier: '...',
name: 'date-widget',
component: DateWidget,
});
```
### كيف يعمل التجميع
تستخدم خطوة البناء أداة esbuild لإنتاج ملف واحد مستقل لكل دالة منطقية ولكل مكوّن أمامي. تُضمَّن الحزم المستوردة داخل الحزمة، باستثناء تلك المدرجة أدناه.
**الدوال المنطقية** تعمل في بيئة Node.js. الوحدات المدمجة في Node (`fs` و`path` و`crypto` و`http` وغيرها) متاحة ولا تحتاج إلى تثبيت.
**المكوّنات الأمامية** تعمل ضمن Web Worker. وحدات Node المدمجة **غير** متاحة — المتوفّر فقط حِزَم npm التي تعمل في بيئة المتصفّح. لاحظ أن الـ sandbox يطبّق نموذج DOM *جزئيًا*، لذلك قد تتمكّن الحزمة من البناء بنجاح ولكنها قد تفشل أثناء وقت التشغيل؛ راجع [القيود الحالية](/l/ar/developers/extend/apps/layout/front-components#current-limitations).
كلتا البيئتين تحتويان على `twenty-client-sdk/core` و`twenty-client-sdk/metadata` كوحدات متاحة مُسبقًا — لا تُضمَّن هذه ضمن الحزم بل تُحلّ وقت التشغيل بواسطة الخادم.
يمكن للمكوّنات الأمامية أيضًا مشاركة التبعيات من خلال [حزمة التبعيات المشتركة](/l/ar/developers/extend/apps/layout/front-components#sharing-dependencies-across-front-components). تُبنَى الحزم المدرجة في حقل `frontComponentSharedDependencies` في ملف package.json الخاص بالتطبيق مرة واحدة في ملف منفصل ويتم تحميلها منه أثناء وقت التشغيل بدلًا من تضمينها داخل كل مكوّن، لذا فإن المكوّن الذي يستورد حزمًا مشتركة فقط لا يحمل تقريبًا أي شيفرة تبعيات خاصة به.
## إعداد
يتضمّن التطبيق المُولَّد بالقالب بالفعل Vitest. إذا أعددته يدويًا، فثبّت التبعيات:
```bash filename="Terminal"
yarn add -D vitest vite-tsconfig-paths
```
أنشئ `vitest.config.ts` في جذر تطبيقك:
```ts vitest.config.ts
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 ?? '<the pre-seeded local dev 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({
projects: ['tsconfig.spec.json'],
ignoreConfigErrors: true,
}),
],
test: {
testTimeout: 120_000,
hookTimeout: 120_000,
fileParallelism: false,
include: ['src/**/*.integration-test.ts'],
globalSetup: ['src/__tests__/global-setup.ts'],
env: {
TWENTY_API_URL,
TWENTY_API_KEY,
},
},
});
```
أنشئ ملف إعداد عام يتحقق من إمكانية الوصول إلى الخادم، ويكتب ملف إعداد اختبار لـ SDK (`~/.twenty/config.test.json`)، ويزامن التطبيق قبل تشغيل الاختبارات:
```ts src/__tests__/global-setup.ts
import * as fs from 'fs';
import * as os from 'os';
import * as path from 'path';
import { appDevOnce, appUninstall } from 'twenty-sdk/cli';
const APP_PATH = process.cwd();
const CONFIG_DIR = path.join(os.homedir(), '.twenty');
export async function setup() {
const apiUrl = process.env.TWENTY_API_URL!;
const apiKey = process.env.TWENTY_API_KEY!;
// Verify the server is running
const response = await fetch(`${apiUrl}/healthz`);
if (!response.ok) {
throw new Error(`Twenty server is not reachable at ${apiUrl}.`);
}
// 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(CONFIG_DIR, 'config.test.json'),
JSON.stringify({
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 إلى الخادم |
| `appDevOnce` | بناء التطبيق ومزامنته مرة واحدة (نفس الأمر مثل `yarn twenty apply`) |
| `appInstall` | تثبيت التطبيق على مساحة العمل النشطة |
| `appUninstall` | إلغاء تثبيت التطبيق من مساحة العمل النشطة |
تُرجع كل دالة كائن نتيجة يحتوي على `success: boolean` وعلى إمّا `data` أو `error`.
## كتابة اختبار تكامل
إليك مثالًا كاملًا يبني التطبيق وينشره ويثبّته، ثم يتحقّق من ظهوره في مساحة العمل:
```ts src/__tests__/app-install.integration-test.ts
import { APPLICATION_UNIVERSAL_IDENTIFIER } from 'src/application-config';
import { appBuild, appDeploy, appInstall, appUninstall } from 'twenty-sdk/cli';
import { MetadataApiClient } from 'twenty-client-sdk/metadata';
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
const APP_PATH = process.cwd();
describe('App installation', () => {
beforeAll(async () => {
const buildResult = await appBuild({
appPath: APP_PATH,
tarball: true,
onProgress: (message: string) => console.log(`[build] ${message}`),
});
if (!buildResult.success) {
throw new Error(`Build failed: ${buildResult.error?.message}`);
}
const deployResult = await appDeploy({
tarballPath: buildResult.data.tarballPath!,
onProgress: (message: string) => console.log(`[deploy] ${message}`),
});
if (!deployResult.success) {
throw new Error(`Deploy failed: ${deployResult.error?.message}`);
}
const installResult = await appInstall({ appPath: APP_PATH });
if (!installResult.success) {
throw new Error(`Install failed: ${installResult.error?.message}`);
}
});
afterAll(async () => {
await appUninstall({ appPath: APP_PATH });
});
it('should find the installed app in the workspace', async () => {
const metadataClient = new MetadataApiClient();
const result = await metadataClient.query({
findManyApplications: {
id: true,
name: true,
universalIdentifier: true,
},
});
const installedApp = result.findManyApplications.find(
(app: { universalIdentifier: string }) =>
app.universalIdentifier === APPLICATION_UNIVERSAL_IDENTIFIER,
);
expect(installedApp).toBeDefined();
});
});
```
## تشغيل الاختبارات
تأكّد من تشغيل خادم Twenty المحلي لديك، ثم:
```bash filename="Terminal"
yarn test
```
أو في وضع المراقبة أثناء التطوير:
```bash filename="Terminal"
yarn test:watch
```
## التحقق من الأنواع
يمكنك أيضًا تشغيل التحقق من الأنواع على تطبيقك دون تشغيل الاختبارات:
```bash filename="Terminal"
yarn twenty dev:typecheck
```
يشغِّل هذا الأمر `tsc --noEmit` على ملف `tsconfig.json` الخاص بتطبيقك ويبلغ عن أي أخطاء في الأنواع. كما تتضمن التطبيقات المُنشأة بالهيكل برنامج نصي `yarn typecheck` يشمل ملفات الاختبار (`tsconfig.spec.json`) أيضًا.
## التكامل المستمر (CI) باستخدام GitHub Actions
تولّد أداة إنشاء الهيكل سير عمل جاهزًا للاستخدام في `.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` في أعلى سير العمل.
راجع قسم [النشر → التكامل/التسليم المستمران الآليان](/l/ar/developers/extend/apps/operations/publishing#automated-cicd-scaffolded-workflows) للاطلاع على شرح كامل لثلاثة مسارات العمل التي تم إنشاؤها تلقائياً (`ci.yml`، وخط أنابيب النشر `cd.yml`، و`publish.yml` للنشر على npm).

View File

@@ -0,0 +1,127 @@
---
title: الترجمات
description: قدّم تطبيقك بعدة لغات — ترجم تسميات ملف البيان وسلاسل واجهة المستخدم عبر كتالوج إعدادات محلية واحد.
icon: language
---
تُكتب تطبيقات Twenty باللغة **الإنجليزية**: السلاسل في شفرتك وملف البيان هي نص المصدر بالإنجليزية، و`en` هي الإعداد المحلي الذي تترجم منه، وأي إعداد محلي يُترك دون ترجمة يعود إليه افتراضيًا.
يحتوي تطبيقك على نوعين من النصوص القابلة للترجمة، وكلاهما يمر عبر نفس
كتالوج `locales/`:
* **تسميات ملف البيان** — أسماء الكائنات والحقول، وعناوين العروض، وعناصر القوائم، وسلاسل أخرى مُعلَنة في بيانات التعريف لتطبيقك.
* **سلاسل الواجهة الأمامية (front-component)** — نص واجهة المستخدم الذي تقوم مكوّنات React الأمامية لديك بعرضه.
تضع علامات على السلاسل القابلة للترجمة، وتستخرجها إلى كتالوجات لكل إعداد محلي، ثم تترجم هذه الكتالوجات، وتتولّى عملية البناء تقديم اللغة الصحيحة للمستخدم الحالي — دون أي إعداد إضافي.
## وضع علامات على سلاسل الواجهة الأمامية (front-component)
قم باستيراد أدوات الترجمة من `twenty-sdk/front-component`:
```tsx
import { Trans, t, msg, useTranslate } from 'twenty-sdk/front-component';
const STATUSES = [
{ id: 'draft', label: msg('Draft') },
{ id: 'sent', label: msg('Sent') },
];
const Card = ({ count, name }: { count: number; name: string }) => {
const { t } = useTranslate();
return (
<section>
{/* Static text — reactive to the user's locale */}
<Trans>Loading postcard…</Trans>
{/* Disambiguate identical sources with a context */}
<Trans context="card-title">Untitled</Trans>
{/* Interpolation: pass values explicitly */}
<p>{t('Hi {name}', { name })}</p>
<p>{t('Saved {count} cards', { count })}</p>
{/* Resolve a lazily-declared descriptor */}
<ul>{STATUSES.map((s) => <li key={s.id}>{t(s.label)}</li>)}</ul>
</section>
);
};
```
### متى تستخدم أيًّا منها
* **`Trans`** — نص ثابت في JSX. استخدم الخاصيتين `message` و`values` من أجل الاستبدال (interpolation)، كما في `Trans message="Hi {name}" values={{ name }}`؛ لا يمكن استخراج الاستبدال مباشرة في العناصر الفرعية (children) بشكل ثابت.
* **`useTranslate().t`** — سلاسل ديناميكية داخل المكوّن. يُعيد التصيير عندما يغيّر المستخدم اللغة. يفضّل استخدامه داخل التصيير (render).
* **`t(...)`** (مستوردة مباشرة) — ترجمة فورية (eager) يمكن استخدامها **في أي مكان**،
بما في ذلك معالجات الأحداث (event handlers)، والبرامج المساعدة (helpers)، ونطاق الوحدة (module scope) — وليس فقط داخل التصيير.
* **`msg(...)`** — واصف مؤجَّل للسلاسل المُعلَنة كبيانات (ثوابت، التهيئة (config)). قم بحلّه لاحقًا باستخدام `t(descriptor)`.
### السياق
مرِّر `context` لتمييز سلاسل المصدر المتطابقة التي تُترجَم بشكل مختلف:
```tsx
t({ message: 'Open', context: 'door' });
t({ message: 'Open', context: 'window' });
<Trans context="card-title">Untitled</Trans>
```
تظهر السلاسل السياقية في كتالوجات اللغة المحلية كمجموعة مفهرسة حسب سياقها:
`"\<context>": { "\<message>": "\<translation>" }`. تكون تسميات الـ Manifest سياقية بطبيعتها — إذ تحمل كل تسمية دورها في البيانات الوصفية (`objectMetadata.labelSingular`، `fieldMetadata.label`، `view.name`، …)، لذلك يمكن أن تُترجَم الكلمة نفسها بشكل مختلف عندما تكون اسم كائن وعندما تكون اسم عرض.
## الاستخراج والترجمة
شغِّل أمر الاستخراج من دليل تطبيقك:
```bash
twenty dev:translations-extract # collect strings into locales/en.json
twenty dev:translations-extract --locale fr-FR # also scaffold a target locale
```
يقوم الاستخراج بجمع كل من تسميات ملف البيان (manifest labels) وسلاسل `t()`/`msg()`/`Trans`
من شفرة الواجهة الأمامية (front-component) في `locales/\<locale>.json`، مفهرسة حسب سلسلة المصدر. املأ الترجمات:
```json
// locales/fr-FR.json
{
"Loading postcard…": "Chargement de la carte…",
"Hi {name}": "Bonjour {name}",
"Saved {count} cards": "{count} cartes enregistrées",
"door": {
"Open": "Ouvrir"
},
"objectMetadata.labelSingular": {
"Postcard": "Carte postale"
}
}
```
السلاسل غير المرتبطة بسياق هي مدخلات مسطَّحة؛ أما السلاسل المرتبطة بسياق — بما في ذلك كل تسمية في ملف manifest — فهي متداخلة تحت مفتاح السياق الخاص بها.
العناصر النائبة مثل `{name}` تُستبدَل وقت التشغيل — احتفظ بها في
الترجمة. أي سلسلة تُترَك فارغة تعود إلى نص المصدر.
## كيفية التشغيل
يقوم `twenty dev:build` بتجميع الكتالوجات وتقديم اللغة الصحيحة للمستخدم
الحالي: يتم حل تسميات ملف البيان (manifest labels) على جانب الخادم، ويتم تضمين كتالوجات الواجهة الأمامية (front-component)
داخل كل حزمة مكوّن. وقت التشغيل يقرأ المكوّن
اللغة (locale) من سياق التنفيذ الخاص به (لغة المضيف الحالية) ويحل كل سلسلة مقابل كتالوجه، مع الرجوع إلى المصدر عندما تكون الترجمة مفقودة. تغيير اللغة في المضيف يعيد تصيير سلاسل `Trans` و
`useTranslate().t` مباشرة.
تُصرَّف تسميات Manifest عند كل مزامنة — `twenty dev:build` و`twenty apply`
وكذلك المراقبة المستمرة `twenty dev` — لذا يكفي تعديل ملف لغة
والمزامنة لرؤية النص الجديد. تُضمَّن كتالوجات مكوّنات Front في
كل حزمة مكوّنات وقت البناء، لذا فإن تغيير أحدها لا يزال يعني
إعادة تشغيل `twenty dev:build` (وإعادة النشر).
قد تمتد عناصر النص التابعة لـ `Trans` على عدة أسطر — يتم ضغط المسافات
بنفس الطريقة التي يقوم JSX بضغطها، لذلك كلاهما يتم استخلاصه إلى المفتاح `Welcome back`:
```tsx
<Trans>Welcome back</Trans>
<Trans>
Welcome
back
</Trans>
```