الانتقال إلى المحتوى الرئيسي

نظرة عامة

يدور تطوير التطبيقات محليًا حول المزامنة: يقوم الـ CLI بإعادة إنشاء ملف manifest الخاص بك ويطبّق الخادم فقط الفرق بينه وبين البيانات الوصفية الموجودة بالفعل في مساحة العمل لديك. تغطي هذه الصفحة الأمر الذي ينبغي استخدامه، وكيفية قراءة ما غيّرته المزامنة، وما الذي يجب فعله — بالترتيب — عندما تبدو الحالة المحلية غير متسقة.

أي أمر، ومتى

للتكرار المحلي اليومي، ستحتاج دائمًا تقريبًا إلى twenty app dev. يُستخدم النشر والإصدار لإطلاق الإصدارات، وليس للحلقة المحلية.

لا تحتاج المزامنة المحلية إلى زيادة في الإصدار

تنطبق قاعدة version المتزايدة بدقة (VERSION_ALREADY_EXISTS عند النشر، وAPP_ALREADY_INSTALLED / CANNOT_DOWNGRADE_APPLICATION عند التثبيت) على twenty app publish / twenty app install — مسار الإصدار. يقوم twenty app dev بمزامنة ملف manifest في مكانه ولا يتطلب تغيير الإصدار أبدًا، لذا لا تحتاج إلى تعديل package.json للتكرار. إذا وجدت نفسك تزيد الإصدار لاختبار تغيير محلي، فأنت تستخدم مسار الإصدارات بينما ما تريده هو حلقة التطوير.

قراءة مخرجات المزامنة

كل عملية مزامنة تطبع تغييرات البيانات الوصفية التي تم تطبيقها (أو التي سيتم تطبيقها عند استخدام plan)، على غرار Terraform — كتلة واحدة لكل كيان مع خصائصه، ثم سطر ملخص:
هذه أداتك الأولى للتشخيص: تُخبرك بدقة ما الكائنات والحقول والتخطيطات التي تغيّرت، بحيث يمكنك التأكد من أن المزامنة أنجزت ما توقّعته قبل التحقّق من واجهة المستخدم. تُدرج التغييرات التدميرية (to destroy) مع ما تحذفه (مثلًا objectMetadata "auditNote" — drops the table and all its rows) وتتطلب تأكيدًا تفاعليًا، أو استخدام --yes في البرامج النصية. تحذف المزامنة كل كيان يملكه تطبيقك ولم يعد مصدرك يعرّف عنه. عندما تحتوي الخطة على عمليات حذف، فإنها توضح ذلك وتشير إلى --no-prune (المعروف سابقًا باسم --no-delete، والذي لا يزال يعمل)، مما يجعل المزامنة تراكمية: تُطبَّق عمليات الإنشاء والتحديث، ولا يُحذف أي شيء مفقود من مصدرك، ويظل مكوّن الإعدادات وارتباطات خطاف إلغاء تثبيت التطبيق كما هي عندما لا يعلن مصدرك عنها. استخدمه عندما يكون مصدرك جزئيًا عن قصد، مثلًا أثناء اعتماد تطبيق تم إنشاؤه في واجهة المستخدم. يُعد التطبيق المخصص لمساحة العمل استثناءً: نظرًا لأن السحب لا يمكنه تصدير كل ما يملكه بعد، فإن مزامنته تكون تراكمية ما لم تمرر --prune. تظل الخوادم التي صدرت قبل هذا التغيير تحذف افتراضيًا، لذا مرر --no-prune عند تطبيق التطبيق المخصص على أحدها. عندما تفشل المزامنة على كيان واحد، يذكر الخطأ اسم الكيان المسبب للمشكلة وuniversalIdentifier الخاص به، على سبيل المثال:
استخدم ذلك المعرّف للعثور على الكيان في ملف manifest الخاص بك (وإن لزم الأمر، في مساحة العمل) بدلًا من تخمين أيّها يتعارض.

معاينة التغييرات (plan)

يقوم twenty app plan ببناء ملف manifest الخاص بك، ويطلب من الخادم خطة الترحيل، ويطبعها — دون تطبيق أي شيء. إنها الطريقة الآمنة للإجابة عن سؤال “ما الذي ستغيّره هذه المزامنة؟” قبل الالتزام بها.
خطة:
  • لا يغيّر مساحة العمل، ولا يُجري ترحيلًا للبيانات الوصفية أو تحديثًا لسجل التطبيق أو تغييرات على الدور/علامة التبويب الافتراضية. ينشئ لقطة محلية، لكنه لا يعيد إنشاء عميل API.
  • يُرجع نفس الفرق الذي ستُطبِّقه مزامنة حقيقية، حتى تتمكن من مراجعة الكيانات التي سيتم إنشاؤها/تحديثها/حذفها مسبقًا.
  • يكون مفيدًا قبل إجراء تغيير محفوف بالمخاطر، أو عند مراجعة تغيير تم إنشاؤه بواسطة الذكاء الاصطناعي، أو في سكربت يجب أن يفشل إذا كان تغيير غير متوقَّع على وشك الحدوث.
تعرض الخطة مسبقًا تغييرات البيانات الوصفية فقط. يعمل أيضًا مع تطبيق لم تتم مزامنته من قبل: يقيّم الخادم ملف manifest مقارنةً بتطبيق فارغ، لذا تسرد الخطة كل ما سينشئه مصدرك.

سحب تطبيق مثبّت إلى المصدر

twenty app pull تجريبي. لا يغطي اليوم سوى جزء من التطبيق وقد يتغير شكل مخرجاته بين الإصدارات، ويستبدل الملفات التي تعرّف الكيانات التي يسحبها. يستعيد الترجمات المنشورة إلى locales/، مع الاحتفاظ بالإدخالات التي لا توجد سلاسلها المصدرية في شجرتك بعد في locales/compiled/ حتى لا تفقدها عملية الدفع. نفّذ عملية commit لعملك قبل تشغيله، وراجِع الفرق.
twenty app pull هو عكس apply: يقرأ تطبيقًا من مساحة العمل ويستعيد ملفات التعريف والترجمات وملفات التبعيات المخزنة الخاصة به.
داخل تطبيق، يقرأ pull معرّف defineApplication() المحلي ويسترجع ذلك التطبيق. خارج تطبيق، يطلب دليلاً وتأكيداً، وينشئ مشروعاً محلياً للتطبيق المخصص لمساحة العمل المحددة، ويثبّت التبعيات باستخدام Yarn، ثم يسحب التعريفات. يتضمن المشروع الجديد ملفات الحزمة والتهيئة، من دون كيانات العرض التوضيحي الخاصة بالمُهيِّئ. استخدم --path لتحديد تطبيق موجود أو دليل جديد أو فارغ. يتطلب الإنشاء غير التفاعلي --path و--yes؛ ويرفض pull الكتابة فوق دليل غير فارغ وغير ذي صلة. لا يؤدي إنشاء مشروع إلى تسجيل تطبيق أو تغيير مساحة العمل. يؤدي مشروع SDK موجود دون تعريف تطبيق أيضًا إلى سحب التطبيق المخصص. لا يوجد خيار --universal-identifier. إذا فشل تثبيت التبعيات لمشروع جديد، فسيُحتفَظ بالهيكل المُنشأ. شغّل yarn install داخله، ثم أعد محاولة السحب. لا تثبّت المشاريع الموجودة التبعيات تلقائيًا. إذا تعذرت قراءة معرّف التطبيق الخاص بها، فسيُبلّغ السحب عن ذلك الخطأ بدلًا من إنشاء تطبيق آخر. أضف --verbose لتوسيع أقسام التغطية في التقرير، والتي تسمي عندئذ ما يصل إلى 20 معرّفًا لكل نوع من البيانات الوصفية بدلًا من الإجماليات فقط. ما الذي يكتبه:
  • تكوين التطبيق؛
  • ملفات التبعيات المخزنة package.json وyarn.lock، في جذر المشروع؛
  • الكائنات، مع حقولها مضمنة؛
  • الحقول التي أضافها تطبيقك إلى كائنات لا يملكها؛
  • الفهارس المنشأة؛
  • علامات الأذونات التي يعرّفها تطبيقك، كملفات definePermissionFlag()؛
  • الأدوار، بأذونات الكائنات وأذونات الحقول ومسندات أذونات مستوى الصف والمجموعات الخاصة بها، وعلامات الأذونات مضمنة. يُكتب الدور الافتراضي لتطبيقك باستخدام defineApplicationRole(). يُكتب إذن كائن أو حقل يكون معرّفه هو المعرّف الذي تستنتجه عملية البناء من دونه. عندما لا يكون الدور الافتراضي أحد أدوار تطبيقك الخاصة، كما في تطبيق Custom لمساحة العمل، يحتفظ تكوين التطبيق بـ defaultRoleUniversalIdentifier بدلًا من ذلك؛
  • طرق العرض، مع حقولها ومجموعات الحقول والمرشحات ومجموعات المرشحات وعمليات الفرز والتجميعات المضمنة؛
  • حقول طرق العرض التي أضافها تطبيقك إلى طرق عرض لا يملكها، كملفات defineViewField() مستقلة؛
  • تخطيطات الصفحات، مع تضمين علامات التبويب والأدوات الخاصة بها؛
  • علامات التبويب التي أضافها تطبيقك إلى تخطيطات الصفحات التي لا يملكها، كملفات definePageLayoutTab() مستقلة؛
  • عناصر قائمة التنقل، كملفات defineNavigationMenuItem()، والمجلدات المضمّنة؛
  • الأدوات التي أضافها تطبيقك إلى علامات تبويب لا يملكها، كملفات definePageLayoutWidget() مستقلة؛
  • عمليات سير العمل، كملفات defineWorkflow()، مع مصدر كل خطوة Code في ملف \<workflow-file-name>/\<step-name>.code.ts خاص بها بجانب سير العمل، عندما تكون نسختها المخزنة هي التي كتبتها آخر مزامنة. بالنسبة إلى التطبيق المخصص لمساحة العمل، فهي النسخة النشطة، سواء نشرتها مزامنة أو منشئ سير العمل، ويقوم التطبيق بنشر تعريف تم تغييره كنسخة جديدة، كما هو موضح في Workflows؛
  • المهارات، كملفات defineSkill()؛
  • الوكلاء، كملفات defineAgent()، مع مشغلاتهم والدور الذي يربطهم به تطبيقك؛
  • قواعد التحقق، كملفات defineValidationRule() مع تعبيرها كسلسلة؛
  • الترجمات المنشورة، بصيغة locales/\<locale>.json لكي يتمكن strings pull من قراءتها مجددًا من البيانات الوصفية والمصدر المسحوبين، وبصيغة locales/compiled/\<locale>.json للبقية.
يذهب كل كيان إلى الملف الذي يعرّفه بالفعل، لذا لا يعيد السحب الثاني كتابة سوى ما تغيّر على الخادم. يُعاد أيضًا كتابة ملف عندما يستخدم دالة تعريف مختلفة عما ستكتبه عملية السحب، على سبيل المثال، دور أصبح الدور الافتراضي أو لم يعد كذلك. توضع الكيانات الجديدة بجانب الملفات الموجودة من نوعها، ويكون الاسم المُولَّد الذي قد يقع على ملف غير ذي صلة مؤهَّلًا بدلًا من استبداله. ملفات التبعيات. عند تغيير package.json أو yarn.lock المخزَّن، يستبدل السحب الملف المحلي ويبلّغ عن أي تعديلات محلية تمت الكتابة فوقها. إذا لم يتغير الملف البعيد منذ السحب السابق، تُحتفَظ بالتعديلات المحلية. لا يؤدي غياب ملف بعيد إلى حذف نظيره المحلي. عندما لا تتضمن الحزمة المُصدَّرة twenty-sdk، كما يفعل تطبيق Custom لمساحة العمل، يحتفظ السحب بتبعية SDK المحلية لكي تظل أوامر CLI قادرة على تحميل المشروع. تُكتب ملفات التبعيات والبيانات الوصفية والخط الأساسي معًا، ويُتراجع عنها معًا إذا فشلت عملية كتابة. شغّل yarn install بعد سحب تغييرات التبعيات. يقوم Pull بتثبيت التبعيات فقط عند إنشاء مشروع جديد، قبل استعادة الملفات البعيدة؛ ولا يعيد تثبيتها بعد استبدال package.json أو yarn.lock. لا يعيد Pull إنشاء العميل محدد الأنواع. شغّل twenty app generate-client عندما تحتاج إلى أنواع محدّثة لمساحة العمل. ما الذي لا يكتبه:
  • لم تُصدَّر بعد — عناصر قائمة الأوامر وعناصر قائمة الإعدادات، ودوال المنطق والمكونات الأمامية، وموفرو الاتصال، ومتغيرات التطبيق، والأصول العامة، وأنواع نشاط المخطط الزمني، والمصدر المخزَّن لدوالك.
  • مُشتقة بواسطة المحرك — حقول النظام، والعلاقات الافتراضية، والعروض الافتراضية والفهارس الداعمة، وتخطيطات الصفحات الافتراضية لكل كائن. يعيد الخادم إنشاءها عند التطبيق. قد يظل الكائن يشير بمعرّف التسمية الخاص به إلى أحدها، كما تفعل كائنات الوصل التي تُنشأ في واجهة المستخدم؛ يُكتب المؤشر ويحلّه البناء دون إضافة حقل.
  • موضوعة في طريقة عرض افتراضية أو خارجية — لا تحتوي عوامل التصفية ومجموعات عوامل التصفية وعمليات الفرز والمجموعات ومجموعات الحقول المضافة إلى طريقة عرض افتراضية للمحرك، أو إلى طريقة عرض يملكها تطبيق آخر، على نموذج مصدر بعد، ويُبلَّغ عنها. تُكتب حقول طريقة العرض المضافة هناك كملفات مستقلة، لأن معرّف طريقة العرض معروف.
  • موضوعة في تخطيط صفحة افتراضي أو أجنبي — تُكتب علامات التبويب والأدوات المضافة إلى تخطيط صفحة افتراضي للمحرك، أو إلى تخطيط يملكه تطبيق آخر، كملفات definePageLayoutTab() وdefinePageLayoutWidget() مستقلة، لأن المعرّف الذي ترتبط به معروف.
  • أدوات لا يمكن للتصدير حملها بعد — في أي تخطيط، بما في ذلك تخطيطك الخاص، يتم الإبلاغ عن أداة من النوع المهمل VIEW، أو أداة تعرض أحد المكوّنات الأمامية الخاصة بتطبيقك أو عناصر قائمة الأوامر، أو أداة بلا موضع لوضع تخطيط علامة التبويب الخاصة بها، ويُكتب التخطيط أو علامة التبويب بدونها.
  • عمليات سير العمل التي لا يمكن للتصدير نقلها بعد — يتم الإبلاغ عن سير العمل الذي تغيّر في منشئ سير العمل منذ آخر مزامنة، أو سير العمل النظامي أو الخاص، أو الذي يحتوي على خطوة Code أُنشئت قبل أن تخزن الخطوات مصدرها، أو الذي يستدعي إجراء سير العمل لتطبيق آخر، ويُترك في مكانه. يحتفظ سير العمل المتروك في مكانه بملفات .code.ts الخاصة به. يُعد التطبيق المخصص لمساحة العمل الاستثناء للحالتين الأولى والأخيرة: إذ ينشر المنشئ سير العمل الخاص به أيضًا، وقد يستدعي إجراءات أي تطبيق، لذا يتم جلبه. يتم الإبلاغ عن أحد مهام سير العمل الخاصة به بدلًا من ذلك عندما لا تكون له نسخة نشطة، أو عندما تستخدم نسخته النشطة إعدادات لا يمكن للتعريف التعبير عنها. تُستخرج خطوات البريد الإلكتروني والتقويم: يُكتب الحساب المشترك مع مساحة العمل بوصفه connectedAccountHandle الخاص به، بينما يُحتفظ بالحساب الخاص بالعضو والمتغيرات كما هي. يُبلّغ عن سير العمل بدلاً من ذلك عندما يكون حساب إحدى الخطوات مؤرشفًا، أو لم يعد بإمكانه تنفيذ الخطوة، أو يشارك عنوانه مع حساب أقدم، لأن تطبيق المعرّف سيختار حسابًا آخر.
  • الوكلاء الذين لا يمكن للتصدير نقلهم بعد — يُبلّغ عن الوكيل الذي له أكثر من دور واحد، أو المرتبط بدور لا تكتب إليه عملية السحب، ويُترك في مكانه.
  • قواعد التحقق التي لا يمكن للتصدير نقلها بعد — يتم الإبلاغ عن قاعدة بلا تعبير أو رسالة، مثل مسودة غير مكتملة من الإعدادات، أو قاعدة تقرأ حقلاً محذوفًا من مساحة العمل، وتُترك في مكانها.
  • حالة وقت التشغيل لمساحة العمل، ومرفقات السجلات، ومرفقات البريد الإلكتروني، وتعيينات أدوار الأعضاء ومفاتيح API، وخطافات الويب، والتنقل لكل مستخدم، وعناصر التنقل المثبتة في سجل، كلها مستبعدة حسب التصميم، بما في ذلك لتطبيق Custom لمساحة العمل.
  • مملوك لتطبيق آخر — يُبلَّغ عنه، ولا يُسحب مطلقًا.
يسرد التقرير في نهاية عملية السحب كل ذلك، مجمّعًا حسب النوع والسبب. كل ما يشير إليه على أنه غير مكتوب لا يزال موجودًا في مساحة عملك، لذا أعد دفع شجرة مسحوبة باستخدام twenty app apply --no-prune حتى تُغطى الأنواع المفقودة. يُطبَّق التطبيق المخصص لمساحة العمل بهذه الطريقة افتراضيًا بالفعل. سيحذف تطبيق مع التقليم (الإعداد الافتراضي للتطبيقات الأخرى، و--prune للتطبيق المخصص) تلك العناصر، ويسرد twenty app plan بالضبط العناصر التي سيحذفها. الحالة المحلية لمساحة العمل. يُكتب الكيان المعطّل في مساحة العمل كتعريف نشط، ولا تُكتب التجاوزات التي أجرتها مساحة العمل على كيان، مثل تسمية معاد تسميتها. يسرد التقرير تلك الصفوف ضمن “مكتوبة بدون حالتها المحلية لمساحة العمل”. إن تطبيق الشجرة مرة أخرى على مساحة العمل نفسها يترك تلك الحالة دون تغيير؛ وتحصل مساحة عمل أخرى على الكيان نشطًا وبدون التجاوزات. الملف الأساسي. يسجل السحب ما سحبه في .twenty/cli/pull-base.json، وهو متجاهل بواسطة git ولكل نسخة سحب. هذا الملف هو ما تقارن به عملية سحب لاحقة لترك الملفات التي لم يتغير كيانها دون مساس، ولحذف ملف كيان لم يعد موجودًا في مساحة العمل. إن checkout من دونه يكتب كل شيء ببساطة ولا يحذف شيئًا.

سلّم الاستعادة

عندما تبدو البيانات الوصفية المحلية غير صحيحة، صعِّد الإجراءات بهذا الترتيب وتوقّف بمجرد زوال العائق. كل خطوة أكثر إرباكًا من التي قبلها.
  1. أعد المزامنة. شغّل twenty app apply مرة أخرى. عمليات المزامنة متطابِقة الأثر (idempotent) — إعادة تشغيل ملف manifest النظيف آمنة وغالبًا ما تحل تعثرًا عابرًا.
  2. عاين الخطة. شغّل twenty app plan لمعرفة ما الذي تنوي المزامنة التالية تغييره بالضبط، دون تطبيقه.
  3. اقرأ الخطأ المسمّى. إذا فشلت المزامنة، لاحظ نوع البيانات الوصفية وuniversalIdentifier في الرسالة (انظر أعلاه) وحدّد ذلك الكيان في ملف manifest الخاص بك. يشير التعارض عادةً إلى معرّف مكرر أو مُعاد استخدامه.
  4. ألغِ التثبيت وأعِد التثبيت. شغّل twenty app uninstall، ثم أجرِ المزامنة مرة أخرى (twenty app dev). هذا يعيد بناء بيانات التطبيق الوصفية من نقطة بداية نظيفة مع إبقاء باقي مساحة العمل سليمة.
  5. إعادة ضبط كاملة (كحل أخير). لبيئة محلية تُدار عبر CLI، شغّل twenty local reset، ثم twenty local start وأعِد الاتصال قبل المزامنة مجددًا. بالنسبة إلى نسخة مصدر تم سحبها، أعِد ضبط قاعدة بيانات التطوير الخاصة بها وأعِد ملأها بالبيانات الأولية بدلًا من ذلك.
تؤدي إعادة ضبط بيئتك المحلية أو قاعدة بيانات التطوير للمستودع إلى حذف كل البيانات في النسخة المحلية لديك، وكل مساحة عمل، وكل سجل، وكل تطبيق. استخدمه فقط بعد فشل الخطوات السابقة.
هل واجهت خطأ في البيانات الوصفية؟ يرجى فتح مشكلة وتضمين رسالة الترحيل الفاشلة (مع نوع بياناتها الوصفية وuniversalIdentifier)، ومخرجات Metadata changes من المزامنة، والأوامر التي شغّلتها.

تجنّب إجراء مزامنات متزامنة على مساحة عمل واحدة

تطبّق المزامنة عمليات ترحيل للبيانات الوصفية. قد يؤدّي تشغيل عدة عمليات مزامنة أو نشر أو تثبيت ضد نفس مساحة العمل في الوقت نفسه — على سبيل المثال، عدّة نوافذ طرفية أو وكلاء ذكاء اصطناعي يتكرّرون بالتوازي — إلى تداخل عمليات الترحيل تلك وترك البيانات الوصفية في حالة مطبَّقة جزئيًا. يقوم الخادم بتسلسل عمليات المزامنة لكل مساحة عمل لمنع ذلك، لكن ما زال ينبغي عليك تمرير عمليات البيانات الوصفية الحساسة عبر عملية واحدة بدلًا من تنفيذها بالتوازي. إذا كنت تنظّم التطوير باستخدام عدة وكلاء، فمرّر استدعاءات المزامنة/النشر/التثبيت عبر طابور واحد حتى تعمل واحدة فقط في الوقت نفسه.

تمييز أنواع الإخفاقات

عندما يحدث خلل ما، يتيح لك فرق البيانات الوصفية والأخطاء المسمّاة تحديد موضع الفشل:
  • خطأ في إنشاء ملف manifest — يفشل الـ CLI قبل إجراء المزامنة (MANIFEST_BUILD_FAILED، TYPECHECK_FAILED)؛ أصلِح كود التطبيق لديك.
  • خطأ في ملكية التسجيل — تمت رفض المزامنة لأن universalIdentifier الخاص بالتطبيق يعود إلى مساحة عمل أخرى، أو لا يعود إلى أي مساحة عمل على الإطلاق؛ راجع ملكية التسجيل.
  • خطأ في المزامنة / الترحيل — تنجح عملية الإنشاء لكن يفشل تطبيق الفرق، مع تسمية الكيان وuniversalIdentifier؛ أصلِح البيانات الوصفية المتعارِضة.
  • خطأ في حجم التبعيات — تفشل عملية المزامنة أو التثبيت لأن dependencies الخاصة ببيئة الإنتاج في التطبيق كبيرة جدًا بحيث لا يمكن تثبيتها كطبقة وقت التشغيل (LOGIC_FUNCTION_DEPENDENCIES_SIZE_EXCEEDED)؛ انقل الحزم التي لا تستوردها دوال المنطق لديك في وقت التشغيل (مكتبات واجهة المستخدم وأدوات التطوير) إلى devDependencies.
  • ترحيل مؤجل قيد التنفيذ — تُرفض المزامنة لأن ترحيلًا في الخلفية لنموذج البيانات من مزامنة سابقة لا يزال قيد التشغيل (DEFERRED_WORKSPACE_MIGRATION_ACTIONS_IN_PROGRESS)؛ انتظر حتى يكتمل، ثم أجرِ المزامنة مرة أخرى.
  • خطأ في وقت تشغيل كود التطبيق — تتم المزامنة بنجاح، ولكن دوال المنطق أو المكوّنات لديك لا تعمل بشكل صحيح أثناء وقت التشغيل؛ تحقّق من سجلات الدوال.
  • حالة المثيل المحلي — لا ينطبق أيّ مما سبق وما زالت مساحة العمل تبدو غير صحيحة؛ تابع النزول في سلّم الاستعادة.