> ## Documentation Index
> Fetch the complete documentation index at: https://docs.twenty.com/llms.txt
> Use this file to discover all available pages before exploring further.

# خطافات التثبيت

> تشغيل المنطق أثناء دورة حياة التثبيت أو الترقية أو إلغاء التثبيت — مثل تعبئة البيانات الأوّلية، ونسخ السجلات احتياطيًا، والتحقق من الترقية، وتنظيف الموارد الخارجية.

خطافات التثبيت هي دوال منطقية خاصة تعمل أثناء دورة حياة التثبيت أو الترقية أو إلغاء التثبيت. تستخدم نفس وقت تشغيل المعالج مثل [دوال المنطق](/l/ar/developers/extend/apps/logic/logic-functions) العادية، ولكن يتم التصريح عنها بدوال تعريف خاصة بها وتعمل خارج نموذج المشغّل المعتاد (HTTP، وcron، وأحداث قاعدة البيانات). تتلقى خطافات التثبيت `InstallPayload` (`{ previousVersion?: string; newVersion: string }` — تكون قيمة `previousVersion` هي `undefined` في حالة التثبيت الجديد)، بينما يتلقى خطاف إلغاء التثبيت `UninstallPayload` (`{ version?: string }` — الإصدار الذي تتم إزالته).

يمكن لكل تطبيق تعريف **خطاف واحد كحد أقصى** من كل نوع (قبل التثبيت، بعد التثبيت، إلغاء التثبيت). سيُنتِج إنشاء ملف البيان خطأً إذا تم اكتشاف أكثر من خطاف واحد من أي نوع.

```
┌─────────────────────────────────────────────────────────────┐
│ install flow                                                │
│                                                             │
│   upload package → [pre-install] → metadata migration →     │
│   generate SDK → [post-install]                             │
│                                                             │
│                  old schema visible    new schema visible   │
└─────────────────────────────────────────────────────────────┘
```

## لمحة سريعة

|                    | `definePreInstallLogicFunction`                                                            | `definePostInstallLogicFunction`                                                                                              |
| ------------------ | ------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------- |
| عمليات التشغيل     | قبل ترحيل البيانات الوصفية — لا يزال المخطط والبيانات **السابقة** سليمين                   | بعد الترحيل وإنشاء الـ SDK — أصبح المخطط **الجديد** في مكانه                                                                  |
| التنفيذ            | دائمًا متزامن؛ يحجب عملية التثبيت                                                          | غير متزامن بشكل افتراضي (يُوضَع في قائمة الانتظار، 3 محاولات إعادة)؛ تفعيل التزامن اختياري عبر `shouldRunSynchronously: true` |
| عند الفشل          | يتم **إحباط** التثبيت قبل أي تغيير في المخطط                                               | غير متزامن: تُعاد المحاولة حتى 3 مرات. متزامن: يتلقى المستدعي `POST_INSTALL_ERROR` (لن يتم التراجع عن تغييرات المخطط).        |
| الاستخدام النموذجي | انسخ البيانات احتياطيًا أو أصلح بيانات قد يفقدها الترحيل؛ ارفض ترقية خطِرة بإلقاء استثناء. | بذر بيانات افتراضية، تهيئة مساحة العمل، تسجيل موارد خارجية                                                                    |

**قاعدة عامة:** اجعل الافتراضي هو post-install. الجأ إلى ما قبل التثبيت فقط عندما يكون الترحيل نفسه هدّامًا وتحتاج إلى التقاط الحالة السابقة قبل أن تزول.

| ترغب في...                                                       | استخدام                                                                   |
| ---------------------------------------------------------------- | ------------------------------------------------------------------------- |
| بذر البيانات، تهيئة مساحة العمل، تسجيل موارد خارجية              | `post-install`                                                            |
| عمل طويل الأمد لا ينبغي أن يحجب استجابة التثبيت                  | `post-install` (الوضع غير المتزامن الافتراضي، مع محاولات إعادة من العامل) |
| إعداد سريع يعتمد عليه المستدعي مباشرةً بعد عودة التثبيت          | `post-install` مع `shouldRunSynchronously: true`                          |
| قراءة البيانات أو نسخها احتياطيًا والتي قد يفقدها الترحيل القادم | `pre-install`                                                             |
| رفض ترقية قد تُفسد البيانات الحالية                              | `pre-install` (ارمِ من المعالج)                                           |
| تنفيذ مواءمة في كل ترقية                                         | أي من الخطافين مع `shouldRunOnVersionUpgrade: true`                       |

## السلوك المشترك بين كلا الخطافين

* إعداد التهيئة هو إعداد `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" theme={null}
yarn twenty dev:function:exec --postInstall
yarn twenty dev:function:exec --preInstall
```

<AccordionGroup>
  <Accordion title="definePostInstallLogicFunction" description="تعمل بعد تطبيق ترحيل البيانات الوصفية لمساحة العمل">
    يعمل بعد انتهاء تثبيت تطبيقك: تمت مزامنة البيانات الوصفية، وتم إنشاء عميل SDK، وأصبح من الممكن الاستعلام عن المخطط الجديد. مثال — بذر سجل افتراضي في عمليات التثبيت الجديدة:

    ```ts src/logic-functions/post-install.ts theme={null}
    import { definePostInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define';
    import { CoreApiClient } from 'twenty-client-sdk/core';

    const handler = async ({ previousVersion }: InstallPayload): Promise<void> => {
      if (previousVersion) return; // fresh installs only

      const client = new CoreApiClient();
      await client.mutation({
        createPostCard: {
          __args: { data: { name: 'Welcome to Postcard', content: 'Your first card!' } },
          id: true,
        },
      });
    };

    export default definePostInstallLogicFunction({
      universalIdentifier: 'f7a2b9c1-3d4e-5678-abcd-ef9876543210',
      name: 'post-install',
      description: 'Seeds a welcome post card after install.',
      timeoutSeconds: 300,
      shouldRunOnVersionUpgrade: false,
      shouldRunSynchronously: false,
      handler,
    });
    ```

    تتحكم الشارة `shouldRunSynchronously` في نموذج التنفيذ:

    * `false` *(الإعداد الافتراضي)* — يُوضَع في قائمة انتظار الرسائل (`retryLimit: 3`) ويُشغِّله عامل. تعود استجابة التثبيت بمجرد وضع المهمة في قائمة الانتظار. **يُستخدم للأعمال طويلة الأمد** — بذر مجموعات بيانات كبيرة، وواجهات برمجة تطبيقات بطيئة لأطراف ثالثة.
    * `true` — يُنفَّذ مضمَّنًا أثناء تدفق التثبيت. يحجب طلب التثبيت حتى ينتهي المعالج؛ يظهر الخطأ الذي يتم رميه كـ `POST_INSTALL_ERROR` للمستدعي (بدون محاولات إعادة). **يُستخدم للأعمال السريعة التي يجب إتمامها قبل الاستجابة.** تم تطبيق الترحيل بالفعل في هذه المرحلة، لذا لا يؤدي الفشل إلى التراجع عن تغييرات المخطط — بل يُظهِر الخطأ فقط.
  </Accordion>

  <Accordion title="definePreInstallLogicFunction" description="تعمل قبل تطبيق ترحيل البيانات الوصفية لمساحة العمل">
    يعمل قبل ترحيل البيانات الوصفية، مقابل المخطط **السابق** — المكان المناسب لنسخ البيانات احتياطيًا التي قد يفقدها الترحيل، أو لرفض ترقية خطِرة. قبل التنفيذ، يُشغِّل الخادم مزامنة ذات طابع إضافي فقط "pared-down sync" تُسجِّل دالة ما قبل التثبيت للإصدار الجديد فقط؛ أما كل ما عدا ذلك — كائنات الإصدار السابق وحقوله وبياناته — فيبقى دون مساس عندما يعمل المعالج.

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

    مثال — نسخ قيم حقل قديم قبل أن يُسقِطه الترحيل:

    ```ts src/logic-functions/pre-install.ts theme={null}
    import { definePreInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define';
    import { CoreApiClient } from 'twenty-client-sdk/core';

    const handler = async ({ previousVersion, newVersion }: InstallPayload): Promise<void> => {
      // Only the 1.x → 2.x upgrade drops the legacy `notes` field.
      if (!previousVersion?.startsWith('1.') || !newVersion.startsWith('2.')) {
        return;
      }

      const client = new CoreApiClient();
      const { postCards } = await client.query({
        postCards: {
          __args: { filter: { notes: { isNot: null } } },
          edges: { node: { id: true, notes: true } },
        },
      });

      // 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({
      universalIdentifier: 'a1b2c3d4-5678-90ab-cdef-1234567890ab',
      name: 'pre-install',
      description: 'Backs up legacy notes into description before the v2 migration.',
      timeoutSeconds: 300,
      shouldRunOnVersionUpgrade: true,
      handler,
    });
    ```
  </Accordion>
</AccordionGroup>

## خطاف إلغاء التثبيت

تُصرّح `defineUninstallLogicFunction` عن خطاف يتم تشغيله عندما يلغي المستخدم تثبيت تطبيقك. يتم تنفيذه **قبل** إزالة بيانات التعريف للتطبيق وبياناته وكوده — بمجرد تشغيل ترحيل الحذف، لن يتبقى أي شيء للتنفيذ — لذلك لا يزال بإمكان معالجك الاستعلام عن كائنات التطبيق وسجلاته. استخدمه لتنظيف الموارد الخارجية: إلغاء توفير موارد واجهة برمجة التطبيقات (API)، وحذف الروبوتات المتبقية، وإبطال خطافات الويب.

الملاحظات:

* الخطاف يعتمد على مبدأ "أقصى جهد ممكن": يتم تشغيله بشكل متزامن، ولكن في حالة الفشل يتم تسجيل الخطأ و**لا يعرقل عملية إلغاء التثبيت مطلقًا** — يجب ألا يجعل التنظيف إزالة التطبيق مستحيلة.
* يتلقى `UninstallPayload` (`{ version?: string }` — الإصدار الذي تتم إزالته).
* لا يتم تشغيله عند التراجع عن عملية تثبيت جديدة فاشلة — حيث لم يكتمل تثبيت التطبيق مطلقًا.
* لا يمكن للخطاف أن يعمل بعد إزالة التطبيق، لذلك يجب أن يتم هنا أي تنظيف خارجي يعتمد على بيانات التطبيق (مثلًا: معرّفات الروبوتات المخزنة في السجلات)، وليس في مهمة مجدولة خارجية.
* ومثل خطافات التثبيت، **لا يتم تنفيذه في وضع التطوير (dev mode)** — بدلًا من ذلك، قم بتشغيله يدويًا:

```bash filename="Terminal" theme={null}
yarn twenty dev:function:exec --uninstall
```

```ts src/logic-functions/uninstall.ts theme={null}
import { defineUninstallLogicFunction, type UninstallPayload } from 'twenty-sdk/define';
import { CoreApiClient } from 'twenty-client-sdk/core';

const handler = async (_payload: UninstallPayload): Promise<void> => {
  const client = new CoreApiClient();
  const { meetingBots } = await client.query({
    meetingBots: { edges: { node: { id: true, externalBotId: true } } },
  });

  // Delete the provider-side bots so nothing keeps recording after uninstall.
  for (const { node } of meetingBots.edges) {
    await fetch(`https://api.recorder.example/bots/${node.externalBotId}`, {
      method: 'DELETE',
      headers: { Authorization: `Bearer ${process.env.RECORDER_API_KEY}` },
    });
  }
};

export default defineUninstallLogicFunction({
  universalIdentifier: 'b2c3d4e5-6789-01bc-def0-234567890abc',
  name: 'uninstall',
  description: 'Deletes remaining recorder bots when the app is uninstalled.',
  timeoutSeconds: 300,
  handler,
});
```
