diff --git a/examples/SafeDOMExample.affine b/examples/SafeDOMExample.affine index 67f3648..2a62c1d 100644 --- a/examples/SafeDOMExample.affine +++ b/examples/SafeDOMExample.affine @@ -1,125 +1,129 @@ // SPDX-License-Identifier: MPL-2.0 -// Example: Using SafeDOM for formally verified DOM mounting +// SafeDOMExample.affine — formally-verified DOM mounting (aspirational). +// +// This example shows the *shape* of SafeDOM consumer code in current +// AffineScript syntax. The `SafeDOM` stdlib surface it references +// (`mount_safe`, `mount_when_ready`, `mount_batch`, +// `proven_selector_validate`, `proven_html_validate`, `mount`) is the +// target of `affinescript#56` (DOM+Pixi binding survey) and does not +// yet exist in the published stdlib. The file is therefore +// parse-checked but not type-checked end-to-end until #56 lands the +// bindings; `affinescript check` reports `Resolve.UndefinedModule +// SafeDOM` which is expected. +// +// Previous versions of this file (estate-wide, 5 dialect variants) +// pre-dated ADR-014 (qualified paths), ADR-016 (effect rows), and the +// `#{`-record-literal sigil (ADR-215). They were retired in favour of +// this canonical via the gitbot-fleet#208 sweep (2026-05-26). + +module SafeDOMExample; + +use prelude::{Option, Some, None, Result, Ok, Err}; + +// `Element` and friends are nominal extern types for now — the real +// shape lands with affinescript#56. +extern type Element; +extern type Selector; +extern type ValidHTML; + +// Single-mount status, lifted from the host into a typed tag union. +enum MountStatus { + Mounted(Element), + MountPointNotFound(String), + InvalidSelector(String), + InvalidHTML(String) +} + +// Batch-mount result. +enum MountResult { + Mounted([Element]), + Failed(String) +} -import SafeDOM +// Spec for one element in a batch mount. +struct MountSpec { + selector: String, + html: String +} -// Example 1: Basic mounting with error handling -fn mount_app() { - SafeDOM::mount_safe( +// SafeDOM's host-side surface, all IO-effecting. Callbacks are passed +// as separate parameters (rather than a `MountCallbacks` record) +// because fn-typed struct fields are not currently parser-supported. +extern fn mount_safe( + selector: ref String, + html: ref String, + on_success: fn(Element) -> (), + on_error: fn(String) -> (), +) -{IO}-> (); + +extern fn mount_when_ready( + selector: ref String, + html: ref String, + on_success: fn(Element) -> (), + on_error: fn(String) -> (), +) -{IO}-> (); + +extern fn mount_batch(specs: ref [MountSpec]) -{IO}-> MountResult; + +extern fn proven_selector_validate(s: ref String) -{IO}-> Result; +extern fn proven_html_validate(s: ref String) -{IO}-> Result; +extern fn mount(sel: ref Selector, html: ref ValidHTML) -{IO}-> MountStatus; + +extern fn array_for_each(xs: ref [Element], f: fn(Element) -> ()) -{IO}-> (); +extern fn array_len(xs: ref [Element]) -> Int; + +// Example 1 — basic mount with success/error branches. +pub fn mount_app() -{IO}-> () { + mount_safe( "#app", "

Hello, World!

Mounted safely with proofs.

", - on_success: fn(el) { - Console::log("✓ App mounted successfully!") - Console::log("Element: ", el) - }, - on_error: fn(err) { - Console::error("✗ Mount failed: ", err) - } - ) + fn(el) -> () { Console::log("App mounted successfully"); }, + fn(err) -> () { Console::error("Mount failed: " ++ err); }, + ); } -// Example 2: Wait for DOM ready before mounting -fn mount_when_dom_ready() { - SafeDOM::mount_when_ready( +// Example 2 — defer until DOM ready. +pub fn mount_when_dom_ready() -{IO}-> () { + mount_when_ready( "#app", "

App Title

", - on_success: fn(_) { Console::log("✓ Mounted after DOM ready") }, - on_error: fn(err) { Console::error("✗ Failed: ", err) } - ) + fn(_el) -> () { Console::log("Mounted after DOM ready"); }, + fn(err) -> () { Console::error("Failed: " ++ err); }, + ); } -// Example 3: Batch mounting (atomic - all or nothing) -fn mount_multiple() { +// Example 3 — atomic batch mount. +pub fn mount_multiple() -{IO}-> () { let specs = [ - {selector: "#header", html: "

Site Title

"}, - {selector: "#nav", html: ""}, - {selector: "#main", html: "

Content here

"}, - {selector: "#footer", html: "
© 2026
"} - ] - - match SafeDOM::mount_batch(specs) { - Ok(elements) => { - Console::log("✓ Successfully mounted ", len(elements), " elements") - for el in elements { - Console::log(" -", el) - } - } - Error(err) => { - Console::error("✗ Batch mount failed: ", err) - Console::error(" (None were mounted - atomic operation)") - } - } -} - -// Example 4: Explicit validation before mounting -fn mount_with_validation() { - // Validate selector first - match ProvenSelector::validate("#my-app") { - Error(e) => Console::error("Invalid selector: ", e) - Ok(valid_selector) => { - // Validate HTML - match ProvenHTML::validate("
Content
") { - Error(e) => Console::error("Invalid HTML: ", e) - Ok(valid_html) => { - // Now mount with proven safety - match SafeDOM::mount(valid_selector, valid_html) { - Mounted(el) => Console::log("✓ Mounted with validated inputs: ", el) - MountPointNotFound(s) => Console::error("✗ Element not found: ", s) - InvalidSelector(_) => Console::error("Impossible - already validated") - InvalidHTML(_) => Console::error("Impossible - already validated") - } - } - } + MountSpec #{ selector: "#header", html: "

Site Title

" }, + MountSpec #{ selector: "#nav", html: "" }, + MountSpec #{ selector: "#main", html: "

Content here

" }, + MountSpec #{ selector: "#footer", html: "
2026
" }, + ]; + + match mount_batch(specs) { + Mounted(elements) => { + Console::log("Batch mount succeeded"); + array_for_each(elements, fn(_el) -> () { Console::log(" element"); }); + }, + Failed(err) => { + Console::error("Batch mount failed (atomic — none mounted): " ++ err); } } } -// Example 5: Integration with TEA -namespace MyApp { - struct Model { - message: String - } - - enum Msg { - NoOp - } - - fn init() -> Model { - Model{message: "Hello from TEA"} - } - - fn update(_model: Model, _msg: Msg) -> Model { - _model - } - - fn view(model: Model) -> String { - "

" + model.message + "

" - } -} - -fn mount_tea_app() { - let model = MyApp::init() - let html = MyApp::view(model) - - SafeDOM::mount_when_ready( - "#tea-app", - html, - on_success: fn(el) { - Console::log("✓ TEA app mounted") - // Set up event handlers, subscriptions here +// Example 4 — explicit two-stage validation before mounting. +pub fn mount_with_validation() -{IO}-> () { + match proven_selector_validate("#my-app") { + Err(e) => Console::error("Invalid selector: " ++ e), + Ok(valid_selector) => match proven_html_validate("
Content
") { + Err(e) => Console::error("Invalid HTML: " ++ e), + Ok(valid_html) => match mount(valid_selector, valid_html) { + Mounted(_el) => Console::log("Mounted with validated inputs"), + MountPointNotFound(s) => Console::error("Element not found: " ++ s), + InvalidSelector(_) => Console::error("impossible — already validated"), + InvalidHTML(_) => Console::error("impossible — already validated"), + }, }, - on_error: fn(err) { Console::error("✗ TEA mount failed: ", err) } - ) -} - -// Entry point -fn main() { - Console::log("SafeDOM Examples") - Console::log("================\n") - - // Choose which example to run - mount_when_dom_ready() // Run on DOM ready + } } - -// Auto-execute when module loads -main()