Situs ini dikemudikan dua tombol, seperti mesin pemiliknya. Membaca: j dan k menggulir, d dan u setengah layar, gg ke atas, G ke bawah, H dan L pindah jendela, ? membuka panduan. Spasi adalah kunci leader Neovim dan mengurus isi: Spasi lalu h beranda, r riset, p proyek, g gear, a tentang, / tag, atau Spasi lalu angka untuk melompat ke jendela bernomor itu. Home adalah prefix tmux dan mengurus jendela: Home lalu c membuka terminal, Home lalu & menutup jendela, Home lalu spasi ke jendela berikutnya. Saat fokus ada di papan ketik, h j k l memindahkan pilihan antar tombol dan Enter membukanya.

RisetR TentangA Gear: G untuk gear, papan ketik, terminal, editor, homelab.G
    ProyekP Tag: / seperti di vim: cari./
    ×
    Menu

    fate-studio: Melihat dan Menjalankan Statechart di Browser

    Viewer dan simulator untuk statechart fate. Buka snapshot descriptor dari disk dan diagramnya ikut berubah saat berkasnya berubah; jalankan machine hidup lewat Server-Sent Events, bahkan yang berjalan di layanan lain. Satu binary statis, dan halamannya tidak membuat satu pun permintaan ke luar.

    Kalau fate adalah mesinnya, fate-studio adalah tempat melihat mesin itu bekerja. Buka sebuah machine, diagramnya muncul; lalu jalankan langkah demi langkah (kirim event, picu transisi tertunda, resolve atau tolak invocation) dan state aktifnya berubah di layar secara langsung.

    Saat tulisan ini pertama dibuat, hanya itu yang bisa dilakukannya, dan sebuah machine hanya bisa dilihat kalau didaftarkan lewat kode Go, di dalam program yang menaut studio-nya. Pekerjaan bulan September adalah soal menghapus syarat itu.

    Kenapa repo terpisah

    Ini keputusan yang paling sering ditanyakan, dan jawabannya satu kalimat: mesinnya tidak punya dependensi, studio-nya butuh web server.

    Kalau keduanya satu repo, go get github.com/arisros/fate akan ikut menarik seluruh isi studio ke dalam program orang yang cuma butuh state machine. Memisahkannya berarti orang membayar hanya untuk apa yang dipakai. go.mod studio-nya punya tepat satu kebutuhan: fate v0.5.1, di-vendor, jadi build Docker-nya tidak mengunduh apa pun.

    Snapshot: diagram tanpa runtime

    Sebuah machine sekarang bisa sampai ke studio sebagai berkas. fate/snapshot menulis descriptor machine sebagai JSON, dan studio merendernya tanpa ada apa pun yang berjalan di belakangnya:

    go run ./cmd/fate-studio --snapshots ./.fate --watch

    Dengan --watch, studio memeriksa direktorinya tiap 500 ms dan menggambar ulang diagram saat berkasnya berubah, lalu mendorong event graph-changed ke halaman lewat aliran SSE milik server. Ia memeriksa berkala alih-alih memakai notifikasi berkas dari OS, karena pustaka notifikasi akan jadi dependensi pertama studio. Berkas yang baru setengah ditulis gagal diurai dan diagram terakhir yang valid tetap di layar, jadi menyimpan dari editor tidak pernah mengosongkan halaman.

    Tanpa flag sama sekali, binary-nya memuat ./.fate kalau direktori itu ada, dan kembali ke demo bawaan kalau tidak ada.

    Menyimulasikan machine yang tinggal di tempat lain

    Snapshot bisa dilihat tapi tidak bisa dijalankan, karena tidak ada mesin di belakangnya. fate v0.5.0 menambahkan fate/httphandler, yang membuka sebuah machine sebagai simulator HTTP dengan format kabel yang sama dengan yang sudah dipakai studio. Jadi studio sekarang bisa mem-proxy simulator sebuah snapshot ke tempat machine aslinya berjalan:

    {
      "snapshots": ["./.fate"],
      "watch": true,
      "proxyURLs": { "checkout": "http://localhost:8081/fsm/checkout" }
    }

    Pemetaan yang sama bisa datang dari lingkungan: FATE_PROXY_CHECKOUT, dengan nama machine dijadikan huruf besar dan semua yang bukan huruf atau angka diganti _. Cookie yang dipasang server jauh dipersempit ke path studio sendiri, dan kalau server jauhnya mati, simulator menjawab 502 yang menyebut nama machine-nya alih-alih menggantung.

    Simulator virtual untuk diagram tanpa backend

    Untuk snapshot yang sama sekali tidak punya proxy, sekarang ada tombol “virtual sim” di setiap halaman machine. Ia menjalankan diagram langkah demi langkah di browser hanya dengan graph-nya. Ia tidak bisa menjalankan guard, karena guard adalah fungsi Go, jadi saat sebuah event punya beberapa target yang mungkin, ia berhenti dan bertanya cabang mana yang diambil.

    Di situlah metadata guard dari fate v0.5.0 berguna. Guard yang dijelaskan dengan fate.Gates(fate.Field("$.approvals").Gte(1)) muncul di inspector sebagai kondisi yang terbaca, dan simulator virtual memeriksanya terhadap konteks tiruan yang kamu ketik, menandai tiap gate terbuka, tertutup, atau tidak diketahui. View model per state tampil dengan cara yang sama, di sebelah JSON Schema asal turunannya.

    Tetap bisa ditempelkan ke program sendiri

    Studio-nya adalah http.Handler biasa, jadi bisa dipasang di dalam aplikasi yang sedang berjalan:

    srv := studio.NewServer("my app")
    srv.SetBasePath("/studio/")
    srv.Register(studio.Entry{
        Name:    "checkout",
        Summary: "the checkout flow",
        Build:   checkoutMachine().Describe,
        BuildLive: func() studio.LiveInstance {
            return studio.NewLiveActor(checkoutMachine(), dispatch, checkoutMachine().Describe)
        },
    })
    http.Handle("/studio/", http.StripPrefix("/studio", srv.Handler()))

    SetBasePath itu baru, dan wajib saat dipasang di bawah prefix. Sebelum ia ada, README sudah mencontohkan pemasangan di bawah http.StripPrefix, tapi halamannya meminta bundle dan API di akar situs, jadi studio yang dipasang begitu cuma tampil kosong. Server sekarang menulis ulang <base> dan path aset halaman ke prefix itu, membatasi cookie sesi ke sana, dan satu uji end-to-end menyajikan demo di bawah /studio/ supaya tetap begitu.

    Tipe kuncinya masih LiveInstance. fate bertipe generik, Actor[Ctx, Evt], sementara simulator harus bisa menjalankan machine apa pun tanpa tahu tipe konkretnya. LiveInstance menghapus tipe itu, dan dispatch yang diberikan pemakai bertugas memetakan nama event dari UI ke event bertipe miliknya. Machine yang didaftarkan dengan Build saja tetap tampil diagramnya, tanpa simulator hidup.

    Kanvas dan transport

    Diagramnya digambar dengan React Flow (@xyflow/react), tata letaknya diatur elkjs, dan garis-garisnya sekarang dirutekan dengan libavoid, yang ikut di dalam binary sebagai berkas WebAssembly. Perubahan state dikirim dari server ke browser lewat Server-Sent Events, bukan WebSocket, karena arah datanya memang satu; event dari pengguna kembali lewat request biasa. Timeline merekam tiap langkah, lengkap dengan undo, impor, dan ekspor.

    Satu binary

    Hasil build UI (Vite) di-commit ke assets/ dan ditanam ke dalam binary Go. Jadi yang di-deploy hanya satu berkas: tidak butuh Node, tidak ada CDN, tidak ada permintaan font eksternal. Satu-satunya lalu lintas keluar yang pernah dibuat server adalah ke simulator yang kamu minta untuk di-proxy.

    go run ./cmd/fate-studio --demos    # lalu buka http://localhost:8090

    Alamatnya diatur lewat --addr atau FATE_STUDIO_ADDR. Image Docker-nya dibangun dengan CGO_ENABLED=0 di atas base distroless tanpa root.

    Demo sebagai rangkaian uji

    Ada sembilan machine demo, dan masing-masing ada untuk menutup satu fitur: traffic-light dan pipeline untuk transisi dan state final, media-player untuk region paralel, editor untuk deep history, counter untuk konteks hidup, timeout untuk transisi tertunda, fetch untuk invocation, order untuk jalur paralel dengan view model per jalur, dan ticket untuk guard bernama dan ber-gate.

    Mereka juga fixture untuk UI. make fixtures menulis tiap demo sebagai snapshot dan sebagai graph yang dibaca uji UI, dan go test gagal selama berkas-berkas itu basi, jadi sebuah demo tidak bisa menyimpang dari yang dipercaya ujinya.

    id en
    rss gh in