نظرة عامة على البنية
الملكيّة على الجانبين
TSX / hooks → Mutation Stream → Scene / Layout / Paint
(غلاف TypeScript) ثنائي ومُجمَّع (نواة Rust، wasm)
↓
مُشغِّل Canvas2D ← DisplayList ← Pictureيملك الغلاف شجرة المكوّنات وتملك النواة الـ Scene، ولا يتشاركان أيّ كائن قابل للتعديل. وكلّ اتّصال عبر هذا الحدّ يجري بتدفّقات ثنائية مُصدَّرة بإصدارات: ترتيب البايتات من الأصغر، ومحاذاة على أربعة بايتات، وبصيغة تعليمات. ويتحقّق المستقبِل من رمز العملية والطول والمحاذاة والمعرّفات والحساب قبل أن يمسّ الذاكرة، ويُرفض الإدخال المشوّه رفضًا ذرّيًا بدل تطبيقه جزئيًا.
هذا الحدّ ليس تحسينًا للأداء بل حدّ صحّة: حتى حين تأتي البايتات عادةً من مُرمِّز المشروع نفسه، يعامل المفكِّك المدخلات على أنّها غير موثوقة، وهو مغطّى بالاختبار العشوائي.
ساعتان
ساعة الواجهة (الخيط الرئيسي) وساعة العرض (الـ Worker) مستقلّتان:
- يجمع الخيط الرئيسي المدخلات ويشغّل شجرة المكوّنات ويُودِع إطارات Mutation.
- ويقود الـ Worker فيزياء التمرير والحركة والتخطيط والتركيب.
لا يستدعي التمرير المستقرّ الغلاف. تُرسم البيانات الناقصة بعناصر نائبة وتُستكمل في إطارات لاحقة. لذلك حين يحجب كود التطبيق الخيط الرئيسي مئتَي ملّي ثانية يبقى التمرير والحركة متّصلين، وهذا السيناريو تحرسه اختبارات آلية بحقن الأعطال.
سلسلة التراجع
يختار كشف القدرات مسار النقل بالترتيب، والمستويات الثلاثة متكافئة وظيفيًا:
- SharedArrayBuffer — يتطلّب العزل بين المصادر (COOP/COEP)
- postMessage — عند غياب SAB
- Canvas2D على الخيط الرئيسي — عند غياب الـ Worker أو OffscreenCanvas
const root = await createHostedCanvasRoot(canvas, {
transport: { preference: "sab" }, // مجرّد تفضيل؛ وعند تعذّره يجري التراجع
});
console.log(root.mode); // "sab" | "post-message" | "main-thread"وPlayground في هذا الموقع مثال حيّ: لا تستطيع GitHub Pages إرسال ترويسات COOP/COEP، فتعمل النسخة المنشورة على مسار postMessage، وشارة النقل أعلى الصفحة تبيّن ذلك بصدق.
نموذج الإبطال
دلالة الخاصيّة هي ما يحدّد نطاق الإبطال، فلا يضع المستدعي علامة «متّسخ» يدويًا، ولا يوجد مخرج من نوع forceUpdate.
تعلن كلّ خاصيّة في مخطّط ذي مصدر واحد ما إذا كانت تؤثّر في التخطيط أو الرسم أو اختبار الإصابة أو الدلالة. فتغيير opacity لا يستدعي إعادة تخطيط، بينما تغيير width يستدعيها. وتُحفظ خرائط البتّات المتّسخة حسب النطاق، وتكشف onFrame عدد العقد المتّسخة في كلّ نطاق.
الخيار هنا هو «إبطال بأضيق ما يمكن مع شبكة أمان من اختبارات الخصائص»: يجب أن تطابق نتيجة العرض التزايدي العرض الكامل بكسلًا ببكسل، وتقلّص الاختبارات التفاضلية أيّ مثال مضادّ إلى أصغر حالة فشل.
تمثيل الـ Scene
الـ Scene داخل النواة بنية SoA (بنية من مصفوفات بدل مصفوفة من بنى):
- تحمل معرّفات العقد جيلًا، فإعادة استخدام الخانة لا تُحيي معرّفًا منتهيًا أبدًا.
- ويُحفظ الترتيب الطوبولوجي بعد الإيداع: يسبق الأب أبناءه دائمًا.
- ويجري رصّ التعديلات البنيوية مرّة واحدة لكلّ إيداع لا مرّة لكلّ تعديل.
- وتُقارَن نتائج التخطيط دفعةً واحدة من بيانات SoA مزدوجة التخزين، دون تخصيص إغلاقات أو مستمعين لكلّ عقدة في المسار الساخن.
خلفية عرض قابلة للاستبدال
تُخرِج النواة DisplayList ثنائيًا مسطّحًا، والخلفية مجرّد مُشغِّل. وخلفية Canvas2D حلقة على مصفوفات مُنمَّطة شحيحة التخصيص، إذ استدعاء wasm→JS مع كلّ عملية رسم ليس مسار عرض مقبولًا.
ويُغذّى النموذج الأوّلي المعزول على wgpu بالـ DisplayList نفسه، وتُقارَن المخرجات بكسلًا ببكسل. وتبنّي WebGPU من عدمه قرار قائم على البيانات، انظر ADR-0006.
الحتميّة
الزمن ومصدر العشوائية وتدفّقات الإدخال كلّها قابلة للحقن أو لإعادة التشغيل، ولا يعتمد خرج النواة على ترتيب جدولة الخيوط. ويسجّل أرشيف DOPR تدفّقَي Mutation وInput بترتيبهما الأصلي، ويمكن تشغيله حتميًا دون متصفّح في بيئة بلا واجهة، فتُستنسخ مشكلة الإنتاج محليًا، بينما تُستثنى تدفّقات التحرير الحسّاسة من التسجيل صراحةً.
للتعمّق
الخوارزميات وبنى البيانات ومعايير القبول كاملةً في وثيقة التصميم التقني.