The successor to WebObjects' .api format: per-element and per-binding documentation, binding types, directionality, value interpretation, typed constraints and element policies — enough to render an element's API in an editor and validate templates against it. Proposal stage; in use here. The element reference renders entirely from .apiext files.
The current/old .api declares an element's bindings — names, a few flags, cross-binding validation — and little to nothing about what a binding means or it's typing. .apiext keeps that format's skeleton and adds all of it; one legacy area (the <validation> predicate language) is replaced outright by typed constraints, with legacy files handled by a mechanical converter. The WO runtime does not read .api; only tooling does (WOLips, Parslips), so the format is constrained only by "don't strand existing files".
Same skeleton as .api (<wodefinitions> / <wo> / <binding>); extensions in green:
<wodefinitions> <wo class="WOCheckBox" content="forbidden" unknownAttributes="passthrough"> <doc><![CDATA[A checkbox. Use **either** `checked` or `value`+`selection`.]]></doc> <binding name="checked"> <pull><type interpretation="truthy">java.lang.Object</type></pull> <push><type>java.lang.Boolean</type></push> <doc>The checked state.</doc> </binding> <choose min="1" max="1"> <binding name="checked"/> <binding name="value"/> </choose> </wo> </wodefinitions>
Each element and binding may carry a <doc>. Bodies use an injection-safe Markdown subset (inline `code`, **bold**, *italic*, links, fenced code blocks); a plain consumer can show the raw text.
A binding declares one or more <type> — a fully-qualified Java class (or, later, a value-set name). Multiple types mean "accepts any of these" (e.g. updateContainerID: String or List). Intended for validation, not only display.
<pull> / <push>A binding's type can differ by direction: a checkbox's checked pulls any value (read as a boolean) and pushes back a Boolean. The type lives inside a direction block; the presence of <pull>/<push> declares the direction (no separate direction= attribute).
<pull> → read.<push> → written back.Only type is direction-specific; name, doc, and required stay on the binding. A <type> is always inside a direction block, never a direct child of <binding>.
interpretation="truthy"Some bindings read a value by a coercion rule, not by type. WOConditional.condition accepts anything but reads it as a boolean (null, 0, false → false). The interpretation attribute names that rule without changing the type: <type interpretation="truthy">java.lang.Object</type>. The type remains the validatable constraint (Object); the interpretation is shown as a qualifier, Object (truthy).
contentAn element declares its policy for child content: expected (usually a container — editors insert <wo:x></wo:x> by default; empty usage is fine), allowed (hybrid — works with and without content) or forbidden (content is a validation error, strictly: the tag must be self-closing). Validation lives entirely in forbidden; the other two are documentation and editor affordance. Absent = no policy stated, the tool decides.
unknownAttributesAn element declares what happens to attributes not named in its binding API: forbidden (an undeclared binding is a validation error — closed set), allowed (accepted; not forwarded, not flagged) or passthrough (accepted and forwarded onto the rendered tag — name preserved, value evaluated, so data-id="$item.id" renders the item's id). One axis: passthrough implies allowed, so "forwarded but flagged" — a contradiction — is unrepresentable. Absent means the author stated no policy and the tool applies its own default; a present value is a portable claim every tool must honour.
<default>A binding may declare the value the element behaves as if bound to when the binding is unbound: <default>div</default>. Literals only — computed fallbacks ("the item's toString") stay prose in <doc>. Never directional, and never satisfies validation: "bound" always means explicitly bound in the template, so a default satisfies neither required nor a constraint. Distinct from the legacy defaults attribute (an autocomplete-preset hint).
<deprecated>A binding — or, as a child of <wo>, a whole element — may be marked deprecated; the body is the migration note (same Markdown subset as <doc>). Using it is a warning-class diagnostic, never an error: deprecated still works. The message is generated from the fact ("'observeFieldFrequency' is deprecated.") plus the note. <doc> keeps describing what the binding does; <deprecated> says what to do instead.
<choose> / <requires>Typed, positive cross-binding rules replacing .api's inverted <validation> predicate trees. <choose min max> is cardinality over a set of alternatives (at-least-one / at-most-one / exactly-one); <requires binding must when> is implication, with must ∈ bound/settable/gettable. Messages are generated from the typed rule (the sample above renders as “Exactly one of 'checked' and 'value' must be bound.”); a message attribute overrides. Every referenced binding must resolve to a declared one — a file with a typo'd reference is rejected, not silently weakened.
The format describes a binding's contract, not the runtime. A type that is only determined at runtime is declared as its widest honest type. Example: WOTextField.value pushes a String, Number, or NSTimestamp depending on the bound formatter (anything, for a custom one), so its push type is declared Object, with the detail in <doc>.
| Capability | Status |
|---|---|
| per-element / per-binding doc (Markdown) | present |
| typed bindings (multi-type) | present (declared; enforcement is future work) |
| required + typed cross-binding constraints (choose / requires) | present (replaces .api's validation language; messages generated) |
| directionality (pull / push) | present |
| value interpretation (truthiness) | present |
default values (<default>) | present (display + validation-relevant: a default never satisfies a constraint) |
unknown-attribute policy (unknownAttributes) | present (forbidden / allowed / passthrough; absent = tool decides) |
child-content policy (content) | present (expected / allowed / forbidden; only forbidden validates) |
deprecation (<deprecated>, binding + element level) | present (warning-class, migration note in the body) |
| value-sets | not yet |
Successor to WO's .api, not a superset — legacy .api files are handled by their own grammar and a mechanical converter. The format spec — grammar (apiext.dtd), field-by-field reference, examples, and status — lives in its own repository: github.com/undur/apiext-format.