Skuld.PageMachine.Contract (skuld_concurrency v0.47.0)
View SourceTyped protocol contract for PageMachine spindle ↔ LiveView communication.
use Skuld.PageMachine.Contract imports defspindle, defevent,
and defyield macros to declare the typed interface between a page's
spindles and its LiveView.
Each declaration is validated at compile time. The protocol module acts as the single source of truth for event routing and yield shapes.
Usage
defmodule MyApp.StoreProtocol do
use Skuld.PageMachine.Contract
defspindle Products do
defevent "search", SearchEvent, params: [query: String.t()]
defevent "filter", FilterEvent, params: [filters: map()]
defevent "buy", BuyEvent, params: [product: Product.t()]
defyield :browsing
defyield results(products: [Product.t()], total: integer())
end
defspindle Checkout do
defevent "submit_shipping", ShippingEvent, params: [shipping: map()]
defevent "submit_payment", PaymentEvent, params: [payment: map()]
defyield :shipping
defyield :payment
end
endEvents with an explicit struct name generate a typed struct module
under the spindle module (StoreProtocol.Products.SearchEvent).
The auto-generated handle_event wraps params into the struct before
resuming the spindle, giving typed receive-side pattern matching.
Events without a struct name pass {event_name, params} as the
resume value (LiveView convention).
The spindle key is the module atom (StoreProtocol.Products) — the
same module where yield functions are generated:
StoreProtocol.Products.results(products: prods, total: n)
StoreProtocol.Checkout.shipping()Integration with PageMachine
use Skuld.PageMachine,
protocol: MyApp.StoreProtocol,
on_yield: &handle_yield/3The :protocol option auto-generates handle_event/3 clauses from
the protocol's event declarations.
Summary
Functions
Declare a LiveView event routed to the current spindle.
Declare a fire-and-forget notification from the current spindle.
Open a spindle block.
Declare a yield from the current spindle to the LiveView.
Functions
Declare a LiveView event routed to the current spindle.
Without params, the spindle receives {event_name, params} (standard
LiveView convention).
With params and a struct name, a typed event struct is generated and the spindle receives it directly — enabling typed receive-side pattern matching.
Syntax
defevent "event_name"
defevent "event_name", StructName, params: [field: type(), ...]
Declare a fire-and-forget notification from the current spindle.
Same syntax as defyield, but generates a function that calls
FiberYield.notify/1 instead of Yield.yield/1 — the spindle
surfaces the value to the caller without pausing.
Syntax
defnotify :tag
defnotify tag(key: type(), ...)
Open a spindle block.
Inside the block, defevent and defyield infer the spindle from the
enclosing defspindle context — no need to repeat the spindle key.
The spindle name becomes the module atom under the protocol module
(e.g., defspindle Products → StoreProtocol.Products), which is
both the spindle key for event routing and the module where typed
yield functions are generated.
Example
defspindle Products do
defevent "search", SearchEvent, params: [query: String.t()]
defyield :browsing
defyield results(products: [Product.t()], total: integer())
end
Declare a yield from the current spindle to the LiveView.
Syntax
defyield :tag
defyield tag(key: type(), ...)