The owning per-element collection.
The class-level prop spec.
Parsed-but-not-converted input from a user write. undefined when no
user input has landed (the prop is showing its default), including after
an attribute removal collapses the input back to undefined.
Value prior to the most recent change.
Origin label for the most recent user write: "property", "attribute",
or undefined if no write has ever landed (the mount-time default fires
with undefined). Persists across dep-cascade recomputes and across
attribute removal — describes the origin of the input shape, not whether
input is currently present. Use internalValue !== undefined to check
"is the prop user-owned right now?" (see dependsOn).
Plugins may introduce additional source values via set; the
built-in code only emits "property", "attribute", or undefined.
StaticsourcesFor validation and to distinguish user vs. cascade writes
Invoke the spec's changed hook and cascade to ElementProps#propChanged.
Apply the spec's convert hook, or pass the value through if none.
Whether this prop currently depends on dep's value.
Includes the default-prop link only while this prop has no user input.
Read this prop's current value, lazily deriving if the cache is empty.
Object.hasOwn is the sentinel because a legitimately-cached undefined
(e.g. a prop with no input and no default) must not trigger re-derivation
on every read.
Write a user-supplied value: parse, store as input, re-derive, reflect to attribute when applicable.
Raw value (string from an attribute, any from a property write).
Optionaloptions: { name?: string; oldAttributeValue?: string; source?: string } = {}Recalculate the derived value from the existing input (does not touch internalValue). Invoked when a dependency changes.
Per-element wrapper around a Prop spec. Holds the current input and derived value for one prop on one element, and the per-element behavior (get, set, update, cascade) for that prop.
The two-slot model:
undefinedmeans "no user input; fall through to default."convert(input), whereinputisinternalValueif set, otherwise the resolved default. It is eagerly cached on every write and on every dep-cascade recompute.Separating the slots means a dep cascade re-derives
valuefrom the untouchedinternalValuerather than re-runningconverton an already-converted value, which is incorrect for non-idempotent converts.