3. Replace src/main.luau
luastra/state · luastra/host · app.resolve · about 15 minutescomplete asynchronous storage lifecycle
Pending request IDs distinguish save and load completions that may arrive out of order.
When to use it
Replace the entire entry module. Keep restore separate so malformed persisted data can be tested without a real host request.
Focused example
--!strict
local Host = require("luastra/host")
local State = require("luastra/state")
local UI = require("luastra/ui")
local App = require("luastra/app")
local Root = {}
local count = 0
local message = "Nothing saved yet"
local pending: { [number]: string } = {}
local function track(id: number, operation: string)
pending[id] = operation
end
function Root.restore(payload: string): boolean
local decoded = State.decode(payload, 1)
if not decoded.success then return false end
local restored = tonumber(decoded.fields.count)
if restored == nil or restored < 0 or restored % 1 ~= 0 then return false end
count = restored
return true
end
function Root.render(): UI.Node
return UI.Screen {
id = "storage-recipe",
UI.Text { id = "counter/title", text = "Persistent counter", variant = "title" },
UI.Text { id = "counter/value", text = tostring(count), role = "status" },
UI.Text { id = "counter/message", text = message, role = "status" },
UI.Actions {
id = "counter/actions",
UI.Button { id = "counter/add", text = "Add", onTap = "counter.add" },
UI.Button { id = "counter/save", text = "Save", onTap = "counter.save" },
UI.Button { id = "counter/load", text = "Load", onTap = "counter.load" },
},
}
end
function Root.handle(action: string, target: string, _value: string)
if action == "counter.add" and target == "counter/add" then
count += 1
elseif action == "counter.save" and target == "counter/save" then
local snapshot = State.encode(1, { count = tostring(count) })
track(Host.storageSet("counter-state", snapshot), "save")
message = "Saving…"
elseif action == "counter.load" and target == "counter/load" then
track(Host.storageGet("counter-state"), "load")
message = "Loading…"
elseif action == "counter.restore" and target == "app" then
message = if Root.restore(_value) then "Loaded" else "Stored state is invalid"
end
end
function Root.resolve(
id: number,
success: boolean,
payload: string,
code: string,
_errorMessage: string
)
local operation = pending[id]
pending[id] = nil
if operation == nil then return end
if not success then message = operation .. " failed: " .. code return end
if operation == "save" then message = "Saved"
elseif Root.restore(payload) then message = "Loaded"
else message = "Stored state is invalid" end
end
function Root.snapshot()
return { count = count, message = message }
end
return App.compose {
render = function(_outputs) return Root.render() end,
handle = Root.handle,
resolve = Root.resolve,
snapshot = Root.snapshot,
}