DeepSeek Harness: دورة حياة الإضافة: آلة حالة Fiber
آخر تحديث: 2026-08-31
كل إضافة في Cordis ليست كتلة كود ثابتة، بل كيان حي بدورة حياة — Fiber. فهم آلة حالة Fiber يعني فهم الرحلة الكاملة من "انتظار التحميل" إلى "التشغيل" إلى "الخروج بأمان"، وهو مفتاح كتابة إضافات متينة.
📋 المتطلبات المسبقة: أكمل 13-effect.md، تفهم التنظيف التلقائي و ctx.effect()
1. ما ستتعلمه
- مفهوم Fiber: حاوية حالة الإضافة
- دورة الحياة: pending → active → disposing → disposed
- إنشاء وتدمير Fiber
- علاقات Fiber الأب-الابن
- معالجة الأخطاء والتراجع عن الحالة
- عزل سياق Fiber
2. مفهوم Fiber
(1) ما هو Fiber
Fiber هو تجريد Cordis لحالة تشغيل الإضافة — كل نسخة إضافة تقابل كائن Fiber يُسجّل في أي مرحلة من دورة الحياة تكون الإضافة حالياً.
interface Fiber {
id: string
name: string
state: FiberState
context: Context
parent: Fiber | null
children: Fiber[]
}
(2) لماذا نحتاج Fiber
بدون Fiber، الإضافات لها حالتان فقط: "مُحمّلة" و "غير مُحمّلة." لكن عملياً:
- عندما لا تكون الاعتماديات جاهزة، يجب أن "تنتظر" الإضافة بدلاً من "تفشل"
- أثناء إلغاء التحميل، تحتاج الإضافة أن "تُنظّف" بدلاً من أن تختفي فقط
- عند الخطأ، قد تحتاج الإضافة "إعادة المحاولة" بدلاً من "تموت"
Fiber يوفر نموذج آلة حالة واضح لهذه الحالات الوسيطة.
(3) ▶ مثال 3
Fiber = "روح" الإضافة (معلومات الحالة)
Context = "جسد" الإضافة (الموارد والبيئة)
كل Fiber يحمل Context؛ دورة حياة Context مرتبطة بـ Fiber.
3. حالات دورة الحياة
(1) ▶ مثال 1
stateDiagram-v2
[*] --> pending: Plugin registered
pending --> active: Dependencies ready + apply succeeds
pending --> errored: apply fails
active --> disposing: Unload requested
errored --> active: Retry succeeds
errored --> disposing: Retry abandoned
disposing --> disposed: Cleanup complete
(2) معاني الحالات
| الحالة | المعنى | ctx متاح | قابل للعكس |
|---|---|---|---|
| pending | انتظار الاعتماديات | محدود | ✅ |
| active | يعمل بشكل طبيعي | بالكامل | ✅ |
| disposing | يجري التنظيف | للقراءة فقط | ❌ |
| disposed | مُدمّر | غير متاح | ❌ |
| errored | فشل البدء | غير متاح | ✅ |
(3) حالة pending
عندما تصرّح إضافة بـ inject لكن الاعتماديات لم تُسجّل بعد، يدخل Fiber حالة pending:
export const inject = ['tools']
// خدمة tools لم تُسجّل بعد → Fiber في حالة pending
// tools سُجّلت → Fiber ينتقل لـ active، يستدعي apply
أثناء pending:
- apply الإضافة لم يُستدعى بعد
- خدمات الاعتماديات على ctx غير متاحة
- ينتقل تلقائياً لـ active بمجرد جاهزية الاعتماديات
(4) حالة active
بعد تنفيذ apply بنجاح، يدخل Fiber حالة active:
export function apply(ctx: Context) {
// Fiber الآن active
ctx.logger.info('I am alive!')
ctx.on('session/created', (s) => {
// يمكن استخدام جميع الخدمات بشكل طبيعي
})
}
أثناء active:
- جميع الخدمات المُصرّح بها متاحة
- يمكن تسجيل موارد ومستمعين جدد
- يمكن الاستجابة للأحداث والأوامر
(5) حالة disposing
بعد تلقي طلب إلغاء التحميل، يدخل Fiber حالة disposing ويبدأ تنظيف الموارد:
عملية disposing:
1. التوقف عن قبول طلبات جديدة
2. تنفيذ دوال تنظيف ctx.effect() بترتيب عكسي
3. إزالة مستمعي الأحداث تلقائياً
4. مسح المؤقتات
5. إلغاء تسجيل الأوامر والخدمات
(6) حالة disposed
بعد تنظيف جميع الموارد، يدخل Fiber حالة disposed:
- ctx لم يعد متاحاً
- أي استدعاء لـ ctx سيرمي خطأً
- كائن Fiber يُحتفظ به للتدقيق وتسجيل السجلات
4. إنشاء وتدمير Fiber
(1) توقيت الإنشاء
Fiber يُنشأ تلقائياً عند تسجيل إضافة:
// المنطق الداخلي للإطار (كود زائف)
function registerPlugin(plugin: PluginDefinition) {
const fiber = new Fiber({
id: generateId(),
name: plugin.name,
state: hasDeps(plugin) ? 'pending' : 'active'
})
if (fiber.state === 'active') {
fiber.context = createContext(fiber)
plugin.apply(fiber.context)
}
}
(2) محفزات التدمير
تدمير Fiber يُحفّز بـ:
| المحفّز | الوصف |
|---|---|
ctx.dispose() |
إلغاء تحميل نشط |
| تدمير Fiber الأب | إلغاء تحميل متسلسل |
| إعادة تحميل تغيير الإعدادات | Fiber القديم يُدمّر، Fiber جديد يُنشأ |
(3) ▶ مثال 3
graph TD
TRIGGER[محفّز إلغاء التحميل] --> STOP[إيقاف الطلبات الجديدة]
STOP --> CHILD[تدمير Fibers الأبناء]
CHILD --> CLEAN[تنفيذ دوال التنظيف]
CLEAN --> DISPOSED[تعليم كمُدمّر]
5. علاقات Fiber الأب-الابن
(1) الهيكل الهرمي
Fibers تدعم علاقات أب-ابن، مُشكّلة هياكل شجرية:
graph TD
ROOT[Root Fiber<br/>dsh-core] --> A[Plugin A Fiber]
ROOT --> B[Plugin B Fiber]
A --> A1[Sub-plugin A1]
A --> A2[Sub-plugin A2]
(2) إنشاء علاقات أب-ابن
أنشئ Fibers أبناء عبر ctx.plugin():
export function apply(ctx: Context) {
ctx.plugin({
name: 'sub-plugin',
apply(subCtx: Context) {
subCtx.logger.info('I am a child fiber')
}
})
}
(3) التدمير المتسلسل
عندما يُدمّر Fiber الأب، تُدمّر جميع Fibers الأبناء تلقائياً:
إلغاء تحميل Plugin A:
→ أولاً تدمير Sub-plugin A1
→ ثم تدمير Sub-plugin A2
→ أخيراً تدمير Plugin A
ترتيب التدمير "الأبناء قبل الأب" هذا يضمن عدم كسر الاعتماديات.
(4) توريث النطاق
Fibers الأبناء ترث ctx الخاص بـ Fiber الأب:
// الخدمات المُسجّلة بالإضافة الأب يمكن الوصول لها من الإضافات الأبناء
export function apply(ctx: Context) {
ctx.provide('parent-service', { ... })
ctx.plugin({
name: 'child',
inject: ['parent-service'],
apply(childCtx) {
childCtx['parent-service'] // ✅ يمكن الوصول لخدمة الأب
}
})
}
6. معالجة الأخطاء والتراجع عن الحالة
(1) فشل apply
عندما يرمي apply استثناءً، يدخل Fiber حالة errored:
export function apply(ctx: Context) {
throw new Error('initialization failed')
// Fiber → errored
}
(2) إعادة المحاولة التلقائية
Cordis يُعيد المحاولة تلقائياً لـ Fibers في حالة errored:
المحاولة 1: apply() → throw Error → errored
المحاولة 2: (انتظار 1ث) apply() → throw Error → errored
المحاولة 3: (انتظار 2ث) apply() → throw Error → errored
المحاولة 4: (انتظار 4ث) apply() → success → active
فاصل إعادة المحاولة يستخدم تراجعاً أسيّاً: 1ث → 2ث → 4ث → 8ث → ... → أقصى 60ث.
(3) إعدادات استراتيجية إعادة المحاولة
export const Config = Schema.object({
maxRetries: Schema.number().default(5).description('Max retry attempts'),
retryInterval: Schema.number().default(1000).description('Initial retry interval (ms)')
})
(4) إعادة المحاولة يدوياً
ctx.on('fiber/errored', (fiber) => {
ctx.logger.warn(`plugin ${fiber.name} errored, retrying...`)
fiber.retry()
})
(5) أخطاء غير قابلة لإعادة المحاولة
بعض الأخطاء لا يجب إعادة محاولتها:
export function apply(ctx: Context) {
if (!process.env.REQUIRED_VAR) {
// خطأ إعدادات، إعادة المحاولة لن تفيد
throw new NonRetryableError('REQUIRED_VAR is not set')
}
}
(6) أخطاء مرحلة التنظيف
أخطاء مرحلة disposing لا تمنع Fiber من الانتقال لـ disposed، لكن تُسجّل:
[warn] cleanup error in plugin my-plugin: Connection already closed
[info] plugin my-plugin disposed (with 1 cleanup warnings)
7. عزل سياق Fiber
(1) كل Fiber له ctx مستقل
const fiberA = new Fiber({ name: 'plugin-a' })
const fiberB = new Fiber({ name: 'plugin-b' })
fiberA.context !== fiberB.context // true
(2) حدود العزل
| المورد | معزول | الوصف |
|---|---|---|
| مستمعو الأحداث | ✅ | كل Fiber يُسجّل بشكل مستقل |
| المؤقتات | ✅ | كل Fiber يُنظّف بشكل مستقل |
| الأوامر | ⚠️ | مشتركة عالمياً، لكن مع نطاقات |
| الخدمات | ❌ | مشتركة عالمياً |
| الإعدادات | ✅ | كل إضافة مستقلة |
(3) موازنة مشاركة الخدمات والعزل
الخدمات مشتركة عالمياً — هذا قرار تصميم في Cordis. خدمة A التي سجّلتها إضافة A يمكن استخدامها بإضافة B عبر inject. إذا احتجت العزل، استخدم آلية النطاق (راجع 20-scope.md).
(4) استعلامات الحالة
// استعلام حالة Fiber
ctx.fiber.state // 'active'
ctx.fiber.id // 'fiber-abc-123'
ctx.fiber.parent // Fiber الأب أو null
ctx.fiber.children // Fiber[] الأبناء
❓ أسئلة شائعة
ctx.plugin()، الإطار ينشئ Fibers تلقائياً.typescript ctx.on('fiber/created', (fiber) => { ... }) ctx.on('fiber/active', (fiber) => { ... }) ctx.on('fiber/disposing', (fiber) => { ... }) ctx.on('fiber/disposed', (fiber) => { ... }) ctx.on('fiber/errored', (fiber) => { ... }) 📖 ملخص
- Fiber هو تجريد Cordis لحالة تشغيل الإضافة؛ كل نسخة إضافة تقابل Fiber
- دورة الحياة: pending (انتظار الاعتماديات) → active (تشغيل) → disposing (تنظيف) → disposed (مُدمّر)
- Fibers الأب-الابن تدعم التدمير المتسلسل بترتيب "الأبناء قبل الأب"
- عند فشل apply، يدخل Fiber حالة errored مع إعادة محاولة تلقائية بتراجع أسي
- كل Fiber له Context مستقل خاص؛ مستمعو الأحداث والمؤقتات معزولون، الخدمات مشتركة عالمياً
- راقب حالة الإضافات عبر أحداث دورة حياة Fiber
📝 تمارين
1. ⭐ أساسي: اكتب إضافة تُخرج حالة Fiber ومعرفه الحالي في apply. ابدأها وتحقق من السجلات أن Fiber في حالة active.
2. ⭐⭐ متوسط: اكتب إضافة ترمي خطأً عمداً في apply (محاكاة فشل التهيئة). لاحظ حالة errored لـ Fiber وسلوك إعادة المحاولة. ثم أصلح الخطأ وتحقق أن Fiber يستعيد حالة active.
3. ⭐⭐⭐ تحدٍ: أنشئ هيكل Fiber أب-ابن: الإضافة الأب تُسجّل خدمة، إضافة الابن تستخدمها عبر inject. ألغِ تحميل الإضافة الأب وتحقق أن الابن يُدمّر متسلسلاً أيضاً. أخرج ترتيب التدمير في دوال التنظيف للتأكد من "الأبناء قبل الأب."