Skip to content

Commit 135d0aa

Browse files
fix(extractor): preserve styled creation and qualify naming coverage
Preserve whole styled argument creation-time evaluation and qualify the inherited naming/extractor coverage union, including sticky getCss proof. Refs #694 Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
1 parent a9c804a commit 135d0aa

47 files changed

Lines changed: 2993 additions & 153 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"changes": {
3+
"bindings/devup-ui-wasm/package.json": "Patch"
4+
},
5+
"note": "Evaluate supported conditional styled rule arguments once when the component is created, preserving branch laziness, authored field order, static selectors and layers, typography and runtime value associations. Keep rule capture outside attrs wrappers while authored attrs callbacks remain render-time. Responsive typography retains its shared base class and order-aware class selection without introducing empty styles for proven inert base-preset lookups. This scheduling repair adds no build errors or styling runtime.",
6+
"date": "2026-10-07T00:00:00.000Z"
7+
}

‎bindings/devup-ui-wasm/src/cache_admission_tests.rs‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,14 @@ use super::*;
22

33
#[path = "cache_allocator_tests.rs"]
44
mod allocators;
5+
#[path = "cache_naming_coverage_tests.rs"]
6+
mod naming_coverage;
57
#[path = "cache_reset_tests.rs"]
68
mod resets;
79
#[path = "cache_seed_tests.rs"]
810
mod seeds;
11+
#[path = "cache_typography_coverage_tests.rs"]
12+
mod typography_coverage;
913

1014
fn fresh() {
1115
reset_build_state_internal();

‎bindings/devup-ui-wasm/src/cache_allocator_proof.rs‎

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,26 +23,23 @@ fn counter_label(number: usize) -> String {
2323
}
2424
}
2525

26-
fn variable_owner(value: &str, prefix: &str, owner: u32) -> bool {
26+
fn variable_owner(value: &str, prefix: &str, owner_label: &str) -> bool {
2727
let variable = value.strip_prefix("var(").and_then(|value| {
2828
value
2929
.strip_suffix(')')
3030
.or_else(|| value.strip_suffix(") !important"))
3131
});
32-
let Ok(owner) = usize::try_from(owner) else {
33-
return false;
34-
};
35-
let mut number = counter_label(owner);
32+
let mut number = owner_label.to_string();
3633
number.retain(|character| character != '-');
3734
variable.is_some_and(|variable| variable.starts_with(&format!("---{prefix}S{number}-")))
3835
}
3936

4037
fn declaration_matches(
4138
property: &StyleSheetProperty,
4239
position: (u8, u8),
43-
slot: (&str, u32),
40+
slot: (&str, &str),
4441
) -> bool {
45-
let (key, owner) = slot;
42+
let (key, owner_label) = slot;
4643
let prefix = css::get_prefix().unwrap_or_default();
4744
[false, true].into_iter().any(|dynamic| {
4845
let content = AtomContent {
@@ -57,7 +54,7 @@ fn declaration_matches(
5754
}
5855
.content();
5956
css::atom_name::hex(&content.lossless) == key
60-
&& (!dynamic || variable_owner(&property.value, &prefix, owner))
57+
&& (!dynamic || variable_owner(&property.value, &prefix, owner_label))
6158
})
6259
}
6360

@@ -112,7 +109,11 @@ pub(super) fn validate(sheet: &StyleSheet, classes: &ClassMap) -> bool {
112109
other.owner_reset && other.class_name == property.class_name
113110
});
114111
(!owns_reset || key.starts_with("642d"))
115-
&& declaration_matches(property, (*level, *order), (key, owner))
112+
&& declaration_matches(
113+
property,
114+
(*level, *order),
115+
(key, &owner_label),
116+
)
116117
}
117118
})
118119
})

‎bindings/devup-ui-wasm/src/cache_names.rs‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ mod tests {
165165
"OHshort",
166166
"OLlonger_than_sixteen_payload",
167167
"prefix-L",
168+
"OXred",
168169
] {
169170
assert!(!content_name(name), "{name}");
170171
}
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
use super::*;
2+
3+
#[rstest]
4+
#[case(0)]
5+
#[case(30)]
6+
#[serial]
7+
fn dynamic_counter_owner_round_trip_keeps_the_validated_label(#[case] owner: usize) {
8+
// Given: real dynamic declarations at the plain and ad-blocker owner-label boundaries.
9+
let configure = || {
10+
fresh();
11+
set_prefix(Some("du-FLa-".into()));
12+
seed_file_map((0..=owner).map(|id| format!("{id:02}.tsx")).collect());
13+
};
14+
configure();
15+
let file = format!("{owner:02}.tsx");
16+
let source = "import {Box} from '@devup-ui/react';export const x=(p)=><Box color={p.color}/>;";
17+
let cold = output(&file, source, false);
18+
let value = snapshot();
19+
assert!(cold.2.contains("color:var(---du-FLa-S"), "{}", cold.2);
20+
configure();
21+
// When: current admission reuses the already validated owner label for variable proof.
22+
assert_eq!(import(value.clone()), Ok(()));
23+
// Then: the complete allocator/reset/declaration snapshot and full output round trip.
24+
assert_eq!(snapshot(), value);
25+
assert_eq!(output(&file, source, false), cold);
26+
fresh();
27+
}
28+
29+
#[test]
30+
#[serial]
31+
fn hoisted_declaration_without_bucket_proof_is_cold() {
32+
// Given: a genuine current atom whose stored placement falsely claims hoisting.
33+
fresh();
34+
let source = "import {Box} from '@devup-ui/react';export const x=<Box color='red'/>;";
35+
let cold = output("fresh.tsx", source, false);
36+
let mut value = snapshot();
37+
value["properties"]["fresh.tsx"]["255"]["0"][0]["h"] = true.into();
38+
fresh();
39+
let before = snapshot();
40+
companions(&value);
41+
// When: admission checks actual placement against the frozen plan.
42+
assert_eq!(import(value), Ok(()));
43+
// Then: the entire snapshot remains cold and extraction remains usable.
44+
assert_eq!(snapshot(), before);
45+
assert_eq!(output("fresh.tsx", source, false), cold);
46+
fresh();
47+
}
48+
49+
#[test]
50+
#[serial]
51+
fn invalid_manual_generated_claim_does_not_replace_live_sheet() {
52+
// Given: authored manual controls plus a generated-looking name lacking its claim.
53+
fresh();
54+
let mut live = StyleSheet::default();
55+
live.add_property(
56+
"manual-card",
57+
"color",
58+
0,
59+
"red",
60+
None,
61+
None,
62+
Some("fresh.tsx"),
63+
);
64+
assert_eq!(import_sheet_internal(live), Ok(()));
65+
let before = snapshot();
66+
let css = with_style_sheet(|sheet| sheet.create_css(Some("fresh.tsx"), false));
67+
assert!(
68+
css.contains(&format!(".manual-card{{color:{}}}", "red")),
69+
"{css}"
70+
);
71+
let mut incoming = StyleSheet::default();
72+
incoming.add_property("OLcolor-vblue", "color", 0, "blue", None, Some(0), None);
73+
// When: in-memory manual import still enforces the generated-name contract.
74+
assert_eq!(import_sheet_internal(incoming), Ok(()));
75+
// Then: authored controls stay intact and the rejected claim is not sticky.
76+
assert_eq!(snapshot(), before);
77+
assert_eq!(
78+
with_style_sheet(|sheet| sheet.create_css(Some("fresh.tsx"), false)),
79+
css
80+
);
81+
assert_eq!(cache_names::check(), Ok(()));
82+
fresh();
83+
}
84+
85+
#[test]
86+
#[serial]
87+
fn wrong_keyframe_descriptor_variant_is_cold() {
88+
// Given: actual keyframe output, with an atom variant substituted in the frame proof.
89+
fresh();
90+
let source = "import {keyframes} from '@devup-ui/react';export const x=keyframes({from:{opacity:0},to:{opacity:1}});";
91+
let cold = output("fresh.tsx", source, false);
92+
let mut value = snapshot();
93+
let claim = value["names"]
94+
.as_object_mut()
95+
.unwrap_or_else(|| panic!("names object"))
96+
.iter_mut()
97+
.find(|(name, _)| name.starts_with('K'))
98+
.unwrap_or_else(|| panic!("keyframe claim"))
99+
.1;
100+
claim["descriptor"][1] = 1.into();
101+
fresh();
102+
let before = snapshot();
103+
// When: exact frame decoding rejects the wrong semantic variant.
104+
assert_eq!(import(value), Ok(()));
105+
// Then: no cached state is installed and complete binding output is cold.
106+
assert_eq!(snapshot(), before);
107+
assert_eq!(output("fresh.tsx", source, false), cold);
108+
fresh();
109+
}
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
use super::*;
2+
3+
#[path = "cache_typography_descriptor_tests.rs"]
4+
mod descriptors;
5+
6+
fn configure(numbered: bool) {
7+
fresh();
8+
register_theme_internal(
9+
serde_json::from_value(serde_json::json!({
10+
"typography":{"body":[{"fontSize":"14px","fontFamily":"Sans"},null,{"fontSize":"18px"}]}
11+
}))
12+
.unwrap_or_else(|error| panic!("{error}")),
13+
);
14+
if numbered {
15+
seed_file_map(vec!["fresh.tsx".into()]);
16+
}
17+
}
18+
19+
fn source(responsive: bool) -> &'static str {
20+
if responsive {
21+
"import {Box} from '@devup-ui/react';export const x=<Box _hover={[null,null,{typography:'body'}]}/>;"
22+
} else {
23+
"import {Box} from '@devup-ui/react';export const x=<Box _hover={{typography:'body'}}/>;"
24+
}
25+
}
26+
27+
#[rstest]
28+
#[serial]
29+
fn current_typography_restores_exact_state_without_fresh_theme_authority(
30+
#[values(false, true)] numbered: bool,
31+
#[values(false, true)] responsive: bool,
32+
#[values(false, true)] matching: bool,
33+
) {
34+
// Given: actual preset expansion, including the original-owner counter path.
35+
configure(numbered);
36+
let cold = output("fresh.tsx", source(responsive), false);
37+
let value = snapshot();
38+
assert!(cold.2.contains("font-size:18px"), "{}", cold.2);
39+
if numbered {
40+
assert!(cold.0.contains("a-a"), "{}", cold.0);
41+
assert_eq!(
42+
value["classMap"]["D9-0"]
43+
.as_object()
44+
.unwrap_or_else(|| panic!("counter class map"))
45+
.len(),
46+
1
47+
);
48+
}
49+
fresh();
50+
let configured_theme = with_style_sheet(|sheet| sheet.theme.typography.len());
51+
if matching {
52+
companions(&value);
53+
}
54+
// When: the cache alone proves its declarations with no current preset available.
55+
assert_eq!(import(value.clone()), Ok(()));
56+
// Then: this is admission, not a silent cold miss, and fresh theme stays fresh.
57+
assert_eq!(snapshot(), value);
58+
assert_eq!(
59+
with_style_sheet(|sheet| sheet.theme.typography.len()),
60+
configured_theme
61+
);
62+
assert_eq!(
63+
with_style_sheet(|sheet| sheet.create_css(Some("fresh.tsx"), false)),
64+
cold.2
65+
);
66+
register_theme_internal(
67+
serde_json::from_value(serde_json::json!({
68+
"typography":{"body":[{"fontSize":"14px","fontFamily":"Sans"},null,{"fontSize":"18px"}]}
69+
}))
70+
.unwrap_or_else(|error| panic!("{error}")),
71+
);
72+
assert_eq!(output("fresh.tsx", source(responsive), false), cold);
73+
fresh();
74+
}
75+
76+
#[rstest]
77+
#[serial]
78+
fn corrupt_typography_declarations_are_cold_not_sticky(
79+
#[values(false, true)] numbered: bool,
80+
#[values(false, true)] responsive: bool,
81+
) {
82+
// Given: a real cache with one expanded declaration changed but its identity retained.
83+
configure(numbered);
84+
let cold = output("fresh.tsx", source(responsive), false);
85+
let mut value = snapshot();
86+
let mut changed = false;
87+
for orders in value["properties"]
88+
.as_object_mut()
89+
.unwrap_or_else(|| panic!("properties object"))
90+
.values_mut()
91+
{
92+
for levels in orders
93+
.as_object_mut()
94+
.unwrap_or_else(|| panic!("orders object"))
95+
.values_mut()
96+
{
97+
for properties in levels
98+
.as_object_mut()
99+
.unwrap_or_else(|| panic!("levels object"))
100+
.values_mut()
101+
{
102+
for property in properties
103+
.as_array_mut()
104+
.unwrap_or_else(|| panic!("declaration array"))
105+
{
106+
if property["t"] == true && property["p"] == "font-size" {
107+
property["v"] = "99px".into();
108+
changed = true;
109+
}
110+
}
111+
}
112+
}
113+
}
114+
assert!(changed);
115+
configure(numbered);
116+
let before = snapshot();
117+
companions(&value);
118+
// When: matching companions cannot excuse the corrupt declaration proof.
119+
assert_eq!(import(value), Ok(()));
120+
// Then: state and complete JS/global/local CSS are independently cold; no error latched.
121+
assert_eq!(snapshot(), before);
122+
assert_eq!(output("fresh.tsx", source(responsive), false), cold);
123+
assert_eq!(cache_names::check(), Ok(()));
124+
fresh();
125+
}
126+
127+
#[rstest]
128+
#[case("g0")]
129+
#[case("0g")]
130+
#[case("0")]
131+
#[serial]
132+
fn malformed_counter_identity_is_cold_with_matching_companions(#[case] key: &str) {
133+
// Given: a real current counter typography cache with a non-hex or truncated slot key.
134+
configure(true);
135+
let cold = output("fresh.tsx", source(false), false);
136+
let mut value = snapshot();
137+
let classes = value["classMap"]["D9-0"]
138+
.as_object_mut()
139+
.unwrap_or_else(|| panic!("counter class map"));
140+
let previous = classes
141+
.keys()
142+
.next()
143+
.cloned()
144+
.unwrap_or_else(|| panic!("counter slot key"));
145+
let slot = classes
146+
.remove(&previous)
147+
.unwrap_or_else(|| panic!("counter slot value"));
148+
classes.insert(key.into(), slot);
149+
configure(true);
150+
let before = snapshot();
151+
companions(&value);
152+
// When: the malformed identity reaches the counter proof itself.
153+
assert_eq!(import(value), Ok(()));
154+
// Then: no partial adoption or false collision, with full cold output preserved.
155+
assert_eq!(snapshot(), before);
156+
assert_eq!(output("fresh.tsx", source(false), false), cold);
157+
fresh();
158+
}

0 commit comments

Comments
 (0)