// The page's session: wai-app's WebAssembly build, the program as it streams, and the log // every action a person takes on the page is sealed into (joule-ui SPEC §6, §7). // // `fire(action, inputs, handler)` is the interaction lifecycle around an asynchronous // handler: authorized first (a denied action's handler never runs), the handler run and // timed on the page's clock, then sealed as the next link of the log under the page's own // key. A page has no energy meter: the figure is the estimator's, from the handler's wall // time, and the log says so (`Estimator`). import init, { Session, verifyLog, registry, guide, check } from "./pkg/wai_app.js"; export { verifyLog, registry, guide, check }; export async function openSession() { await init(); const seed = crypto.getRandomValues(new Uint8Array(32)); const s = new Session(seed); seed.fill(0); return new PageSession(s); } export class PageSession { constructor(wasm) { this.w = wasm; this.listeners = new Set(); this.actor = wasm.actor(); } push(chunk) { return JSON.parse(this.w.push(chunk)); } finish() { return JSON.parse(this.w.finish()); } reset() { this.w.reset(); } program() { return this.w.program(); } canonical() { return this.w.canonical(); } log() { return JSON.parse(this.w.log()); } logRaw() { return this.w.logRaw(); } verify() { return JSON.parse(this.w.verify()); } totalUj() { return this.w.totalUj(); } on(f) { this.listeners.add(f); return () => this.listeners.delete(f); } note(entry) { for (const f of this.listeners) { try { f(entry); } catch { /* a view's own fault */ } } } async fire(action, inputs, handler) { const b = JSON.parse(this.w.beginAction(action, JSON.stringify(inputs === undefined ? null : inputs))); if (b.sealed) { this.note(b.sealed); throw new Error(`${action} was denied: ${b.sealed.reason}`); } const t0 = performance.now(); let out; let err = null; try { out = await handler(); } catch (e) { err = e; } const wall = performance.now() - t0; const output = err ? { error: String((err && err.message) || err) } : out === undefined ? null : out; let text; try { text = JSON.stringify(output); } catch { text = JSON.stringify(String(output)); } const entry = JSON.parse(this.w.finishAction(b.pending, text === undefined ? "null" : text, wall)); this.note(entry); if (err) throw err; return out; } }