التوثيق

السمات

آلات حالات تتفاعل مع الأحداث، وتتحقق من الشروط، وتنفذ التأثيرات، وترسم الشاشة.

السمة آلة حالات مرتبطة بكيان. تسرد حالات، وكل حالة تسرد الأحداث التي تقبلها، وإلى أين يقود كل منها، وما يحدث في الطريق.

الحالات والانتقالات

lolo
1trait Approval -> Expense [interaction, instance] { 2 initial: draft 3 4 state draft { 5 SUBMIT -> submitted when (> @entity.amount 0) 6 (render-ui main { type: typography, content: "Submitted", variant: h4 }) 7 } 8 9 state submitted { 10 APPROVE -> approved when (<= @entity.amount 500) 11 (set @entity.note "Approved") 12 REJECT -> rejected 13 (set @entity.note "Rejected") 14 } 15}
  • EVENT -> target انتقال، وتتبعه التأثيرات كل منها في سطر وتُنفَّذ بالترتيب.
  • when (…) شرط: إن كان خاطئًا يُرفض الحدث ولا يتغير شيء.
  • الحالة الأولى هي البداية ما لم يحدد initial: غيرها.
  • كل انتقال قائم بذاته. انتقالان لا يختلفان إلا في حالة المصدر يُكتبان مرتين، فلا شيء يُشارَك ضمنيًا.
  • الحالة التي لا مخرج منها خطأ، إلا إن كان البرنامج يقصد أن ينتهي عندها.

يحمل قوس الترويسة فئة (interaction وlifecycle وintegration…) ومعها instance (آلة حالات لكل صف من الكيان، وهو الافتراضي) أو collection (آلة واحدة للكيان كله).

هذا مسار الموافقة كاملًا يعمل الآن. جرّب تقديم مطالبة تتجاوز 500 يورو ثم الموافقة عليها، وسيرفضها الشرط:

آلة الحالات

Approval

on Expense · states: 4 · transitions: 7
emitsMOVED
INIT
RAISE
SUBMIT
(> @entity.amount 0)
APPROVE
(<= @entity.amount 500)
REJECT
NEW_CLAIM
REVISE
nowdraft
submitted
approved
rejected
يعمل الآن مباشرة

Draft claim: EUR

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

std-orb-docex-expense.lolololo
1app std-orb-docex-expense "1.0.0" 2"std-orb-docex-expense — an approval flow: four states, a guard on the amount, and a rule the shape of the program enforces." 3orbital ExpenseOrbital { 4 entity Expense [runtime] { 5 id : string! 6 amount : number = 120 7 note : string = "" 8 } 9 10 trait Approval -> Expense [interaction, instance] { 11 initial: draft 12 13 state draft { 14 INIT -> draft 15 (render-ui main { type: stack, direction: vertical, gap: sm, children: [ 16 { type: typography, content: (str/concat "Draft claim: " @entity.amount " EUR"), variant: h4 }, 17 { type: stack, direction: horizontal, gap: sm, children: [ 18 { type: button, label: "Add 50 EUR", action: RAISE, variant: ghost }, 19 { type: button, label: "Submit", action: SUBMIT, variant: primary } 20 ] } 21 ] }) 22 RAISE -> draft 23 (emit MOVED { event: "RAISE" }) 24 (set @entity.amount (+ @entity.amount 50)) 25 SUBMIT -> submitted when (> @entity.amount 0) 26 (emit MOVED { event: "SUBMIT" }) 27 (render-ui main { type: stack, direction: vertical, gap: sm, children: [ 28 { type: typography, content: (str/concat "Submitted: " @entity.amount " EUR"), variant: h4 }, 29 { type: typography, content: "Claims over 500 EUR cannot be approved here.", variant: small, color: muted }, 30 { type: stack, direction: horizontal, gap: sm, children: [ 31 { type: button, label: "Approve", action: APPROVE, variant: primary }, 32 { type: button, label: "Reject", action: REJECT, variant: secondary } 33 ] } 34 ] }) 35 } 36 37 state submitted { 38 APPROVE -> approved when (<= @entity.amount 500) 39 (emit MOVED { event: "APPROVE" }) 40 (set @entity.note "Approved") 41 (render-ui main { type: stack, direction: vertical, gap: sm, children: [ 42 { type: badge, label: "Approved", variant: success }, 43 { type: button, label: "New claim", action: NEW_CLAIM, variant: ghost } 44 ] }) 45 REJECT -> rejected 46 (emit MOVED { event: "REJECT" }) 47 (set @entity.note "Rejected") 48 (render-ui main { type: stack, direction: vertical, gap: sm, children: [ 49 { type: badge, label: "Rejected", variant: error }, 50 { type: button, label: "Revise", action: REVISE, variant: primary } 51 ] }) 52 } 53 54 state approved { 55 NEW_CLAIM -> draft 56 (emit MOVED { event: "NEW_CLAIM" }) 57 (set @entity.amount 120) 58 (render-ui main { type: stack, direction: vertical, gap: sm, children: [ 59 { type: typography, content: (str/concat "Draft claim: " @entity.amount " EUR"), variant: h4 }, 60 { type: stack, direction: horizontal, gap: sm, children: [ 61 { type: button, label: "Add 50 EUR", action: RAISE, variant: ghost }, 62 { type: button, label: "Submit", action: SUBMIT, variant: primary } 63 ] } 64 ] }) 65 } 66 67 state rejected { 68 REVISE -> draft 69 (emit MOVED { event: "REVISE" }) 70 (render-ui main { type: stack, direction: vertical, gap: sm, children: [ 71 { type: typography, content: (str/concat "Draft claim: " @entity.amount " EUR"), variant: h4 }, 72 { type: stack, direction: horizontal, gap: sm, children: [ 73 { type: button, label: "Add 50 EUR", action: RAISE, variant: ghost }, 74 { type: button, label: "Submit", action: SUBMIT, variant: primary } 75 ] } 76 ] }) 77 } 78 79 emits { 80 MOVED -> external { 81 event : string! 82 } 83 @description "Each move names the event it took, so the diagram beside the demo can follow it." 84 } 85 } 86 87 page "/expenses" -> Approval 88}

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

submittedAPPROVE
→
approved
GuardThe transition only fires when this check is true.
(<= @entity.amount 500)
@entity.amount500<=
Effects (2)What runs, in order, when the transition fires.
(set @entity.note "Approved")
@entity.note"Approved"set

التأثيرات

التأثيرعمله
(set @entity.f v)يعيّن حقلًا. صيغة التعيين الوحيدة.
(fetch E { … }) / (persist create|update|delete E data)يقرأ الصفوف المخزّنة ويكتبها.
(emit EVENT { … })يبث حدثًا معلَنًا.
(render-ui slot { … })يرسم شجرة أنماط في خانة (main وmodal وdrawer وtoast…)، وnull يفرغها.
(navigate "/path") / (navigate-back)ينتقل إلى صفحة أخرى.
(notify info "Saved")يعرض إشعارًا.
(call-service provider action { … })يستدعي تكاملًا (نموذج لغوي، بريد، دفع…).

العوامل تحسب القيم: (+ a b) و(str/concat …) و(array/map xs (fn x …)). كلها بادئة، ولا توجد صيغة وسطية. يسرد مرجع العوامل كل واحد منها.

الروابط

الرمزالمعنى
@entity.fieldحقل من الكيان المرتبط
?fieldحقل من حمولة الحدث الوارد
@config.keyقيمة من config الخاص بالسمة
@user.fieldالزائر المسجّل
now / stateالطابع الزمني لهذه المعالجة / الحالة الحالية

البث والاستماع

تعلن السمة ما تبثه، وتعلن سمة أخرى ما تتفاعل معه. هنا يبث القرص TARGET_CHANGED، وتستمع إليه الحالة وتحوّله إلى حدثها الخاص SHOW:

آلة الحالات

Dial

on Thermostat · states: 1 · transitions: 3
emitsTARGET_CHANGEDemitsMOVED
INIT
UP
(< @entity.target 28)
DOWN
(> @entity.target 16)
nowidle

Status

on Thermostat · states: 1 · transitions: 2
listensTARGET_CHANGEDemitsMOVED
INIT
SHOW
nowwatching
يعمل الآن مباشرة

21 °C

Set a temperature between 16 and 28 °C.

std-orb-docex-thermostat.lolololo
1app std-orb-docex-thermostat "1.0.0" 2"std-orb-docex-thermostat — two traits in one orbital: one guards and emits, the other listens and reacts." 3orbital ThermostatOrbital { 4 entity Thermostat [runtime, shared] { 5 id : string! 6 target : number = 21 7 } 8 9 trait Dial -> Thermostat [interaction, instance] { 10 initial: idle 11 12 state idle { 13 INIT -> idle 14 (render-ui main { type: stack, direction: horizontal, gap: md, align: center, children: [ 15 { type: button, label: "Colder", action: DOWN, variant: secondary }, 16 { type: typography, content: (str/concat @entity.target " °C"), variant: h2 }, 17 { type: button, label: "Warmer", action: UP, variant: secondary } 18 ] }) 19 UP -> idle when (< @entity.target 28) 20 (emit MOVED { event: "UP" }) 21 (set @entity.target (+ @entity.target 1)) 22 (emit TARGET_CHANGED { target: @entity.target }) 23 DOWN -> idle when (> @entity.target 16) 24 (emit MOVED { event: "DOWN" }) 25 (set @entity.target (- @entity.target 1)) 26 (emit TARGET_CHANGED { target: @entity.target }) 27 } 28 29 emits { 30 TARGET_CHANGED { 31 target : number! 32 } 33 @description "The target temperature moved." 34 MOVED -> external { 35 event : string! 36 } 37 @description "Each move names the event it took, so the diagram beside the demo can follow it." 38 } 39 } 40 41 trait Status -> Thermostat [interaction, instance] { 42 initial: watching 43 44 state watching { 45 INIT -> watching 46 (render-ui main { type: typography, content: "Set a temperature between 16 and 28 °C.", variant: body, color: muted }) 47 SHOW -> watching 48 (emit MOVED { event: "SHOW" }) 49 (render-ui main { type: badge, label: (if (> ?target 23) "Heating up" (if (< ?target 19) "Cooling down" "Comfortable")), variant: (if (> ?target 23) warning (if (< ?target 19) info success)) }) 50 } 51 52 emits { 53 MOVED -> external { 54 event : string! 55 } 56 @description "Each move names the event it took, so the diagram beside the demo can follow it." 57 } 58 59 listens { 60 Dial.TARGET_CHANGED -> SHOW 61 SHOW { 62 target : number! 63 } 64 } 65 } 66 67 page "/thermostat" -> Dial, Status 68}

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

idleUP
→
idle
GuardThe transition only fires when this check is true.
(< @entity.target 28)
@entity.target28<
Effects (2)What runs, in order, when the transition fires.
(set @entity.target (+ @entity.target 1))
@entity.target@entity.target1+set
(emit "TARGET_CHANGED" { target: @entity.target })
"TARGET_CHANGED"@entity.targettarget{ }emit
lolo
1emits { 2 TARGET_CHANGED { 3 target : number! 4 } 5} 6 7listens { 8 Dial.TARGET_CHANGED -> SHOW 9 SHOW { 10 target : number! 11 } 12}

الأحداث internal (الافتراضية) تبقى داخل المداريّ، والأحداث external تعبر إلى مداريّات أخرى ويجب أن تعلن حمولتها. يُحَل Trait.EVENT داخل مداريّ المستمع نفسه، وعبر المداريّات يُكتب Orbital.Trait.EVENT. يعرض درس الأحداث بين المداريّات الحالتين.

النبضات

النبضة تأثير دوري، لا يعمل إلا ما دام شرط when متحققًا، فالنبضة المرتبطة بحالة تتوقف لحظة خروج السمة منها:

آلة الحالات

Timing

on Stopwatch · states: 2 · transitions: 4
emitsMOVED
INIT
START
RESET
STOP
nowstopped
running
يعمل الآن مباشرة

s

std-orb-docex-stopwatch.lolololo
1app std-orb-docex-stopwatch "1.0.0" 2"std-orb-docex-stopwatch — a tick: a periodic effect that runs only while the trait is in the running state." 3orbital StopwatchOrbital { 4 entity Stopwatch [runtime] { 5 id : string! 6 seconds : number = 0 7 } 8 9 trait Timing -> Stopwatch [interaction, instance] { 10 initial: stopped 11 12 state stopped { 13 INIT -> stopped 14 (render-ui main { type: stack, direction: horizontal, gap: md, align: center, children: [ 15 { type: typography, content: (str/concat @entity.seconds " s"), variant: h2 }, 16 { type: button, label: "Start", action: START, variant: primary }, 17 { type: button, label: "Reset", action: RESET, variant: ghost } 18 ] }) 19 START -> running 20 (emit MOVED { event: "START" }) 21 (render-ui main { type: stack, direction: horizontal, gap: md, align: center, children: [ 22 { type: typography, content: (str/concat @entity.seconds " s"), variant: h2 }, 23 { type: button, label: "Stop", action: STOP, variant: secondary } 24 ] }) 25 RESET -> stopped 26 (emit MOVED { event: "RESET" }) 27 (set @entity.seconds 0) 28 } 29 30 state running { 31 STOP -> stopped 32 (emit MOVED { event: "STOP" }) 33 (render-ui main { type: stack, direction: horizontal, gap: md, align: center, children: [ 34 { type: typography, content: (str/concat @entity.seconds " s"), variant: h2 }, 35 { type: button, label: "Start", action: START, variant: primary }, 36 { type: button, label: "Reset", action: RESET, variant: ghost } 37 ] }) 38 } 39 40 emits { 41 MOVED -> external { 42 event : string! 43 } 44 @description "Each move names the event it took, so the diagram beside the demo can follow it." 45 } 46 47 ticks { 48 second every 1s when (= state "running") 49 (set @entity.seconds (+ @entity.seconds 1)) 50 } 51 } 52 53 page "/stopwatch" -> Timing 54}

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

stoppedSTART
→
running
Effects (1)What runs, in order, when the transition fires.
lolo
1 2 3 4 ▼ticks { second every 1s when (= state "running") (set @entity.seconds (+ @entity.seconds 1)) }

الفواصل بالمللي ثانية، أو مدة (5s و1m و1h و1d)، أو نص cron. النبضة على كيان مخزّن تعمل على الخادم ما دامت صفحة ما تحمل السمة، ووسمها [background] يجعلها تعمل دون أن يراقبها أحد.

الإعدادات

config { pageSize : int = 10 @label "Items per page" } يعلن مفتاحًا تقرؤه السمة بـ @config.pageSize. المفاتيح هي ما يحق لموضع الاستدعاء تغييره عند استيراد السمة، راجع التركيب.

Orb

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

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