Security
The HTML exporter escaped nothing. Labels, help text, placeholders, values, select and radio option labels, and group/step titles were interpolated into the markup raw. Any form definition built from external data could inject arbitrary HTML, and values placed into attributes could close the attribute and add their own (" onclick="…).
All interpolation now goes through new html_text() / html_attr() / attrs_to_str() helpers. attrs_to_str() also filters attribute names, so a crafted key cannot introduce a new attribute.
Fixes — client-side validation
The generated validation script was either absent or broken:
generate_validation_code()returned""for every format except plain"html", so no Bootstrap consumer ever had client-side validation. It is now generated forhtml,html_bootstrap4andhtml_bootstrap5.- The script looked the form up by
form.name, which is not what ends up in theidattribute. It now receives the exported id, and bails out quietly if the element is absent. - Per-field code was emitted as
let {field.name} = …— aSyntaxErrorfor any field name that is not a JS identifier (mi-campo) or is a reserved word (class). Rules are now emitted as JSON and evaluated by a generic runtime, so field names are just data. - Messages, labels and regex patterns are serialized with
json.dumps. Previously a single apostrophe or backslash broke the script. - Values are read by a helper that handles checkbox/radio groups (where
form.elements[name]yields a collection) and multiple selects, instead of assuming.value. - The handler attaches with
addEventListenerinstead of assigningonsubmit, which clobbered any other handler on the form, and is guarded against double attachment.
Compatibility
validate_<form_name>(form)is still exposed when the form name can be a JS identifier, so the previous entry point keeps working. Validation is also reachable aswindow.codeformsValidate[formId]().- Consumers that relied on unescaped HTML in labels, help text or option labels will now see those tags rendered as text.
- Bootstrap consumers start receiving a non-empty
javascript_validation_codefor the first time. It is inert until you inject it into the page.
42 new tests; 248 total.