التوثيق

الكيانات

البيانات التي يملكها المداريّ، وأين تُخزَّن، ومن يحق له قراءتها أو تغييرها.

الكيان هو البيانات التي يملكها المداريّ. يعلن كل مداريّ كيانًا أساسيًا واحدًا بالضبط، وأي كتلة entity ثانية أو ثالثة تصبح كيانًا مساعدًا في المداريّ نفسه. ترتبط السمات بالكيان عبر ->، وتقرؤه وتكتبه عبر @entity.

إعلان الحقول

lolo
1entity Invoice [persistent: invoices] { 2 id : string! 3 client : string! 4 amount : money = 0 5 status : "draft" | "sent" | "paid" = draft 6 dueOn : date 7 paidAmount : number = derived (if (= @entity.status "paid") @entity.amount 0) 8}
  • ! تعني حقلًا إلزاميًا، و? حقلًا يقبل القيمة الفارغة.
  • = value تحدد قيمة افتراضية. القيم الافتراضية تعابير، و= now تسجّل وقت الإنشاء.
  • "a" | "b" | "c" مفردات مغلقة: تعيين أي قيمة أخرى خطأ تحقق.
  • derived (…) يُحسب من حقول أخرى كلما تغيرت، ولا يُخزَّن أبدًا.

الأنواع هي الأنواع الأولية المعتادة (string وnumber وint وboolean وdate وtimestamp) إضافة إلى أنواع مُدقَّقة تُعرض وتُتحقق بشكل أفضل: money وemail وurl وphone وfile وduration وicon.

أين تعيش البيانات

الوسم بين القوسين يحدد مكان حفظ الصفوف:

الوسمالمعنى
[runtime]في الذاكرة طوال عمر الصفحة. حالة العرض والعدّادات ومعالجات الخطوات.
[persistent: invoices]صفوف مخزّنة في المجموعة المسماة، تُقرأ بـ fetch وتُكتب بـ persist.
[persistent: invoices, local]صفوف مخزّنة تعيش في متصفح الزائر (IndexedDB). لا يحتاج التطبيق إلى خادم.
[runtime, shared]إطار واحد مشترك بين كل السمات المرتبطة بالكيان، داخل جلسة زائر واحد.
[persistent: people, identity]هذا الكيان هو شكل الزائر المسجّل، أي @user.

قيمة @entity في السمة هي القيم الافتراضية للكيان، ثم الصف المخزّن المرتبطة به (إن وُجد)، ثم إطارها الخاص، وهو حالة العرض التي يكتبها (set @entity.x v). سمتان على الكيان نفسه غير المشترك لا ترى إحداهما كتابات الأخرى، فاستخدم shared حين ينبغي أن تتشاركا.

تعبئة الصفوف

يمكن أن يبدأ الكيان المخزّن في المتصفح بصفوف. يكتبها instances صراحة، ويُتحقق من نوع كل صف مقابل الحقول:

آلة الحالات

NoteList

on Note · states: 2 · transitions: 4
listensNOTE_SAVEDemitsNotesLoadedemitsNotesFailedemitsMOVED
INIT
NotesLoaded
NotesFailed
RELOAD
1loading
nowready

NoteForm

on Note · states: 1 · transitions: 2
emitsNOTE_SAVEDemitsMOVED
INIT
SAVE
nowediting
يعمل الآن مباشرة

Read about the closed circuit

Note:Event, guard, transition, effects, UI.

Run orb validate

Note:Zero errors and zero warnings is the bar.

البرنامج أعلاه كآلات حالات، واحدة لكل سمة: كل مربع حالة، وكل سهم حدث ينقلها، والأيقونات على السهم هي التأثيرات التي يشغّلها ذلك الحدث. الرمز ◇ على السهم شرط حارس؛ مرّر المؤشر فوقه لتقرأ الشرط. كيف تقرأ AVL

std-orb-docex-notes.lolololo
1app std-orb-docex-notes "1.0.0" 2"std-orb-docex-notes — a stored list with a form: the list loads on mount, the form saves a new note, and a listens route reloads the list." 3orbital NotesOrbital { 4 entity Note [persistent: docex_notes, local] { 5 id : string! 6 title : string! 7 body : string = "" 8 instances [ 9 { id: "N-1", title: "Read about the closed circuit", body: "Event, guard, transition, effects, UI." }, 10 { id: "N-2", title: "Run orb validate", body: "Zero errors and zero warnings is the bar." } 11 ] 12 } 13 14 trait NoteList -> Note [interaction, collection] { 15 initial: loading 16 17 state loading { 18 INIT -> loading 19 (fetch Note { emit: { success: NotesLoaded, failure: NotesFailed } }) 20 (render-ui main { type: skeleton, variant: list, rows: 2 }) 21 NotesLoaded -> ready 22 (emit MOVED { event: "NotesLoaded" }) 23 (render-ui main { type: data-list, entity: ?data, fields: [{ name: "title", label: "Title", variant: h4 }, { name: "body", label: "Note", variant: body }], emptyTitle: "No notes yet", emptyDescription: "Add one with the form.", emptyIcon: "file-text" }) 24 NotesFailed -> ready 25 (emit MOVED { event: "NotesFailed" }) 26 (render-ui main { type: alert, variant: error, message: ?error }) 27 } 28 29 state ready { 30 RELOAD -> loading 31 (emit MOVED { event: "RELOAD" }) 32 (fetch Note { emit: { success: NotesLoaded, failure: NotesFailed } }) 33 } 34 35 emits { 36 NotesLoaded { 37 data : [Note] 38 } 39 NotesFailed { 40 error : string 41 } 42 MOVED -> external { 43 event : string! 44 } 45 @description "Each move names the event it took, so the diagram beside the demo can follow it." 46 } 47 48 listens { 49 NoteForm.NOTE_SAVED -> RELOAD 50 } 51 } 52 53 trait NoteForm -> Note [interaction, instance] { 54 initial: editing 55 56 state editing { 57 INIT -> editing 58 (render-ui main { type: form-section, fields: ["title", "body"], submitEvent: SAVE, mode: create, entity: {} }) 59 SAVE -> editing 60 (emit MOVED { event: "SAVE" }) 61 (persist create Note ?data) 62 (emit NOTE_SAVED) 63 (render-ui main { type: form-section, fields: ["title", "body"], submitEvent: SAVE, mode: create, entity: {} }) 64 } 65 66 emits { 67 NOTE_SAVED 68 @description "A note was stored." 69 MOVED -> external { 70 event : string! 71 } 72 @description "Each move names the event it took, so the diagram beside the demo can follow it." 73 } 74 75 listens { 76 SAVE { 77 data : @entity! 78 } 79 } 80 } 81 82 page "/notes" -> NoteList, NoteForm 83}

ما الذي يحدث عندما يقع الحدث SAVE:

editingSAVE
→
editing
Effects (3)What runs, in order, when the transition fires.
(persist "create" "Note" @payload.data)
"create""Note"@payload.datapersist
(emit "NOTE_SAVED")
"NOTE_SAVED"emit

كيف تتحادث السمات فيما بينها، بالترتيب:

NoteListNoteFormNoteListNoteFormالمستخدمSAVEpersist create NoteNOTE_SAVED → RELOADfetch NoteNotesLoaded → readyrender-ui mainالمستخدم

تُعبَّأ الصفوف مرة واحدة في مخزن فارغ، ولا تُكتب فوق صفوف يملكها الزائر. البرنامج الذي يعلن locales [en, ar, sl] يستطيع تعبئة كل لغة على حدة بـ instances ar [ … ] وinstances sl [ … ]، راجع التوطين.

القراءة والكتابة

lolo
1(fetch Note { emit: { success: NotesLoaded, failure: NotesFailed } }) 2(fetch Note { id: ?id }) 3(fetch Note { filter: ["=", (object/get @entity status), "open"], limit: 20 }) 4(persist create Note ?data) 5(persist update Note (object/merge ?row { done: true })) 6(persist delete Note ?id)

يحمّل fetch الصفوف ويبلغ عبر الأحداث المذكورة في emit، ودونها لا شيء يلاحظ التحميل. يُعلَن حدثا النتيجة كلاهما في emits الخاصة بالسمة. داخل filter يعني (object/get @entity field) الصف قيد الاختبار، بينما يعني @entity.field المجرد إطار السمة نفسها.

العلاقات

تحديد نوع حقل بكيان آخر ينشئ علاقة: customerId : Customer (واحد) أو attendees : [Person] (متعدد). الأمر (fetch Order { include: ["customerId"] }) يملؤها. وتعني T.f نوع حقل من كيان آخر، فيكون tagId : Tag.id! نصًا معروفًا بأنه يحمل معرّف وسم.

من يحق له القراءة والكتابة

سياسات الوصول تُكتب في أعلى جسم الكيان، وتُفرض مرة واحدة في طبقة البيانات لكل سمة مرتبطة به:

lolo
1entity Session [persistent: sessions] { 2 @read (or ["=", @user.role, "manager"] ["=", (object/get @entity trainerId), @user.id]) 3 @update ["=", (object/get @entity trainerId), @user.id] 4 @delete none "sessions are archived, never deleted" 5 trainerId : Person 6 title : string! 7}

@user هو الزائر، و@entity هو الصف المعني. غياب السياسة يعني أن الجميع يستطيع تنفيذ تلك العملية، وnone "reason" توثّق إغفالًا مقصودًا. السياسات تحمي البيانات، أما ما تعرضه الشاشة أو تخفيه فيُكتب صراحة في البرنامج بالشروط.

Orb

اللغة التي تكون فيها القاعدة هي البرنامج.

© 2026 المدار. ليوبليانا، سلوفينيا.
Not reported here