1use std::{
10 collections::{BTreeMap, BTreeSet},
11 sync::OnceLock,
12};
13
14use browserslist::{Distrib, Opts, resolve as resolve_browserslist};
15use omena_transform_cst::{TransformPassKind, transform_pass_sort_ordinal};
16use omena_transform_passes::{
17 TransformPassPlanV0, TransformVendorPrefixPolicyV0, plan_transform_passes,
18};
19use serde::{Deserialize, Serialize};
20
21const BROWSER_THRESHOLDS_SOURCE: &str = include_str!("../data/browser-thresholds.toml");
22const PASS_FEATURE_BINDINGS_SOURCE: &str = include_str!("../data/pass-feature-bindings.toml");
23const NATIVE_STAGE2_COVERAGE_SOURCE: &str = include_str!("../data/native-stage2-coverage.json");
24const TARGET_DATA_CONTRACT_ID: &str = "omena-transform-target-data-v0";
25const TARGET_DATA_SOURCE_FILES: &[&str] = &[
26 "data/compat-feature-selections.json",
27 "data/browser-thresholds.toml",
28 "data/pass-feature-bindings.toml",
29 "data/native-stage2-coverage.json",
30];
31const COMPAT_QUORUM_SOURCES: &[&str] = &["caniuse", "web-features", "mdn-bcd"];
32const CANIUSE_RESOLVER_WORKSPACE_DEPENDENCY: &str = "browserslist";
33const CANIUSE_RESOLVER_CARGO_PACKAGE: &str = "oxc-browserslist";
34const THRESHOLD_SOURCE_POLICY: &str = "mdnFullUnprefixedResolverCoveredV0";
35const VENDOR_PREFIX_MATRIX_SOURCE: &str = "curatedVendorPrefixMatrixTomlV0";
36const RUNTIME_FALLBACK_FEATURE_KEYS: &[&str] = &[];
37
38#[derive(Debug, Clone, PartialEq, Eq, Default)]
39struct BrowserThresholdDataV0 {
40 schema_version: String,
41 product: String,
42 refreshed_at: String,
43 threshold_source_policy: String,
44 quorum_min_sources: usize,
45 caniuse_resolver_workspace_dependency: String,
46 caniuse_resolver_cargo_package: String,
47 caniuse_resolver_cargo_version: String,
48 thresholds: Vec<BrowserFeatureThresholdV0>,
49 parse_error_count: usize,
50}
51
52#[derive(Debug, Clone, PartialEq, Eq, Default)]
53struct BrowserFeatureThresholdV0 {
54 table: String,
55 browser: String,
56 min_major: u16,
57 min_minor: u16,
58 caniuse_key: String,
59 source_quorum: Vec<String>,
60 last_verified: String,
61}
62
63#[derive(Debug, Clone, PartialEq, Eq, Default)]
64struct PassFeatureBindingDataV0 {
65 schema_version: String,
66 product: String,
67 refreshed_at: String,
68 caniuse_resolver_workspace_dependency: String,
69 caniuse_resolver_cargo_package: String,
70 caniuse_resolver_cargo_version: String,
71 bindings: Vec<PassFeatureBindingV0>,
72 parse_error_count: usize,
73}
74
75#[derive(Debug, Clone, PartialEq, Eq, Default)]
76struct PassFeatureBindingV0 {
77 pass_id: String,
78 caniuse_keys: Vec<String>,
79 support_table: String,
80}
81
82#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
83#[serde(rename_all = "camelCase")]
84pub struct TargetFeatureSupportV0 {
85 pub vendor_prefix_required: bool,
86 pub supports_light_dark: bool,
87 pub supports_color_mix: bool,
88 pub supports_oklch_oklab: bool,
89 pub supports_color_function: bool,
90 pub supports_relative_color: bool,
91 pub supports_logical_properties: bool,
92 pub supports_css_nesting: bool,
93 pub supports_css_scope: bool,
94 pub supports_cascade_layers: bool,
95}
96
97#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
98#[serde(rename_all = "camelCase")]
99#[serde(default)]
100pub struct TargetTransformOptionsV0 {
101 pub allow_logical_to_physical: bool,
102 pub allow_scope_flatten: bool,
103 pub allow_layer_flatten: bool,
104 pub enable_supports_static_eval: bool,
105 pub enable_media_static_eval: bool,
106 pub enable_container_static_eval: bool,
107 pub drop_dark_mode_media_queries: bool,
108}
109
110impl Default for TargetTransformOptionsV0 {
111 fn default() -> Self {
112 conservative_target_options()
113 }
114}
115
116#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
117#[serde(rename_all = "camelCase")]
118pub struct TransformTargetDataContractV0 {
119 pub product: &'static str,
120 pub contract_id: &'static str,
121 pub snapshot_id: String,
122 pub browser_threshold_schema_version: String,
123 pub pass_feature_binding_schema_version: String,
124 pub browser_threshold_refreshed_at: String,
125 pub pass_feature_binding_refreshed_at: String,
126 pub threshold_source_policy: String,
127 pub caniuse_resolver_workspace_dependency: String,
128 pub caniuse_resolver_cargo_package: String,
129 pub caniuse_resolver_cargo_version: String,
130 pub source_files: Vec<&'static str>,
131 pub parse_error_count: usize,
132 pub quorum_min_sources: usize,
133 pub browser_threshold_table_count: usize,
134 pub browser_threshold_entry_count: usize,
135 pub pass_feature_binding_count: usize,
136 pub stale_entry_count: usize,
137 pub unmapped_threshold_table_count: usize,
138 pub unresolvable_threshold_query_count: usize,
139 pub runtime_fallback_feature_keys: Vec<&'static str>,
140 pub runtime_fallback_feature_count: usize,
141 pub generated_coverage_complete: bool,
142 pub quorum_valid: bool,
143 pub bindings_valid: bool,
144 pub resolver_provenance_valid: bool,
145 pub valid: bool,
146}
147
148#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
149#[serde(rename_all = "camelCase")]
150pub struct TransformTargetDataEvidenceV0 {
151 pub product: &'static str,
152 pub pass_id: String,
153 pub support_table: String,
154 pub caniuse_keys: Vec<String>,
155 pub source_quorum: Vec<String>,
156 pub last_verified: Vec<String>,
157 pub resolved_targets: Vec<TransformTargetResolvedTargetEvidenceV0>,
158 pub all_resolved_targets_supported: bool,
159}
160
161#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
162#[serde(rename_all = "camelCase")]
163pub struct TransformTargetResolvedTargetEvidenceV0 {
164 pub browser: String,
165 pub version: String,
166 pub supported: bool,
167 pub matched_threshold: Option<TransformTargetThresholdEvidenceV0>,
168}
169
170#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
171#[serde(rename_all = "camelCase")]
172pub struct TransformTargetThresholdEvidenceV0 {
173 pub browser: String,
174 pub min_version: String,
175 pub caniuse_key: String,
176 pub source_quorum: Vec<String>,
177 pub last_verified: String,
178}
179
180#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
181#[serde(rename_all = "camelCase")]
182pub struct TransformTargetBoundarySummaryV0 {
183 pub schema_version: &'static str,
184 pub product: &'static str,
185 pub managed_pass_ids: Vec<&'static str>,
186 pub opt_in_pass_ids: Vec<&'static str>,
187 pub target_data_source: &'static str,
188 pub planner_surface: &'static str,
189 pub browser_threshold_table_count: usize,
190 pub browser_threshold_entry_count: usize,
191 pub pass_feature_binding_count: usize,
192 pub browser_data_source_files: Vec<&'static str>,
193 pub browser_data_parse_error_count: usize,
194 pub browser_data_quorum_valid: bool,
195 pub browser_data_bindings_valid: bool,
196 pub target_data_contract: TransformTargetDataContractV0,
197 pub vendor_prefix_matrix_source: &'static str,
198 pub runtime_fallback_feature_keys: Vec<&'static str>,
199 pub generated_coverage_complete: bool,
200 pub native_stage2_coverage: NativeStage2CoverageSummaryV0,
201}
202
203#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
204#[serde(rename_all = "camelCase")]
205pub struct NativeStage2CoverageFallbackV0 {
206 pub plugin_id: String,
207 pub policy: String,
208}
209
210#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
211#[serde(rename_all = "camelCase")]
212pub struct NativeStage2CoveredFeatureV0 {
213 pub feature_id: String,
214 pub category: String,
215 pub pass_ids: Vec<String>,
216 pub target_rule: String,
217 pub caniuse_keys: Vec<String>,
218 pub source_keys: BTreeMap<String, String>,
219 pub status: String,
220}
221
222#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
223#[serde(rename_all = "camelCase")]
224pub struct NativeStage2UncoveredFeatureV0 {
225 pub feature_id: String,
226 pub category: String,
227 pub reason: String,
228 pub observed_properties: Vec<String>,
229 pub fallback: String,
230}
231
232#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
233#[serde(rename_all = "camelCase")]
234struct NativeStage2CoverageDataV0 {
235 schema_version: String,
236 product: String,
237 binding_source: String,
238 selection_basis: Vec<String>,
239 stage1_fallback: NativeStage2CoverageFallbackV0,
240 covered_features: Vec<NativeStage2CoveredFeatureV0>,
241 uncovered_features: Vec<NativeStage2UncoveredFeatureV0>,
242}
243
244#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
245#[serde(rename_all = "camelCase")]
246pub struct NativeStage2CoverageSummaryV0 {
247 pub schema_version: String,
248 pub product: String,
249 pub binding_source: String,
250 pub selection_basis: Vec<String>,
251 pub stage1_fallback: NativeStage2CoverageFallbackV0,
252 pub covered_features: Vec<NativeStage2CoveredFeatureV0>,
253 pub uncovered_features: Vec<NativeStage2UncoveredFeatureV0>,
254 pub covered_feature_count: usize,
255 pub uncovered_feature_count: usize,
256 pub covered_pass_census_valid: bool,
257 pub binding_census_valid: bool,
258 pub valid: bool,
259}
260
261#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
262#[serde(rename_all = "camelCase")]
263pub struct TransformTargetQueryPlanV0 {
264 pub schema_version: &'static str,
265 pub product: &'static str,
266 pub query: String,
267 pub normalized_query: String,
268 pub profile_id: &'static str,
269 pub recognized_profile: bool,
270 pub target_data_source: &'static str,
271 pub target_data_contract_id: &'static str,
272 pub target_data_snapshot_id: String,
273 pub target_data_evidence: Vec<TransformTargetDataEvidenceV0>,
274 pub vendor_prefix_matrix_source: &'static str,
275 pub vendor_prefix_policy: Option<TransformVendorPrefixPolicyV0>,
276 pub resolved_targets: Vec<String>,
277 pub resolution_error: Option<String>,
278 pub support: TargetFeatureSupportV0,
279 pub transform_plan: TransformTargetPlanV0,
280 pub native_stage2_coverage: NativeStage2CoverageSummaryV0,
281}
282
283#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
284#[serde(rename_all = "camelCase")]
285pub struct TransformTargetPlanV0 {
286 pub schema_version: &'static str,
287 pub product: &'static str,
288 pub target_label: String,
289 pub required_pass_ids: Vec<&'static str>,
290 pub blocked_pass_ids: Vec<&'static str>,
291 pub planned_pass_ids: Vec<&'static str>,
292 pub pass_plan: TransformPassPlanV0,
293}
294
295pub fn summarize_omena_transform_target_boundary() -> TransformTargetBoundarySummaryV0 {
296 let browser_data = browser_threshold_data();
297 let bindings = pass_feature_binding_data();
298 let target_data_contract = target_data_contract_summary(browser_data, bindings);
299
300 TransformTargetBoundarySummaryV0 {
301 schema_version: "0",
302 product: "omena-transform-target.boundary",
303 managed_pass_ids: target_managed_passes()
304 .iter()
305 .map(|pass| pass.id())
306 .collect(),
307 opt_in_pass_ids: vec![
308 TransformPassKind::LogicalToPhysical.id(),
309 TransformPassKind::ScopeFlatten.id(),
310 TransformPassKind::LayerFlatten.id(),
311 ],
312 target_data_source: "oxcBrowserslistV3+browserThresholdsTomlV0+staticTargetProfileV0+generatedFeatureMatrixV0",
313 planner_surface: "omena-transform-passes.plan",
314 browser_threshold_table_count: browser_threshold_table_count(browser_data),
315 browser_threshold_entry_count: browser_data.thresholds.len(),
316 pass_feature_binding_count: bindings.bindings.len(),
317 browser_data_source_files: target_data_source_files(),
318 browser_data_parse_error_count: browser_data.parse_error_count + bindings.parse_error_count,
319 browser_data_quorum_valid: browser_threshold_data_is_valid(browser_data),
320 browser_data_bindings_valid: pass_feature_binding_data_is_valid(browser_data, bindings),
321 target_data_contract,
322 vendor_prefix_matrix_source: VENDOR_PREFIX_MATRIX_SOURCE,
323 runtime_fallback_feature_keys: runtime_fallback_feature_keys(),
324 generated_coverage_complete: runtime_fallback_feature_keys().is_empty(),
325 native_stage2_coverage: summarize_native_stage2_coverage(),
326 }
327}
328
329pub fn summarize_native_stage2_coverage() -> NativeStage2CoverageSummaryV0 {
330 let data = native_stage2_coverage_data();
331 let implemented_pass_ids = omena_transform_passes::implemented_mutation_pass_ids()
332 .into_iter()
333 .collect::<BTreeSet<_>>();
334 let binding_pairs = pass_feature_binding_data()
335 .bindings
336 .iter()
337 .map(|binding| (binding.pass_id.as_str(), binding.support_table.as_str()))
338 .collect::<BTreeSet<_>>();
339 let covered_pass_census_valid = data.covered_features.iter().all(|feature| {
340 feature
341 .pass_ids
342 .iter()
343 .all(|pass_id| implemented_pass_ids.contains(pass_id.as_str()))
344 });
345 let binding_census_valid = data.covered_features.iter().all(|feature| {
346 let Some(support_table) = feature.target_rule.strip_prefix("allResolvedTargetsMeet:")
347 else {
348 return false;
349 };
350 feature
351 .pass_ids
352 .iter()
353 .all(|pass_id| binding_pairs.contains(&(pass_id.as_str(), support_table)))
354 });
355 let valid = data.schema_version == "0"
356 && data.product == "omena-transform-target.native-stage2-coverage"
357 && !data.covered_features.is_empty()
358 && !data.uncovered_features.is_empty()
359 && data
360 .uncovered_features
361 .iter()
362 .all(|feature| feature.fallback == "stage1")
363 && covered_pass_census_valid
364 && binding_census_valid;
365
366 NativeStage2CoverageSummaryV0 {
367 schema_version: data.schema_version.clone(),
368 product: data.product.clone(),
369 binding_source: data.binding_source.clone(),
370 selection_basis: data.selection_basis.clone(),
371 stage1_fallback: data.stage1_fallback.clone(),
372 covered_features: data.covered_features.clone(),
373 uncovered_features: data.uncovered_features.clone(),
374 covered_feature_count: data.covered_features.len(),
375 uncovered_feature_count: data.uncovered_features.len(),
376 covered_pass_census_valid,
377 binding_census_valid,
378 valid,
379 }
380}
381
382pub fn plan_target_transforms_from_query(
383 query: impl Into<String>,
384 options: TargetTransformOptionsV0,
385) -> TransformTargetQueryPlanV0 {
386 let query = query.into();
387 let normalized_query = normalize_target_query(&query);
388 let target_resolution = target_feature_support_for_query(&normalized_query);
389 let transform_plan = plan_target_transforms(
390 target_resolution.profile_id,
391 target_resolution.support,
392 options,
393 );
394
395 TransformTargetQueryPlanV0 {
396 schema_version: "0",
397 product: "omena-transform-target.query-plan",
398 query,
399 normalized_query,
400 profile_id: target_resolution.profile_id,
401 recognized_profile: target_resolution.recognized_profile,
402 target_data_source: target_resolution.target_data_source,
403 target_data_contract_id: target_resolution.target_data_contract_id,
404 target_data_snapshot_id: target_resolution.target_data_snapshot_id,
405 target_data_evidence: target_resolution.target_data_evidence,
406 vendor_prefix_matrix_source: target_resolution.vendor_prefix_matrix_source,
407 vendor_prefix_policy: target_resolution.vendor_prefix_policy,
408 resolved_targets: target_resolution.resolved_targets,
409 resolution_error: target_resolution.resolution_error,
410 support: target_resolution.support,
411 transform_plan,
412 native_stage2_coverage: summarize_native_stage2_coverage(),
413 }
414}
415
416pub fn plan_target_transforms(
417 target_label: impl Into<String>,
418 support: TargetFeatureSupportV0,
419 options: TargetTransformOptionsV0,
420) -> TransformTargetPlanV0 {
421 let target_label = target_label.into();
422 let mut required_passes = Vec::new();
423 let mut blocked_passes = Vec::new();
424
425 if support.vendor_prefix_required {
426 required_passes.push(TransformPassKind::VendorPrefixing);
427 } else {
428 required_passes.push(TransformPassKind::StalePrefixRemoval);
429 }
430 if !support.supports_light_dark {
431 required_passes.push(TransformPassKind::LightDarkLowering);
432 }
433 if !support.supports_color_mix {
434 required_passes.push(TransformPassKind::ColorMixLowering);
435 }
436 if !support.supports_oklch_oklab {
437 required_passes.push(TransformPassKind::OklchOklabLowering);
438 }
439 if !support.supports_color_function {
440 required_passes.push(TransformPassKind::ColorFunctionLowering);
441 }
442 if !support.supports_relative_color {
443 required_passes.push(TransformPassKind::RelativeColorLowering);
444 }
445 if !support.supports_logical_properties {
446 push_required_or_blocked(
447 TransformPassKind::LogicalToPhysical,
448 options.allow_logical_to_physical,
449 &mut required_passes,
450 &mut blocked_passes,
451 );
452 }
453 if !support.supports_css_nesting {
454 required_passes.push(TransformPassKind::NestingUnwrap);
455 }
456 if !support.supports_css_scope {
457 push_required_or_blocked(
458 TransformPassKind::ScopeFlatten,
459 options.allow_scope_flatten,
460 &mut required_passes,
461 &mut blocked_passes,
462 );
463 }
464 if !support.supports_cascade_layers {
465 push_required_or_blocked(
466 TransformPassKind::LayerFlatten,
467 options.allow_layer_flatten,
468 &mut required_passes,
469 &mut blocked_passes,
470 );
471 }
472 if options.enable_supports_static_eval {
473 required_passes.push(TransformPassKind::SupportsStaticEval);
474 }
475 if options.enable_media_static_eval {
476 required_passes.push(TransformPassKind::MediaStaticEval);
477 }
478 if options.enable_container_static_eval {
479 required_passes.push(TransformPassKind::ContainerStaticEval);
480 }
481 if options.drop_dark_mode_media_queries {
482 required_passes.push(TransformPassKind::DeadMediaBranchRemoval);
483 }
484
485 required_passes.sort_by_key(|pass| transform_pass_sort_ordinal(*pass));
486 required_passes.dedup();
487 blocked_passes.sort_by_key(|pass| transform_pass_sort_ordinal(*pass));
488 blocked_passes.dedup();
489
490 let pass_plan = plan_transform_passes(&required_passes);
491 let planned_pass_ids = pass_plan.ordered_pass_ids.clone();
492
493 TransformTargetPlanV0 {
494 schema_version: "0",
495 product: "omena-transform-target.plan",
496 target_label,
497 required_pass_ids: required_passes.iter().map(|pass| pass.id()).collect(),
498 blocked_pass_ids: blocked_passes.iter().map(|pass| pass.id()).collect(),
499 planned_pass_ids,
500 pass_plan,
501 }
502}
503
504pub const fn modern_feature_support() -> TargetFeatureSupportV0 {
505 TargetFeatureSupportV0 {
506 vendor_prefix_required: false,
507 supports_light_dark: true,
508 supports_color_mix: true,
509 supports_oklch_oklab: true,
510 supports_color_function: true,
511 supports_relative_color: true,
512 supports_logical_properties: true,
513 supports_css_nesting: true,
514 supports_css_scope: true,
515 supports_cascade_layers: true,
516 }
517}
518
519pub const fn legacy_webview_feature_support() -> TargetFeatureSupportV0 {
520 TargetFeatureSupportV0 {
521 vendor_prefix_required: true,
522 supports_light_dark: false,
523 supports_color_mix: false,
524 supports_oklch_oklab: false,
525 supports_color_function: false,
526 supports_relative_color: false,
527 supports_logical_properties: false,
528 supports_css_nesting: false,
529 supports_css_scope: false,
530 supports_cascade_layers: false,
531 }
532}
533
534pub const fn conservative_target_options() -> TargetTransformOptionsV0 {
535 TargetTransformOptionsV0 {
536 allow_logical_to_physical: false,
537 allow_scope_flatten: false,
538 allow_layer_flatten: false,
539 enable_supports_static_eval: false,
540 enable_media_static_eval: false,
541 enable_container_static_eval: false,
542 drop_dark_mode_media_queries: false,
543 }
544}
545
546struct TargetQueryResolutionV0 {
547 profile_id: &'static str,
548 recognized_profile: bool,
549 target_data_source: &'static str,
550 target_data_contract_id: &'static str,
551 target_data_snapshot_id: String,
552 target_data_evidence: Vec<TransformTargetDataEvidenceV0>,
553 vendor_prefix_matrix_source: &'static str,
554 vendor_prefix_policy: Option<TransformVendorPrefixPolicyV0>,
555 resolved_targets: Vec<String>,
556 resolution_error: Option<String>,
557 support: TargetFeatureSupportV0,
558}
559
560fn normalize_target_query(query: &str) -> String {
561 query
562 .split_whitespace()
563 .collect::<Vec<_>>()
564 .join(" ")
565 .trim()
566 .to_ascii_lowercase()
567}
568
569fn target_feature_support_for_query(normalized_query: &str) -> TargetQueryResolutionV0 {
570 if matches!(
571 normalized_query,
572 "" | "modern" | "baseline 2024" | "baseline-2024"
573 ) {
574 return TargetQueryResolutionV0 {
575 profile_id: "modern-evergreen",
576 recognized_profile: true,
577 target_data_source: "staticTargetProfileV0",
578 target_data_contract_id: TARGET_DATA_CONTRACT_ID,
579 target_data_snapshot_id: current_target_data_snapshot_id(),
580 target_data_evidence: Vec::new(),
581 vendor_prefix_matrix_source: VENDOR_PREFIX_MATRIX_SOURCE,
582 vendor_prefix_policy: Some(TransformVendorPrefixPolicyV0::none()),
583 resolved_targets: Vec::new(),
584 resolution_error: None,
585 support: modern_feature_support(),
586 };
587 }
588
589 if normalized_query == "legacy" || normalized_query == "legacy-webview" {
590 return TargetQueryResolutionV0 {
591 profile_id: "legacy-webview",
592 recognized_profile: true,
593 target_data_source: "staticTargetProfileV0",
594 target_data_contract_id: TARGET_DATA_CONTRACT_ID,
595 target_data_snapshot_id: current_target_data_snapshot_id(),
596 target_data_evidence: Vec::new(),
597 vendor_prefix_matrix_source: VENDOR_PREFIX_MATRIX_SOURCE,
598 vendor_prefix_policy: Some(TransformVendorPrefixPolicyV0::conservative()),
599 resolved_targets: Vec::new(),
600 resolution_error: None,
601 support: legacy_webview_feature_support(),
602 };
603 }
604
605 match resolve_browserslist(&[normalized_query], &Opts::default()) {
606 Ok(distribs) if !distribs.is_empty() => {
607 let resolved_targets = distribs.iter().map(distrib_key).collect::<Vec<_>>();
608 let target_data_evidence = target_data_evidence_for_resolved_targets(&distribs);
609 TargetQueryResolutionV0 {
610 profile_id: "browserslist-resolved",
611 recognized_profile: true,
612 target_data_source: "oxcBrowserslistV3+browserThresholdsTomlV0+generatedFeatureMatrixV0",
613 target_data_contract_id: TARGET_DATA_CONTRACT_ID,
614 target_data_snapshot_id: current_target_data_snapshot_id(),
615 target_data_evidence,
616 vendor_prefix_matrix_source: VENDOR_PREFIX_MATRIX_SOURCE,
617 vendor_prefix_policy: Some(vendor_prefix_policy_for_resolved_targets(&distribs)),
618 support: feature_support_for_resolved_targets(&distribs),
619 resolved_targets,
620 resolution_error: None,
621 }
622 }
623 Ok(_) => unknown_conservative_resolution(None),
624 Err(error) => unknown_conservative_resolution(Some(error.to_string())),
625 }
626}
627
628fn unknown_conservative_resolution(resolution_error: Option<String>) -> TargetQueryResolutionV0 {
629 TargetQueryResolutionV0 {
630 profile_id: "unknown-conservative",
631 recognized_profile: false,
632 target_data_source: "staticTargetProfileV0",
633 target_data_contract_id: TARGET_DATA_CONTRACT_ID,
634 target_data_snapshot_id: current_target_data_snapshot_id(),
635 target_data_evidence: Vec::new(),
636 vendor_prefix_matrix_source: VENDOR_PREFIX_MATRIX_SOURCE,
637 vendor_prefix_policy: Some(TransformVendorPrefixPolicyV0::conservative()),
638 resolved_targets: Vec::new(),
639 resolution_error,
640 support: legacy_webview_feature_support(),
641 }
642}
643
644fn vendor_prefix_policy_for_resolved_targets(
645 distribs: &[Distrib],
646) -> TransformVendorPrefixPolicyV0 {
647 let mut policy = TransformVendorPrefixPolicyV0::none();
648 for distrib in distribs {
649 let browser = distrib.name();
650 let version = distrib.version();
651 match browser {
652 "ie" => policy.ms = true,
653 "edge" if !browser_version_at_least(version, 79, 0) => policy.ms = true,
654 "safari" | "ios_saf" => policy.webkit = true,
655 "chrome" | "and_chr" | "android" | "opera" | "op_mob"
656 if !browser_version_at_least(version, 50, 0) =>
657 {
658 policy.webkit = true;
659 }
660 "firefox" if !browser_version_at_least(version, 70, 0) => policy.moz = true,
661 _ => {}
662 }
663 }
664 policy
665}
666
667fn feature_support_for_resolved_targets(distribs: &[Distrib]) -> TargetFeatureSupportV0 {
668 let flexbox_fully_supported =
669 target_set_is_subset_of_browser_threshold_table(distribs, "flexbox");
670 let sticky_fully_supported =
671 target_set_is_subset_of_browser_threshold_table(distribs, "sticky_positioning");
672
673 TargetFeatureSupportV0 {
674 vendor_prefix_required: !(flexbox_fully_supported && sticky_fully_supported),
675 supports_light_dark: target_set_is_subset_of_browser_threshold_table(
676 distribs,
677 "light_dark",
678 ),
679 supports_color_mix: target_set_is_subset_of_browser_threshold_table(distribs, "color_mix"),
680 supports_oklch_oklab: target_set_is_subset_of_browser_threshold_table(
681 distribs,
682 "oklch_oklab",
683 ),
684 supports_color_function: target_set_is_subset_of_browser_threshold_table(
685 distribs,
686 "color_function",
687 ),
688 supports_relative_color: target_set_is_subset_of_browser_threshold_table(
689 distribs,
690 "relative_color",
691 ),
692 supports_logical_properties: target_set_is_subset_of_browser_threshold_table(
693 distribs,
694 "logical_properties",
695 ),
696 supports_css_nesting: target_set_is_subset_of_browser_threshold_table(
697 distribs,
698 "css_nesting",
699 ),
700 supports_css_scope: target_set_is_subset_of_browser_threshold_table(distribs, "css_scope"),
701 supports_cascade_layers: target_set_is_subset_of_browser_threshold_table(
702 distribs,
703 "cascade_layers",
704 ),
705 }
706}
707
708fn target_set_is_subset_of_browser_threshold_table(distribs: &[Distrib], table: &str) -> bool {
709 !distribs.is_empty()
710 && distribs
711 .iter()
712 .all(|distrib| distrib_matches_browser_threshold_table(distrib, table))
713}
714
715fn distrib_matches_browser_threshold_table(distrib: &Distrib, table: &str) -> bool {
716 browser_threshold_data()
717 .thresholds
718 .iter()
719 .filter(|threshold| threshold.table == table)
720 .find(|threshold| threshold.browser == distrib.name())
721 .is_some_and(|threshold| {
722 browser_version_at_least(distrib.version(), threshold.min_major, threshold.min_minor)
723 })
724}
725
726fn browser_threshold_data() -> &'static BrowserThresholdDataV0 {
727 static DATA: OnceLock<BrowserThresholdDataV0> = OnceLock::new();
728 DATA.get_or_init(|| {
729 parse_browser_threshold_data(BROWSER_THRESHOLDS_SOURCE).unwrap_or_else(|_| {
730 BrowserThresholdDataV0 {
731 parse_error_count: 1,
732 ..BrowserThresholdDataV0::default()
733 }
734 })
735 })
736}
737
738fn pass_feature_binding_data() -> &'static PassFeatureBindingDataV0 {
739 static DATA: OnceLock<PassFeatureBindingDataV0> = OnceLock::new();
740 DATA.get_or_init(|| {
741 parse_pass_feature_binding_data(PASS_FEATURE_BINDINGS_SOURCE).unwrap_or_else(|_| {
742 PassFeatureBindingDataV0 {
743 parse_error_count: 1,
744 ..PassFeatureBindingDataV0::default()
745 }
746 })
747 })
748}
749
750fn native_stage2_coverage_data() -> &'static NativeStage2CoverageDataV0 {
751 static DATA: OnceLock<NativeStage2CoverageDataV0> = OnceLock::new();
752 DATA.get_or_init(|| {
753 serde_json::from_str(NATIVE_STAGE2_COVERAGE_SOURCE).unwrap_or_else(|_| {
754 NativeStage2CoverageDataV0 {
755 schema_version: "invalid".to_string(),
756 product: "invalid".to_string(),
757 binding_source: "invalid".to_string(),
758 selection_basis: Vec::new(),
759 stage1_fallback: NativeStage2CoverageFallbackV0 {
760 plugin_id: String::new(),
761 policy: String::new(),
762 },
763 covered_features: Vec::new(),
764 uncovered_features: Vec::new(),
765 }
766 })
767 })
768}
769
770fn target_data_source_files() -> Vec<&'static str> {
771 TARGET_DATA_SOURCE_FILES.to_vec()
772}
773
774fn runtime_fallback_feature_keys() -> Vec<&'static str> {
775 RUNTIME_FALLBACK_FEATURE_KEYS.to_vec()
776}
777
778fn current_target_data_snapshot_id() -> String {
779 target_data_snapshot_id(browser_threshold_data(), pass_feature_binding_data())
780}
781
782fn target_data_snapshot_id(
783 browser_data: &BrowserThresholdDataV0,
784 bindings: &PassFeatureBindingDataV0,
785) -> String {
786 format!(
787 "{TARGET_DATA_CONTRACT_ID}:thresholds-{}:bindings-{}",
788 browser_data.refreshed_at, bindings.refreshed_at
789 )
790}
791
792fn target_data_contract_summary(
793 browser_data: &BrowserThresholdDataV0,
794 bindings: &PassFeatureBindingDataV0,
795) -> TransformTargetDataContractV0 {
796 let parse_error_count = browser_data.parse_error_count + bindings.parse_error_count;
797 let stale_entry_count = browser_threshold_stale_entry_count(browser_data);
798 let unmapped_threshold_table_count = unmapped_threshold_table_count(browser_data, bindings);
799 let unresolvable_threshold_query_count = unresolvable_threshold_query_count(browser_data);
800 let quorum_valid = browser_threshold_data_is_valid(browser_data);
801 let bindings_valid = pass_feature_binding_data_is_valid(browser_data, bindings);
802 let resolver_provenance_valid = resolver_provenance_is_valid(browser_data, bindings);
803
804 TransformTargetDataContractV0 {
805 product: "omena-transform-target.data-contract",
806 contract_id: TARGET_DATA_CONTRACT_ID,
807 snapshot_id: target_data_snapshot_id(browser_data, bindings),
808 browser_threshold_schema_version: browser_data.schema_version.clone(),
809 pass_feature_binding_schema_version: bindings.schema_version.clone(),
810 browser_threshold_refreshed_at: browser_data.refreshed_at.clone(),
811 pass_feature_binding_refreshed_at: bindings.refreshed_at.clone(),
812 threshold_source_policy: browser_data.threshold_source_policy.clone(),
813 caniuse_resolver_workspace_dependency: browser_data
814 .caniuse_resolver_workspace_dependency
815 .clone(),
816 caniuse_resolver_cargo_package: browser_data.caniuse_resolver_cargo_package.clone(),
817 caniuse_resolver_cargo_version: browser_data.caniuse_resolver_cargo_version.clone(),
818 source_files: target_data_source_files(),
819 parse_error_count,
820 quorum_min_sources: browser_data.quorum_min_sources,
821 browser_threshold_table_count: browser_threshold_table_count(browser_data),
822 browser_threshold_entry_count: browser_data.thresholds.len(),
823 pass_feature_binding_count: bindings.bindings.len(),
824 stale_entry_count,
825 unmapped_threshold_table_count,
826 unresolvable_threshold_query_count,
827 runtime_fallback_feature_keys: runtime_fallback_feature_keys(),
828 runtime_fallback_feature_count: RUNTIME_FALLBACK_FEATURE_KEYS.len(),
829 generated_coverage_complete: RUNTIME_FALLBACK_FEATURE_KEYS.is_empty(),
830 quorum_valid,
831 bindings_valid,
832 resolver_provenance_valid,
833 valid: parse_error_count == 0
834 && stale_entry_count == 0
835 && unmapped_threshold_table_count == 0
836 && unresolvable_threshold_query_count == 0
837 && quorum_valid
838 && bindings_valid
839 && resolver_provenance_valid,
840 }
841}
842
843fn target_data_evidence_for_resolved_targets(
844 distribs: &[Distrib],
845) -> Vec<TransformTargetDataEvidenceV0> {
846 let browser_data = browser_threshold_data();
847 pass_feature_binding_data()
848 .bindings
849 .iter()
850 .map(|binding| {
851 let resolved_targets = distribs
852 .iter()
853 .map(|distrib| {
854 resolved_target_evidence_for_table(
855 browser_data,
856 distrib,
857 &binding.support_table,
858 )
859 })
860 .collect::<Vec<_>>();
861 let all_resolved_targets_supported = !resolved_targets.is_empty()
862 && resolved_targets.iter().all(|target| target.supported);
863
864 TransformTargetDataEvidenceV0 {
865 product: "omena-transform-target.data-evidence",
866 pass_id: binding.pass_id.clone(),
867 support_table: binding.support_table.clone(),
868 caniuse_keys: binding.caniuse_keys.clone(),
869 source_quorum: source_quorum_for_table(browser_data, &binding.support_table),
870 last_verified: last_verified_for_table(browser_data, &binding.support_table),
871 resolved_targets,
872 all_resolved_targets_supported,
873 }
874 })
875 .collect()
876}
877
878fn resolved_target_evidence_for_table(
879 browser_data: &BrowserThresholdDataV0,
880 distrib: &Distrib,
881 table: &str,
882) -> TransformTargetResolvedTargetEvidenceV0 {
883 let threshold = browser_threshold_for_table_and_browser(browser_data, table, distrib.name());
884 let supported = threshold.is_some_and(|threshold| {
885 browser_version_at_least(distrib.version(), threshold.min_major, threshold.min_minor)
886 });
887
888 TransformTargetResolvedTargetEvidenceV0 {
889 browser: distrib.name().to_string(),
890 version: distrib.version().to_string(),
891 supported,
892 matched_threshold: threshold.map(threshold_evidence),
893 }
894}
895
896fn threshold_evidence(threshold: &BrowserFeatureThresholdV0) -> TransformTargetThresholdEvidenceV0 {
897 TransformTargetThresholdEvidenceV0 {
898 browser: threshold.browser.clone(),
899 min_version: format!("{}.{}", threshold.min_major, threshold.min_minor),
900 caniuse_key: threshold.caniuse_key.clone(),
901 source_quorum: threshold.source_quorum.clone(),
902 last_verified: threshold.last_verified.clone(),
903 }
904}
905
906fn browser_threshold_for_table_and_browser<'a>(
907 browser_data: &'a BrowserThresholdDataV0,
908 table: &str,
909 browser: &str,
910) -> Option<&'a BrowserFeatureThresholdV0> {
911 browser_data
912 .thresholds
913 .iter()
914 .find(|threshold| threshold.table == table && threshold.browser == browser)
915}
916
917fn source_quorum_for_table(browser_data: &BrowserThresholdDataV0, table: &str) -> Vec<String> {
918 let mut sources = Vec::new();
919 for threshold in browser_data
920 .thresholds
921 .iter()
922 .filter(|threshold| threshold.table == table)
923 {
924 push_unique_strings(&mut sources, threshold.source_quorum.iter());
925 }
926 sources
927}
928
929fn last_verified_for_table(browser_data: &BrowserThresholdDataV0, table: &str) -> Vec<String> {
930 let mut dates = Vec::new();
931 for threshold in browser_data
932 .thresholds
933 .iter()
934 .filter(|threshold| threshold.table == table)
935 {
936 push_unique_strings(&mut dates, std::iter::once(&threshold.last_verified));
937 }
938 dates
939}
940
941fn push_unique_strings<'a>(output: &mut Vec<String>, values: impl IntoIterator<Item = &'a String>) {
942 for value in values {
943 if !output.iter().any(|existing| existing == value) {
944 output.push(value.clone());
945 }
946 }
947}
948
949fn parse_browser_threshold_data(source: &str) -> Result<BrowserThresholdDataV0, String> {
950 let mut data = BrowserThresholdDataV0::default();
951 let mut current_threshold: Option<BrowserFeatureThresholdV0> = None;
952
953 for line in significant_toml_lines(source) {
954 if line == "[[threshold]]" {
955 if let Some(threshold) = current_threshold.take() {
956 data.thresholds.push(threshold);
957 }
958 current_threshold = Some(BrowserFeatureThresholdV0::default());
959 continue;
960 }
961
962 let (key, value) = parse_toml_assignment(&line)?;
963 if let Some(threshold) = current_threshold.as_mut() {
964 match key {
965 "table" => threshold.table = parse_toml_string(value)?,
966 "browser" => threshold.browser = parse_toml_string(value)?,
967 "min_major" => threshold.min_major = parse_toml_u16(value)?,
968 "min_minor" => threshold.min_minor = parse_toml_u16(value)?,
969 "caniuse_key" => threshold.caniuse_key = parse_toml_string(value)?,
970 "source_quorum" => threshold.source_quorum = parse_toml_string_array(value)?,
971 "last_verified" => threshold.last_verified = parse_toml_string(value)?,
972 _ => return Err(format!("unknown browser threshold key `{key}`")),
973 }
974 } else {
975 match key {
976 "schema_version" => data.schema_version = parse_toml_string(value)?,
977 "product" => data.product = parse_toml_string(value)?,
978 "refreshed_at" => data.refreshed_at = parse_toml_string(value)?,
979 "threshold_source_policy" => {
980 data.threshold_source_policy = parse_toml_string(value)?
981 }
982 "quorum_min_sources" => data.quorum_min_sources = parse_toml_usize(value)?,
983 "caniuse_resolver_workspace_dependency" => {
984 data.caniuse_resolver_workspace_dependency = parse_toml_string(value)?
985 }
986 "caniuse_resolver_cargo_package" => {
987 data.caniuse_resolver_cargo_package = parse_toml_string(value)?
988 }
989 "caniuse_resolver_cargo_version" => {
990 data.caniuse_resolver_cargo_version = parse_toml_string(value)?
991 }
992 _ => return Err(format!("unknown browser threshold root key `{key}`")),
993 }
994 }
995 }
996
997 if let Some(threshold) = current_threshold.take() {
998 data.thresholds.push(threshold);
999 }
1000
1001 Ok(data)
1002}
1003
1004fn parse_pass_feature_binding_data(source: &str) -> Result<PassFeatureBindingDataV0, String> {
1005 let mut data = PassFeatureBindingDataV0::default();
1006 let mut current_binding: Option<PassFeatureBindingV0> = None;
1007
1008 for line in significant_toml_lines(source) {
1009 if line == "[[binding]]" {
1010 if let Some(binding) = current_binding.take() {
1011 data.bindings.push(binding);
1012 }
1013 current_binding = Some(PassFeatureBindingV0::default());
1014 continue;
1015 }
1016
1017 let (key, value) = parse_toml_assignment(&line)?;
1018 if let Some(binding) = current_binding.as_mut() {
1019 match key {
1020 "pass_id" => binding.pass_id = parse_toml_string(value)?,
1021 "caniuse_keys" => binding.caniuse_keys = parse_toml_string_array(value)?,
1022 "support_table" => binding.support_table = parse_toml_string(value)?,
1023 _ => return Err(format!("unknown pass feature binding key `{key}`")),
1024 }
1025 } else {
1026 match key {
1027 "schema_version" => data.schema_version = parse_toml_string(value)?,
1028 "product" => data.product = parse_toml_string(value)?,
1029 "refreshed_at" => data.refreshed_at = parse_toml_string(value)?,
1030 "caniuse_resolver_workspace_dependency" => {
1031 data.caniuse_resolver_workspace_dependency = parse_toml_string(value)?
1032 }
1033 "caniuse_resolver_cargo_package" => {
1034 data.caniuse_resolver_cargo_package = parse_toml_string(value)?
1035 }
1036 "caniuse_resolver_cargo_version" => {
1037 data.caniuse_resolver_cargo_version = parse_toml_string(value)?
1038 }
1039 _ => return Err(format!("unknown pass feature binding root key `{key}`")),
1040 }
1041 }
1042 }
1043
1044 if let Some(binding) = current_binding.take() {
1045 data.bindings.push(binding);
1046 }
1047
1048 Ok(data)
1049}
1050
1051fn significant_toml_lines(source: &str) -> impl Iterator<Item = String> + '_ {
1052 source
1053 .lines()
1054 .map(|line| line.split('#').next().unwrap_or("").trim())
1055 .filter(|line| !line.is_empty())
1056 .map(ToOwned::to_owned)
1057}
1058
1059fn parse_toml_assignment(line: &str) -> Result<(&str, &str), String> {
1060 let Some((key, value)) = line.split_once('=') else {
1061 return Err(format!("invalid assignment `{line}`"));
1062 };
1063 Ok((key.trim(), value.trim()))
1064}
1065
1066fn parse_toml_string(value: &str) -> Result<String, String> {
1067 let value = value.trim();
1068 if value.len() < 2 || !value.starts_with('"') || !value.ends_with('"') {
1069 return Err(format!("expected quoted string, got `{value}`"));
1070 }
1071 Ok(value[1..value.len() - 1].to_string())
1072}
1073
1074fn parse_toml_u16(value: &str) -> Result<u16, String> {
1075 value
1076 .trim()
1077 .parse::<u16>()
1078 .map_err(|error| format!("invalid u16 `{value}`: {error}"))
1079}
1080
1081fn parse_toml_usize(value: &str) -> Result<usize, String> {
1082 value
1083 .trim()
1084 .parse::<usize>()
1085 .map_err(|error| format!("invalid usize `{value}`: {error}"))
1086}
1087
1088fn parse_toml_string_array(value: &str) -> Result<Vec<String>, String> {
1089 let value = value.trim();
1090 if value.len() < 2 || !value.starts_with('[') || !value.ends_with(']') {
1091 return Err(format!("expected string array, got `{value}`"));
1092 }
1093 let body = value[1..value.len() - 1].trim();
1094 if body.is_empty() {
1095 return Ok(Vec::new());
1096 }
1097
1098 body.split(',')
1099 .map(|item| parse_toml_string(item.trim()))
1100 .collect()
1101}
1102
1103fn browser_threshold_data_is_valid(data: &BrowserThresholdDataV0) -> bool {
1104 data.parse_error_count == 0
1105 && data.schema_version == "0"
1106 && data.product == "omena-transform-target.browser-thresholds"
1107 && is_iso_date(&data.refreshed_at)
1108 && data.threshold_source_policy == THRESHOLD_SOURCE_POLICY
1109 && data.quorum_min_sources == COMPAT_QUORUM_SOURCES.len()
1110 && data.caniuse_resolver_workspace_dependency == CANIUSE_RESOLVER_WORKSPACE_DEPENDENCY
1111 && data.caniuse_resolver_cargo_package == CANIUSE_RESOLVER_CARGO_PACKAGE
1112 && !data.caniuse_resolver_cargo_version.is_empty()
1113 && browser_threshold_table_count(data) >= 2
1114 && browser_threshold_stale_entry_count(data) == 0
1115 && unresolvable_threshold_query_count(data) == 0
1116 && data.thresholds.iter().all(|threshold| {
1117 !threshold.table.is_empty()
1118 && !threshold.browser.is_empty()
1119 && !threshold.caniuse_key.is_empty()
1120 && compat_source_quorum_is_complete(&threshold.source_quorum)
1121 && is_iso_date(&threshold.last_verified)
1122 })
1123}
1124
1125fn compat_source_quorum_is_complete(source_quorum: &[String]) -> bool {
1126 source_quorum.len() == COMPAT_QUORUM_SOURCES.len()
1127 && COMPAT_QUORUM_SOURCES
1128 .iter()
1129 .all(|expected| source_quorum.iter().any(|source| source == expected))
1130}
1131
1132fn pass_feature_binding_data_is_valid(
1133 browser_data: &BrowserThresholdDataV0,
1134 binding_data: &PassFeatureBindingDataV0,
1135) -> bool {
1136 binding_data.parse_error_count == 0
1137 && binding_data.schema_version == "0"
1138 && binding_data.product == "omena-transform-target.pass-feature-bindings"
1139 && is_iso_date(&binding_data.refreshed_at)
1140 && binding_data.caniuse_resolver_workspace_dependency
1141 == CANIUSE_RESOLVER_WORKSPACE_DEPENDENCY
1142 && binding_data.caniuse_resolver_cargo_package == CANIUSE_RESOLVER_CARGO_PACKAGE
1143 && !binding_data.caniuse_resolver_cargo_version.is_empty()
1144 && !binding_data.bindings.is_empty()
1145 && unmapped_threshold_table_count(browser_data, binding_data) == 0
1146 && binding_data.bindings.iter().all(|binding| {
1147 !binding.pass_id.is_empty()
1148 && target_managed_passes()
1149 .iter()
1150 .any(|pass| pass.id() == binding.pass_id)
1151 && !binding.caniuse_keys.is_empty()
1152 && binding.caniuse_keys.iter().all(|key| {
1153 browser_data.thresholds.iter().any(|threshold| {
1154 threshold.table == binding.support_table && threshold.caniuse_key == *key
1155 })
1156 })
1157 })
1158}
1159
1160fn resolver_provenance_is_valid(
1161 browser_data: &BrowserThresholdDataV0,
1162 binding_data: &PassFeatureBindingDataV0,
1163) -> bool {
1164 browser_data.caniuse_resolver_workspace_dependency == CANIUSE_RESOLVER_WORKSPACE_DEPENDENCY
1165 && browser_data.caniuse_resolver_cargo_package == CANIUSE_RESOLVER_CARGO_PACKAGE
1166 && !browser_data.caniuse_resolver_cargo_version.is_empty()
1167 && browser_data.caniuse_resolver_workspace_dependency
1168 == binding_data.caniuse_resolver_workspace_dependency
1169 && browser_data.caniuse_resolver_cargo_package
1170 == binding_data.caniuse_resolver_cargo_package
1171 && browser_data.caniuse_resolver_cargo_version
1172 == binding_data.caniuse_resolver_cargo_version
1173}
1174
1175fn browser_threshold_stale_entry_count(data: &BrowserThresholdDataV0) -> usize {
1176 data.thresholds
1177 .iter()
1178 .filter(|threshold| threshold.last_verified != data.refreshed_at)
1179 .count()
1180}
1181
1182fn unmapped_threshold_table_count(
1183 browser_data: &BrowserThresholdDataV0,
1184 binding_data: &PassFeatureBindingDataV0,
1185) -> usize {
1186 let bound_tables = binding_data
1187 .bindings
1188 .iter()
1189 .map(|binding| binding.support_table.as_str())
1190 .collect::<BTreeSet<_>>();
1191 browser_data
1192 .thresholds
1193 .iter()
1194 .map(|threshold| threshold.table.as_str())
1195 .collect::<BTreeSet<_>>()
1196 .difference(&bound_tables)
1197 .count()
1198}
1199
1200fn unresolvable_threshold_query_count(browser_data: &BrowserThresholdDataV0) -> usize {
1201 browser_data
1202 .thresholds
1203 .iter()
1204 .filter(|threshold| !threshold_browser_query_resolves(threshold))
1205 .count()
1206}
1207
1208fn threshold_browser_query_resolves(threshold: &BrowserFeatureThresholdV0) -> bool {
1209 let query = threshold_browser_query(threshold);
1210 resolve_browserslist(&[query.as_str()], &Opts::default()).is_ok_and(|distribs| {
1211 distribs.iter().any(|distrib| {
1212 distrib.name() == threshold.browser
1213 && browser_version_at_least(
1214 distrib.version(),
1215 threshold.min_major,
1216 threshold.min_minor,
1217 )
1218 })
1219 })
1220}
1221
1222fn threshold_browser_query(threshold: &BrowserFeatureThresholdV0) -> String {
1223 browser_version_query(
1224 threshold.browser.as_str(),
1225 threshold.min_major,
1226 threshold.min_minor,
1227 )
1228}
1229
1230fn browser_version_query(browser: &str, major: u16, minor: u16) -> String {
1231 if minor == 0 {
1232 format!("{browser} {major}")
1233 } else {
1234 format!("{browser} {major}.{minor}")
1235 }
1236}
1237
1238fn browser_threshold_table_count(data: &BrowserThresholdDataV0) -> usize {
1239 data.thresholds
1240 .iter()
1241 .map(|threshold| threshold.table.as_str())
1242 .collect::<BTreeSet<_>>()
1243 .len()
1244}
1245
1246fn is_iso_date(value: &str) -> bool {
1247 let bytes = value.as_bytes();
1248 bytes.len() == 10
1249 && bytes[4] == b'-'
1250 && bytes[7] == b'-'
1251 && bytes
1252 .iter()
1253 .enumerate()
1254 .all(|(index, byte)| matches!(index, 4 | 7) || byte.is_ascii_digit())
1255}
1256
1257fn browser_version_at_least(version: &str, min_major: u16, min_minor: u16) -> bool {
1258 if version.eq_ignore_ascii_case("tp") {
1259 return true;
1260 }
1261
1262 let version = version.split('-').next().unwrap_or(version);
1263 let mut parts = version.split('.');
1264 let Some(major) = parts.next().and_then(|part| part.parse::<u16>().ok()) else {
1265 return false;
1266 };
1267 let minor = parts
1268 .next()
1269 .and_then(|part| part.parse::<u16>().ok())
1270 .unwrap_or(0);
1271
1272 major > min_major || (major == min_major && minor >= min_minor)
1273}
1274
1275fn distrib_key(distrib: &Distrib) -> String {
1276 distrib.to_string()
1277}
1278
1279fn push_required_or_blocked(
1280 pass: TransformPassKind,
1281 allowed: bool,
1282 required_passes: &mut Vec<TransformPassKind>,
1283 blocked_passes: &mut Vec<TransformPassKind>,
1284) {
1285 if allowed {
1286 required_passes.push(pass);
1287 } else {
1288 blocked_passes.push(pass);
1289 }
1290}
1291
1292fn target_managed_passes() -> [TransformPassKind; 14] {
1293 [
1294 TransformPassKind::VendorPrefixing,
1295 TransformPassKind::StalePrefixRemoval,
1296 TransformPassKind::LightDarkLowering,
1297 TransformPassKind::ColorMixLowering,
1298 TransformPassKind::OklchOklabLowering,
1299 TransformPassKind::ColorFunctionLowering,
1300 TransformPassKind::RelativeColorLowering,
1301 TransformPassKind::LogicalToPhysical,
1302 TransformPassKind::NestingUnwrap,
1303 TransformPassKind::ScopeFlatten,
1304 TransformPassKind::LayerFlatten,
1305 TransformPassKind::SupportsStaticEval,
1306 TransformPassKind::MediaStaticEval,
1307 TransformPassKind::DeadMediaBranchRemoval,
1308 ]
1309}
1310
1311#[cfg(test)]
1312mod tests {
1313 use super::{
1314 TargetFeatureSupportV0, TargetTransformOptionsV0, conservative_target_options,
1315 plan_target_transforms, plan_target_transforms_from_query,
1316 summarize_omena_transform_target_boundary,
1317 };
1318
1319 #[test]
1320 fn exposes_target_lowering_boundary() {
1321 let boundary = summarize_omena_transform_target_boundary();
1322
1323 assert_eq!(boundary.product, "omena-transform-target.boundary");
1324 assert_eq!(boundary.managed_pass_ids.len(), 14);
1325 assert_eq!(
1326 boundary.target_data_source,
1327 "oxcBrowserslistV3+browserThresholdsTomlV0+staticTargetProfileV0+generatedFeatureMatrixV0"
1328 );
1329 assert_eq!(boundary.browser_threshold_table_count, 11);
1330 assert_eq!(boundary.browser_threshold_entry_count, 119);
1331 assert_eq!(boundary.pass_feature_binding_count, 13);
1332 assert_eq!(boundary.browser_data_parse_error_count, 0);
1333 assert!(boundary.browser_data_quorum_valid);
1334 assert!(boundary.browser_data_bindings_valid);
1335 assert_eq!(
1336 boundary.target_data_contract.contract_id,
1337 super::TARGET_DATA_CONTRACT_ID
1338 );
1339 assert_eq!(
1340 boundary.target_data_contract.snapshot_id,
1341 "omena-transform-target-data-v0:thresholds-2026-07-19:bindings-2026-07-19"
1342 );
1343 assert!(boundary.target_data_contract.valid);
1344 assert_eq!(boundary.target_data_contract.pass_feature_binding_count, 13);
1345 assert_eq!(
1346 boundary
1347 .target_data_contract
1348 .caniuse_resolver_workspace_dependency,
1349 super::CANIUSE_RESOLVER_WORKSPACE_DEPENDENCY
1350 );
1351 assert_eq!(
1352 boundary.target_data_contract.caniuse_resolver_cargo_package,
1353 super::CANIUSE_RESOLVER_CARGO_PACKAGE
1354 );
1355 assert_eq!(
1356 boundary.target_data_contract.caniuse_resolver_cargo_version,
1357 "3.0.5"
1358 );
1359 assert_eq!(
1360 boundary.target_data_contract.threshold_source_policy,
1361 super::THRESHOLD_SOURCE_POLICY
1362 );
1363 assert!(boundary.target_data_contract.resolver_provenance_valid);
1364 assert_eq!(boundary.target_data_contract.stale_entry_count, 0);
1365 assert_eq!(
1366 boundary.target_data_contract.unmapped_threshold_table_count,
1367 0
1368 );
1369 assert_eq!(
1370 boundary
1371 .target_data_contract
1372 .unresolvable_threshold_query_count,
1373 0
1374 );
1375 assert_eq!(
1376 boundary.target_data_contract.runtime_fallback_feature_keys,
1377 Vec::<&'static str>::new()
1378 );
1379 assert_eq!(
1380 boundary.target_data_contract.runtime_fallback_feature_count,
1381 0
1382 );
1383 assert!(boundary.target_data_contract.generated_coverage_complete);
1384 assert_eq!(
1385 boundary.runtime_fallback_feature_keys,
1386 boundary.target_data_contract.runtime_fallback_feature_keys
1387 );
1388 assert!(boundary.generated_coverage_complete);
1389 assert_eq!(boundary.native_stage2_coverage.covered_feature_count, 11);
1390 assert_eq!(boundary.native_stage2_coverage.uncovered_feature_count, 6);
1391 assert!(boundary.native_stage2_coverage.covered_pass_census_valid);
1392 assert!(boundary.native_stage2_coverage.binding_census_valid);
1393 assert!(boundary.native_stage2_coverage.valid);
1394 assert_eq!(
1395 boundary.vendor_prefix_matrix_source,
1396 super::VENDOR_PREFIX_MATRIX_SOURCE
1397 );
1398 assert!(boundary.managed_pass_ids.contains(&"vendor-prefixing"));
1399 assert!(boundary.managed_pass_ids.contains(&"stale-prefix-removal"));
1400 assert!(boundary.managed_pass_ids.contains(&"media-static-eval"));
1401 assert!(boundary.opt_in_pass_ids.contains(&"scope-flatten"));
1402 }
1403
1404 #[test]
1405 fn native_stage2_coverage_is_complete_for_declared_native_features() {
1406 let coverage = super::summarize_native_stage2_coverage();
1407
1408 assert!(coverage.valid);
1409 assert_eq!(coverage.covered_feature_count, 11);
1410 assert_eq!(coverage.uncovered_feature_count, 6);
1411 assert_eq!(
1412 coverage.stage1_fallback.plugin_id,
1413 "autoprefixer-legacy-browsers"
1414 );
1415 assert!(coverage.covered_features.iter().any(|feature| {
1416 feature.feature_id == "relative-color"
1417 && feature.pass_ids == ["relative-color-lowering"]
1418 && feature.target_rule == "allResolvedTargetsMeet:relative_color"
1419 }));
1420 assert!(coverage.uncovered_features.iter().any(|feature| {
1421 feature.feature_id == "vendor-prefixing.user-select" && feature.fallback == "stage1"
1422 }));
1423 }
1424
1425 #[test]
1426 fn relative_color_lowering_tracks_its_own_browser_support_table() {
1427 let unsupported =
1428 plan_target_transforms_from_query("chrome 121", conservative_target_options());
1429 let supported =
1430 plan_target_transforms_from_query("chrome 122", conservative_target_options());
1431
1432 assert!(!unsupported.support.supports_relative_color);
1433 assert!(
1434 unsupported
1435 .transform_plan
1436 .planned_pass_ids
1437 .contains(&"relative-color-lowering")
1438 );
1439 assert!(supported.support.supports_relative_color);
1440 assert!(
1441 !supported
1442 .transform_plan
1443 .planned_pass_ids
1444 .contains(&"relative-color-lowering")
1445 );
1446 }
1447
1448 #[test]
1449 fn browser_data_governance_externalizes_thresholds_and_bindings() {
1450 let boundary = summarize_omena_transform_target_boundary();
1451
1452 assert!(
1453 boundary
1454 .browser_data_source_files
1455 .contains(&"data/browser-thresholds.toml")
1456 );
1457 assert!(
1458 boundary
1459 .browser_data_source_files
1460 .contains(&"data/pass-feature-bindings.toml")
1461 );
1462 assert!(boundary.browser_data_quorum_valid);
1463 assert!(boundary.browser_data_bindings_valid);
1464 assert_eq!(
1465 boundary
1466 .target_data_contract
1467 .unresolvable_threshold_query_count,
1468 0
1469 );
1470 assert_eq!(
1471 boundary.target_data_contract.source_files,
1472 boundary.browser_data_source_files
1473 );
1474 assert_eq!(
1475 boundary
1476 .target_data_contract
1477 .browser_threshold_schema_version,
1478 "0"
1479 );
1480 assert_eq!(
1481 boundary
1482 .target_data_contract
1483 .pass_feature_binding_schema_version,
1484 "0"
1485 );
1486 assert_eq!(
1487 boundary.target_data_contract.threshold_source_policy,
1488 super::THRESHOLD_SOURCE_POLICY
1489 );
1490 assert!(boundary.target_data_contract.resolver_provenance_valid);
1491 assert_eq!(
1492 boundary.target_data_contract.runtime_fallback_feature_count,
1493 0
1494 );
1495 assert!(boundary.target_data_contract.generated_coverage_complete);
1496 }
1497
1498 #[test]
1499 fn generated_browser_threshold_rows_resolve_and_match_pass_boundaries() {
1500 let browser_data = super::browser_threshold_data();
1501 let bindings = super::pass_feature_binding_data();
1502 let options = TargetTransformOptionsV0 {
1503 allow_logical_to_physical: true,
1504 allow_scope_flatten: true,
1505 allow_layer_flatten: true,
1506 enable_supports_static_eval: false,
1507 enable_media_static_eval: false,
1508 enable_container_static_eval: false,
1509 drop_dark_mode_media_queries: false,
1510 };
1511
1512 for binding in &bindings.bindings {
1513 let pass_id = binding.pass_id.as_str();
1514 for threshold in browser_data
1515 .thresholds
1516 .iter()
1517 .filter(|threshold| threshold.table == binding.support_table)
1518 {
1519 let query = super::threshold_browser_query(threshold);
1520 let plan = plan_target_transforms_from_query(query.clone(), options);
1521 assert_eq!(
1522 plan.profile_id, "browserslist-resolved",
1523 "{query} must resolve through oxc-browserslist"
1524 );
1525
1526 let evidence = plan.target_data_evidence.iter().find(|evidence| {
1527 evidence.support_table == threshold.table && evidence.pass_id == pass_id
1528 });
1529 assert!(
1530 evidence.is_some(),
1531 "{query} missing evidence for {}",
1532 threshold.table
1533 );
1534 let Some(evidence) = evidence else {
1535 continue;
1536 };
1537 let resolved_target = evidence
1538 .resolved_targets
1539 .iter()
1540 .find(|target| target.browser == threshold.browser);
1541 assert!(
1542 resolved_target.is_some(),
1543 "{query} missing resolved target {}",
1544 threshold.browser
1545 );
1546 let Some(resolved_target) = resolved_target else {
1547 continue;
1548 };
1549 assert!(
1550 resolved_target.supported,
1551 "{query} should satisfy {}",
1552 threshold.table
1553 );
1554 if pass_id != super::TransformPassKind::VendorPrefixing.id()
1555 && pass_id != super::TransformPassKind::StalePrefixRemoval.id()
1556 {
1557 assert!(
1558 !plan.transform_plan.required_pass_ids.contains(&pass_id),
1559 "{query} should not require {pass_id} at the support threshold"
1560 );
1561 assert!(
1562 !plan.transform_plan.blocked_pass_ids.contains(&pass_id),
1563 "{query} should not block {pass_id} at the support threshold"
1564 );
1565 }
1566
1567 if let Some((previous_major, previous_minor)) =
1568 previous_browser_version(threshold.min_major, threshold.min_minor)
1569 {
1570 let previous_query = super::browser_version_query(
1571 threshold.browser.as_str(),
1572 previous_major,
1573 previous_minor,
1574 );
1575 let previous_plan =
1576 plan_target_transforms_from_query(previous_query.clone(), options);
1577 if previous_plan.profile_id == "browserslist-resolved"
1578 && previous_plan
1579 .resolved_targets
1580 .iter()
1581 .any(|target| target == &previous_query)
1582 {
1583 let previous_evidence =
1584 previous_plan.target_data_evidence.iter().find(|evidence| {
1585 evidence.support_table == threshold.table
1586 && evidence.pass_id == pass_id
1587 });
1588 assert!(
1589 previous_evidence.is_some(),
1590 "{previous_query} missing evidence for {}",
1591 threshold.table
1592 );
1593 let Some(previous_evidence) = previous_evidence else {
1594 continue;
1595 };
1596 let previous_target = previous_evidence
1597 .resolved_targets
1598 .iter()
1599 .find(|target| target.browser == threshold.browser);
1600 assert!(
1601 previous_target.is_some(),
1602 "{previous_query} missing resolved target {}",
1603 threshold.browser
1604 );
1605 let Some(previous_target) = previous_target else {
1606 continue;
1607 };
1608 assert!(
1609 !previous_target.supported,
1610 "{previous_query} should not satisfy {}",
1611 threshold.table
1612 );
1613 if pass_id != super::TransformPassKind::VendorPrefixing.id()
1614 && pass_id != super::TransformPassKind::StalePrefixRemoval.id()
1615 {
1616 assert!(
1617 previous_plan
1618 .transform_plan
1619 .required_pass_ids
1620 .contains(&pass_id)
1621 || previous_plan
1622 .transform_plan
1623 .blocked_pass_ids
1624 .contains(&pass_id),
1625 "{previous_query} should require or block {pass_id} below the support threshold"
1626 );
1627 }
1628 }
1629 }
1630 }
1631 }
1632 }
1633
1634 fn previous_browser_version(major: u16, minor: u16) -> Option<(u16, u16)> {
1635 if minor > 0 {
1636 return Some((major, minor - 1));
1637 }
1638 major.checked_sub(1).map(|previous| (previous, 0))
1639 }
1640
1641 #[test]
1642 fn plans_target_lowering_with_vendor_prefix_after_lowering_edges() {
1643 let support = TargetFeatureSupportV0 {
1644 vendor_prefix_required: true,
1645 supports_light_dark: false,
1646 supports_color_mix: false,
1647 supports_oklch_oklab: true,
1648 supports_color_function: true,
1649 supports_relative_color: true,
1650 supports_logical_properties: true,
1651 supports_css_nesting: false,
1652 supports_css_scope: false,
1653 supports_cascade_layers: false,
1654 };
1655 let options = TargetTransformOptionsV0 {
1656 allow_logical_to_physical: false,
1657 allow_scope_flatten: true,
1658 allow_layer_flatten: true,
1659 enable_supports_static_eval: true,
1660 enable_media_static_eval: true,
1661 enable_container_static_eval: false,
1662 drop_dark_mode_media_queries: false,
1663 };
1664
1665 let plan = plan_target_transforms("legacy-webview", support, options);
1666
1667 assert_eq!(plan.pass_plan.violated_dag_edge_count, 0);
1668 assert!(plan.required_pass_ids.contains(&"light-dark-lowering"));
1669 assert!(plan.required_pass_ids.contains(&"color-mix-lowering"));
1670 assert!(plan.required_pass_ids.contains(&"vendor-prefixing"));
1671 assert!(plan.required_pass_ids.contains(&"scope-flatten"));
1672 assert!(plan.required_pass_ids.contains(&"layer-flatten"));
1673 let vendor_index = plan
1674 .planned_pass_ids
1675 .iter()
1676 .position(|id| *id == "vendor-prefixing");
1677 let light_dark_index = plan
1678 .planned_pass_ids
1679 .iter()
1680 .position(|id| *id == "light-dark-lowering");
1681 assert!(light_dark_index < vendor_index);
1682 }
1683
1684 #[test]
1685 fn blocks_opt_in_flattening_when_not_explicitly_enabled() {
1686 let support = TargetFeatureSupportV0 {
1687 supports_css_scope: false,
1688 supports_cascade_layers: false,
1689 ..super::modern_feature_support()
1690 };
1691
1692 let plan = plan_target_transforms(
1693 "modern-without-scope",
1694 support,
1695 conservative_target_options(),
1696 );
1697
1698 assert!(plan.blocked_pass_ids.contains(&"scope-flatten"));
1699 assert!(plan.blocked_pass_ids.contains(&"layer-flatten"));
1700 assert!(!plan.required_pass_ids.contains(&"scope-flatten"));
1701 assert!(!plan.required_pass_ids.contains(&"layer-flatten"));
1702 }
1703
1704 #[test]
1705 fn plans_dark_mode_media_drop_as_dead_media_branch_pass() {
1706 let mut options = conservative_target_options();
1707 options.drop_dark_mode_media_queries = true;
1708
1709 let plan = plan_target_transforms("modern", super::modern_feature_support(), options);
1710
1711 assert!(
1712 plan.required_pass_ids
1713 .contains(&"dead-media-branch-removal")
1714 );
1715 assert!(plan.planned_pass_ids.contains(&"dead-media-branch-removal"));
1716 }
1717
1718 #[test]
1719 fn plans_stale_prefix_removal_for_targets_that_do_not_need_prefixes() {
1720 let plan = plan_target_transforms(
1721 "modern",
1722 super::modern_feature_support(),
1723 conservative_target_options(),
1724 );
1725
1726 assert!(plan.required_pass_ids.contains(&"stale-prefix-removal"));
1727 assert!(!plan.required_pass_ids.contains(&"vendor-prefixing"));
1728 assert!(plan.planned_pass_ids.contains(&"stale-prefix-removal"));
1729 }
1730
1731 #[test]
1732 fn plans_target_lowering_from_static_target_query_profiles() {
1733 let options = TargetTransformOptionsV0 {
1734 allow_logical_to_physical: true,
1735 allow_scope_flatten: true,
1736 allow_layer_flatten: true,
1737 enable_supports_static_eval: true,
1738 enable_media_static_eval: true,
1739 enable_container_static_eval: false,
1740 drop_dark_mode_media_queries: false,
1741 };
1742 let plan = plan_target_transforms_from_query("legacy-webview", options);
1743
1744 assert!(plan.recognized_profile);
1745 assert_eq!(plan.normalized_query, "legacy-webview");
1746 assert_eq!(plan.profile_id, "legacy-webview");
1747 assert!(plan.support.vendor_prefix_required);
1748 assert_eq!(
1749 plan.vendor_prefix_policy,
1750 Some(super::TransformVendorPrefixPolicyV0::conservative())
1751 );
1752 assert_eq!(plan.transform_plan.pass_plan.violated_dag_edge_count, 0);
1753 assert!(
1754 plan.transform_plan
1755 .required_pass_ids
1756 .contains(&"vendor-prefixing")
1757 );
1758 assert!(
1759 plan.transform_plan
1760 .required_pass_ids
1761 .contains(&"nesting-unwrap")
1762 );
1763 assert!(
1764 plan.transform_plan
1765 .required_pass_ids
1766 .contains(&"logical-to-physical")
1767 );
1768
1769 let modern = plan_target_transforms_from_query("modern", conservative_target_options());
1770 assert!(modern.recognized_profile);
1771 assert_eq!(modern.profile_id, "modern-evergreen");
1772 assert_eq!(modern.target_data_source, "staticTargetProfileV0");
1773 assert_eq!(
1774 modern.target_data_contract_id,
1775 super::TARGET_DATA_CONTRACT_ID
1776 );
1777 assert_eq!(
1778 modern.target_data_snapshot_id,
1779 "omena-transform-target-data-v0:thresholds-2026-07-19:bindings-2026-07-19"
1780 );
1781 assert!(modern.target_data_evidence.is_empty());
1782 assert_eq!(
1783 modern.vendor_prefix_policy,
1784 Some(super::TransformVendorPrefixPolicyV0::none())
1785 );
1786 assert_eq!(
1787 modern.transform_plan.required_pass_ids,
1788 vec!["stale-prefix-removal"]
1789 );
1790 }
1791
1792 #[test]
1793 fn plans_target_lowering_from_resolved_browserslist_query() {
1794 let options = TargetTransformOptionsV0 {
1795 allow_logical_to_physical: true,
1796 allow_scope_flatten: true,
1797 allow_layer_flatten: true,
1798 enable_supports_static_eval: false,
1799 enable_media_static_eval: false,
1800 enable_container_static_eval: false,
1801 drop_dark_mode_media_queries: false,
1802 };
1803 let plan = plan_target_transforms_from_query("ie 11", options);
1804
1805 assert!(plan.recognized_profile);
1806 assert_eq!(plan.profile_id, "browserslist-resolved");
1807 assert_eq!(
1808 plan.target_data_source,
1809 "oxcBrowserslistV3+browserThresholdsTomlV0+generatedFeatureMatrixV0"
1810 );
1811 assert_eq!(plan.resolved_targets, vec!["ie 11"]);
1812 assert_eq!(plan.resolution_error, None);
1813 assert_eq!(plan.target_data_contract_id, super::TARGET_DATA_CONTRACT_ID);
1814 assert_eq!(
1815 plan.target_data_snapshot_id,
1816 "omena-transform-target-data-v0:thresholds-2026-07-19:bindings-2026-07-19"
1817 );
1818 assert_eq!(plan.target_data_evidence.len(), 13);
1819 assert_eq!(
1820 plan.vendor_prefix_policy,
1821 Some(super::TransformVendorPrefixPolicyV0 {
1822 webkit: false,
1823 moz: false,
1824 ms: true,
1825 })
1826 );
1827 assert!(
1828 plan.target_data_evidence
1829 .iter()
1830 .any(|evidence| evidence.support_table == "light_dark"
1831 && evidence.caniuse_keys == vec!["css-light-dark-function".to_string()]
1832 && evidence.source_quorum
1833 == vec![
1834 "caniuse".to_string(),
1835 "web-features".to_string(),
1836 "mdn-bcd".to_string()
1837 ]
1838 && evidence.last_verified == vec!["2026-07-19".to_string()])
1839 );
1840 assert!(
1841 plan.target_data_evidence
1842 .iter()
1843 .any(|evidence| evidence.support_table == "css_nesting"
1844 && evidence.caniuse_keys == vec!["css-nesting".to_string()]
1845 && !evidence.all_resolved_targets_supported)
1846 );
1847 assert!(
1848 plan.target_data_evidence
1849 .iter()
1850 .any(|evidence| evidence.support_table == "cascade_layers"
1851 && evidence.caniuse_keys == vec!["css-cascade-layers".to_string()]
1852 && !evidence.all_resolved_targets_supported)
1853 );
1854 assert!(
1855 plan.target_data_evidence
1856 .iter()
1857 .any(|evidence| evidence.support_table == "color_function"
1858 && evidence.caniuse_keys == vec!["css-color-function".to_string()]
1859 && !evidence.all_resolved_targets_supported)
1860 );
1861 assert!(
1862 plan.target_data_evidence
1863 .iter()
1864 .any(|evidence| evidence.support_table == "css_scope"
1865 && evidence.caniuse_keys == vec!["css-cascade-scope".to_string()]
1866 && !evidence.all_resolved_targets_supported)
1867 );
1868 assert!(
1869 plan.target_data_evidence
1870 .iter()
1871 .any(|evidence| evidence.pass_id == "stale-prefix-removal"
1872 && evidence.support_table == "flexbox"
1873 && evidence.caniuse_keys == vec!["flexbox".to_string()]
1874 && evidence.source_quorum
1875 == vec![
1876 "caniuse".to_string(),
1877 "web-features".to_string(),
1878 "mdn-bcd".to_string()
1879 ])
1880 );
1881 assert!(
1882 plan.target_data_evidence
1883 .iter()
1884 .any(|evidence| evidence.pass_id == "stale-prefix-removal"
1885 && evidence.support_table == "sticky_positioning"
1886 && evidence.caniuse_keys == vec!["css-sticky".to_string()]
1887 && evidence.source_quorum
1888 == vec![
1889 "caniuse".to_string(),
1890 "web-features".to_string(),
1891 "mdn-bcd".to_string()
1892 ])
1893 );
1894 assert!(plan.support.vendor_prefix_required);
1895 assert!(
1896 plan.transform_plan
1897 .required_pass_ids
1898 .contains(&"vendor-prefixing")
1899 );
1900 assert!(
1901 plan.transform_plan
1902 .required_pass_ids
1903 .contains(&"nesting-unwrap")
1904 );
1905 assert!(
1906 plan.transform_plan
1907 .required_pass_ids
1908 .contains(&"logical-to-physical")
1909 );
1910 }
1911
1912 #[test]
1913 fn resolves_target_features_from_static_compatibility_matrix() {
1914 let chrome_110 =
1915 plan_target_transforms_from_query("chrome 110", conservative_target_options());
1916 assert!(!chrome_110.support.supports_color_function);
1917 assert!(
1918 chrome_110
1919 .transform_plan
1920 .required_pass_ids
1921 .contains(&"color-function-lowering")
1922 );
1923
1924 let chrome_111 =
1925 plan_target_transforms_from_query("chrome 111", conservative_target_options());
1926 assert!(chrome_111.support.supports_color_function);
1927 assert!(!chrome_111.support.supports_css_nesting);
1928 assert!(
1929 !chrome_111
1930 .transform_plan
1931 .required_pass_ids
1932 .contains(&"color-function-lowering")
1933 );
1934 assert!(
1935 chrome_111
1936 .transform_plan
1937 .required_pass_ids
1938 .contains(&"nesting-unwrap")
1939 );
1940
1941 let chrome_119 =
1942 plan_target_transforms_from_query("chrome 119", conservative_target_options());
1943 assert!(!chrome_119.support.supports_css_nesting);
1944 assert!(
1945 chrome_119
1946 .transform_plan
1947 .required_pass_ids
1948 .contains(&"nesting-unwrap")
1949 );
1950
1951 let chrome_120 =
1952 plan_target_transforms_from_query("chrome 120", conservative_target_options());
1953 assert!(chrome_120.support.supports_css_nesting);
1954 assert!(
1955 !chrome_120
1956 .transform_plan
1957 .required_pass_ids
1958 .contains(&"nesting-unwrap")
1959 );
1960
1961 let chrome_98 =
1962 plan_target_transforms_from_query("chrome 98", conservative_target_options());
1963 assert!(!chrome_98.support.supports_cascade_layers);
1964
1965 let chrome_99 =
1966 plan_target_transforms_from_query("chrome 99", conservative_target_options());
1967 assert!(chrome_99.support.supports_cascade_layers);
1968
1969 let chrome_117 =
1970 plan_target_transforms_from_query("chrome 117", conservative_target_options());
1971 assert!(!chrome_117.support.supports_css_scope);
1972
1973 let chrome_118 =
1974 plan_target_transforms_from_query("chrome 118", conservative_target_options());
1975 assert!(chrome_118.support.supports_css_scope);
1976
1977 let chrome_122 =
1978 plan_target_transforms_from_query("chrome 122", conservative_target_options());
1979 assert_eq!(chrome_122.profile_id, "browserslist-resolved");
1980 assert!(!chrome_122.support.supports_light_dark);
1981 assert!(chrome_122.support.supports_color_mix);
1982 assert!(
1983 chrome_122
1984 .transform_plan
1985 .required_pass_ids
1986 .contains(&"light-dark-lowering")
1987 );
1988 assert!(
1989 !chrome_122
1990 .transform_plan
1991 .required_pass_ids
1992 .contains(&"color-mix-lowering")
1993 );
1994
1995 let chrome_123 =
1996 plan_target_transforms_from_query("chrome 123", conservative_target_options());
1997 assert!(chrome_123.support.supports_light_dark);
1998 assert!(chrome_123.support.supports_color_mix);
1999 assert!(
2000 !chrome_123
2001 .transform_plan
2002 .required_pass_ids
2003 .contains(&"light-dark-lowering")
2004 );
2005 assert!(
2006 !chrome_123
2007 .transform_plan
2008 .required_pass_ids
2009 .contains(&"color-mix-lowering")
2010 );
2011
2012 let safari_16_2 =
2013 plan_target_transforms_from_query("safari 16.2", conservative_target_options());
2014 assert!(!safari_16_2.support.supports_light_dark);
2015 assert!(safari_16_2.support.supports_color_mix);
2016 assert!(!safari_16_2.support.supports_css_nesting);
2017
2018 let safari_17_5 =
2019 plan_target_transforms_from_query("safari 17.5", conservative_target_options());
2020 assert!(safari_17_5.support.supports_light_dark);
2021 assert!(safari_17_5.support.supports_color_mix);
2022 assert!(safari_17_5.support.supports_css_nesting);
2023 }
2024
2025 #[test]
2026 fn query_plan_evidence_explains_threshold_support_for_resolved_targets() {
2027 let chrome_122 =
2028 plan_target_transforms_from_query("chrome 122", conservative_target_options());
2029
2030 let light_dark = chrome_122
2031 .target_data_evidence
2032 .iter()
2033 .find(|evidence| evidence.support_table == "light_dark");
2034 assert!(
2035 light_dark.is_some(),
2036 "light-dark target data evidence should be present"
2037 );
2038 let Some(light_dark) = light_dark else {
2039 return;
2040 };
2041 assert_eq!(light_dark.pass_id, "light-dark-lowering");
2042 assert_eq!(
2043 light_dark.caniuse_keys,
2044 vec!["css-light-dark-function".to_string()]
2045 );
2046 assert_eq!(
2047 light_dark.source_quorum,
2048 vec![
2049 "caniuse".to_string(),
2050 "web-features".to_string(),
2051 "mdn-bcd".to_string()
2052 ]
2053 );
2054 assert_eq!(light_dark.last_verified, vec!["2026-07-19".to_string()]);
2055 assert!(!light_dark.all_resolved_targets_supported);
2056
2057 let chrome_light_dark = light_dark
2058 .resolved_targets
2059 .iter()
2060 .find(|target| target.browser == "chrome");
2061 assert!(
2062 chrome_light_dark.is_some(),
2063 "chrome target evidence should be present"
2064 );
2065 let Some(chrome_light_dark) = chrome_light_dark else {
2066 return;
2067 };
2068 assert_eq!(chrome_light_dark.version, "122");
2069 assert!(!chrome_light_dark.supported);
2070 let light_dark_threshold = chrome_light_dark.matched_threshold.as_ref();
2071 assert!(
2072 light_dark_threshold.is_some(),
2073 "chrome light-dark threshold should be present"
2074 );
2075 let Some(light_dark_threshold) = light_dark_threshold else {
2076 return;
2077 };
2078 assert_eq!(light_dark_threshold.min_version, "123.0");
2079 assert_eq!(light_dark_threshold.caniuse_key, "css-light-dark-function");
2080
2081 let color_mix = chrome_122
2082 .target_data_evidence
2083 .iter()
2084 .find(|evidence| evidence.support_table == "color_mix");
2085 assert!(
2086 color_mix.is_some(),
2087 "color-mix target data evidence should be present"
2088 );
2089 let Some(color_mix) = color_mix else {
2090 return;
2091 };
2092 assert!(color_mix.all_resolved_targets_supported);
2093 let chrome_color_mix = color_mix
2094 .resolved_targets
2095 .iter()
2096 .find(|target| target.browser == "chrome");
2097 assert!(
2098 chrome_color_mix.is_some(),
2099 "chrome target evidence should be present"
2100 );
2101 let Some(chrome_color_mix) = chrome_color_mix else {
2102 return;
2103 };
2104 assert!(chrome_color_mix.supported);
2105 let color_mix_threshold = chrome_color_mix.matched_threshold.as_ref();
2106 assert!(
2107 color_mix_threshold.is_some(),
2108 "chrome color-mix threshold should be present"
2109 );
2110 let Some(color_mix_threshold) = color_mix_threshold else {
2111 return;
2112 };
2113 assert_eq!(color_mix_threshold.min_version, "111.0");
2114 assert_eq!(color_mix_threshold.caniuse_key, "css-color-mix");
2115
2116 let color_function = chrome_122
2117 .target_data_evidence
2118 .iter()
2119 .find(|evidence| evidence.support_table == "color_function");
2120 assert!(
2121 color_function.is_some(),
2122 "color() target data evidence should be present"
2123 );
2124 let Some(color_function) = color_function else {
2125 return;
2126 };
2127 assert_eq!(color_function.pass_id, "color-function-lowering");
2128 assert_eq!(
2129 color_function.caniuse_keys,
2130 vec!["css-color-function".to_string()]
2131 );
2132 assert!(color_function.all_resolved_targets_supported);
2133 let chrome_color_function = color_function
2134 .resolved_targets
2135 .iter()
2136 .find(|target| target.browser == "chrome");
2137 assert!(
2138 chrome_color_function.is_some(),
2139 "chrome color() target evidence should be present"
2140 );
2141 let Some(chrome_color_function) = chrome_color_function else {
2142 return;
2143 };
2144 assert!(chrome_color_function.supported);
2145 let color_function_threshold = chrome_color_function.matched_threshold.as_ref();
2146 assert!(
2147 color_function_threshold.is_some(),
2148 "chrome color() threshold should be present"
2149 );
2150 let Some(color_function_threshold) = color_function_threshold else {
2151 return;
2152 };
2153 assert_eq!(color_function_threshold.min_version, "111.0");
2154 assert_eq!(color_function_threshold.caniuse_key, "css-color-function");
2155
2156 let css_nesting = chrome_122
2157 .target_data_evidence
2158 .iter()
2159 .find(|evidence| evidence.support_table == "css_nesting");
2160 assert!(
2161 css_nesting.is_some(),
2162 "css nesting target data evidence should be present"
2163 );
2164 let Some(css_nesting) = css_nesting else {
2165 return;
2166 };
2167 assert_eq!(css_nesting.pass_id, "nesting-unwrap");
2168 assert_eq!(css_nesting.caniuse_keys, vec!["css-nesting".to_string()]);
2169 assert!(css_nesting.all_resolved_targets_supported);
2170 let chrome_css_nesting = css_nesting
2171 .resolved_targets
2172 .iter()
2173 .find(|target| target.browser == "chrome");
2174 assert!(
2175 chrome_css_nesting.is_some(),
2176 "chrome nesting target evidence should be present"
2177 );
2178 let Some(chrome_css_nesting) = chrome_css_nesting else {
2179 return;
2180 };
2181 assert!(chrome_css_nesting.supported);
2182 let css_nesting_threshold = chrome_css_nesting.matched_threshold.as_ref();
2183 assert!(
2184 css_nesting_threshold.is_some(),
2185 "chrome nesting threshold should be present"
2186 );
2187 let Some(css_nesting_threshold) = css_nesting_threshold else {
2188 return;
2189 };
2190 assert_eq!(css_nesting_threshold.min_version, "120.0");
2191 assert_eq!(css_nesting_threshold.caniuse_key, "css-nesting");
2192
2193 let cascade_layers = chrome_122
2194 .target_data_evidence
2195 .iter()
2196 .find(|evidence| evidence.support_table == "cascade_layers");
2197 assert!(
2198 cascade_layers.is_some(),
2199 "cascade layers target data evidence should be present"
2200 );
2201 let Some(cascade_layers) = cascade_layers else {
2202 return;
2203 };
2204 assert_eq!(cascade_layers.pass_id, "layer-flatten");
2205 assert_eq!(
2206 cascade_layers.caniuse_keys,
2207 vec!["css-cascade-layers".to_string()]
2208 );
2209 assert!(cascade_layers.all_resolved_targets_supported);
2210 let chrome_cascade_layers = cascade_layers
2211 .resolved_targets
2212 .iter()
2213 .find(|target| target.browser == "chrome");
2214 assert!(
2215 chrome_cascade_layers.is_some(),
2216 "chrome cascade layers target evidence should be present"
2217 );
2218 let Some(chrome_cascade_layers) = chrome_cascade_layers else {
2219 return;
2220 };
2221 assert!(chrome_cascade_layers.supported);
2222 let cascade_layers_threshold = chrome_cascade_layers.matched_threshold.as_ref();
2223 assert!(
2224 cascade_layers_threshold.is_some(),
2225 "chrome cascade layers threshold should be present"
2226 );
2227 let Some(cascade_layers_threshold) = cascade_layers_threshold else {
2228 return;
2229 };
2230 assert_eq!(cascade_layers_threshold.min_version, "99.0");
2231 assert_eq!(cascade_layers_threshold.caniuse_key, "css-cascade-layers");
2232
2233 let css_scope = chrome_122
2234 .target_data_evidence
2235 .iter()
2236 .find(|evidence| evidence.support_table == "css_scope");
2237 assert!(
2238 css_scope.is_some(),
2239 "css scope target data evidence should be present"
2240 );
2241 let Some(css_scope) = css_scope else {
2242 return;
2243 };
2244 assert_eq!(css_scope.pass_id, "scope-flatten");
2245 assert_eq!(
2246 css_scope.caniuse_keys,
2247 vec!["css-cascade-scope".to_string()]
2248 );
2249 assert!(css_scope.all_resolved_targets_supported);
2250 let chrome_css_scope = css_scope
2251 .resolved_targets
2252 .iter()
2253 .find(|target| target.browser == "chrome");
2254 assert!(
2255 chrome_css_scope.is_some(),
2256 "chrome scope target evidence should be present"
2257 );
2258 let Some(chrome_css_scope) = chrome_css_scope else {
2259 return;
2260 };
2261 assert!(chrome_css_scope.supported);
2262 let css_scope_threshold = chrome_css_scope.matched_threshold.as_ref();
2263 assert!(
2264 css_scope_threshold.is_some(),
2265 "chrome scope threshold should be present"
2266 );
2267 let Some(css_scope_threshold) = css_scope_threshold else {
2268 return;
2269 };
2270 assert_eq!(css_scope_threshold.min_version, "118.0");
2271 assert_eq!(css_scope_threshold.caniuse_key, "css-cascade-scope");
2272 }
2273
2274 #[test]
2275 fn resolved_multi_target_queries_fold_to_the_least_supported_feature_set() {
2276 let mixed_targets = plan_target_transforms_from_query(
2277 "chrome 123, safari 16.2",
2278 conservative_target_options(),
2279 );
2280
2281 assert_eq!(mixed_targets.profile_id, "browserslist-resolved");
2282 assert_eq!(
2283 mixed_targets.resolved_targets,
2284 vec!["chrome 123", "safari 16.2"]
2285 );
2286 assert!(
2287 !mixed_targets.support.supports_light_dark,
2288 "safari 16.2 keeps the multi-target set below the light-dark threshold"
2289 );
2290 assert!(mixed_targets.support.supports_color_mix);
2291 assert!(!mixed_targets.support.supports_css_nesting);
2292 assert!(
2293 mixed_targets
2294 .transform_plan
2295 .required_pass_ids
2296 .contains(&"light-dark-lowering")
2297 );
2298 assert!(
2299 !mixed_targets
2300 .transform_plan
2301 .required_pass_ids
2302 .contains(&"color-mix-lowering")
2303 );
2304 assert!(
2305 mixed_targets
2306 .transform_plan
2307 .required_pass_ids
2308 .contains(&"nesting-unwrap")
2309 );
2310 }
2311
2312 #[test]
2313 fn invalid_target_query_uses_conservative_profile_without_claiming_recognition() {
2314 let plan = plan_target_transforms_from_query("yuru 1.0", conservative_target_options());
2315
2316 assert!(!plan.recognized_profile);
2317 assert_eq!(plan.profile_id, "unknown-conservative");
2318 assert!(plan.resolution_error.is_some());
2319 assert!(plan.support.vendor_prefix_required);
2320 assert!(
2321 plan.transform_plan
2322 .required_pass_ids
2323 .contains(&"vendor-prefixing")
2324 );
2325 assert!(
2326 plan.transform_plan
2327 .blocked_pass_ids
2328 .contains(&"scope-flatten")
2329 );
2330 }
2331
2332 #[test]
2333 fn browser_data_contract_rejects_stale_threshold_entries() {
2334 let mut browser_data = super::browser_threshold_data().clone();
2335 browser_data.thresholds[0].last_verified = "2026-05-01".to_string();
2336
2337 assert_eq!(super::browser_threshold_stale_entry_count(&browser_data), 1);
2338 assert!(!super::browser_threshold_data_is_valid(&browser_data));
2339
2340 let contract =
2341 super::target_data_contract_summary(&browser_data, super::pass_feature_binding_data());
2342 assert_eq!(contract.stale_entry_count, 1);
2343 assert!(!contract.valid);
2344 }
2345
2346 #[test]
2347 fn browser_data_contract_rejects_unresolvable_threshold_queries() {
2348 let mut browser_data = super::browser_threshold_data().clone();
2349 browser_data.thresholds[0].browser = "android".to_string();
2350 browser_data.thresholds[0].min_major = 148;
2351 browser_data.thresholds[0].min_minor = 0;
2352
2353 assert_eq!(super::unresolvable_threshold_query_count(&browser_data), 1);
2354 assert!(!super::browser_threshold_data_is_valid(&browser_data));
2355
2356 let contract =
2357 super::target_data_contract_summary(&browser_data, super::pass_feature_binding_data());
2358 assert_eq!(contract.unresolvable_threshold_query_count, 1);
2359 assert!(!contract.valid);
2360 }
2361
2362 #[test]
2363 fn browser_data_contract_requires_three_source_quorum_minimum() {
2364 let mut browser_data = super::browser_threshold_data().clone();
2365 browser_data.quorum_min_sources = 2;
2366
2367 assert!(!super::browser_threshold_data_is_valid(&browser_data));
2368
2369 let contract =
2370 super::target_data_contract_summary(&browser_data, super::pass_feature_binding_data());
2371 assert_eq!(contract.quorum_min_sources, 2);
2372 assert!(!contract.quorum_valid);
2373 assert!(!contract.valid);
2374 }
2375
2376 #[test]
2377 fn browser_data_contract_rejects_incomplete_threshold_source_quorum() {
2378 let mut browser_data = super::browser_threshold_data().clone();
2379 browser_data.thresholds[0].source_quorum =
2380 vec!["caniuse".to_string(), "web-features".to_string()];
2381
2382 assert!(!super::browser_threshold_data_is_valid(&browser_data));
2383
2384 let contract =
2385 super::target_data_contract_summary(&browser_data, super::pass_feature_binding_data());
2386 assert!(!contract.quorum_valid);
2387 assert!(!contract.valid);
2388 }
2389
2390 #[test]
2391 fn browser_data_contract_rejects_threshold_source_policy_drift() {
2392 let mut browser_data = super::browser_threshold_data().clone();
2393 browser_data.threshold_source_policy = "manualThresholdSelection".to_string();
2394
2395 assert!(!super::browser_threshold_data_is_valid(&browser_data));
2396
2397 let contract =
2398 super::target_data_contract_summary(&browser_data, super::pass_feature_binding_data());
2399 assert_eq!(contract.threshold_source_policy, "manualThresholdSelection");
2400 assert!(!contract.quorum_valid);
2401 assert!(!contract.valid);
2402 }
2403
2404 #[test]
2405 fn browser_data_contract_rejects_resolver_provenance_drift() {
2406 let browser_data = super::browser_threshold_data().clone();
2407 let mut bindings = super::pass_feature_binding_data().clone();
2408 bindings.caniuse_resolver_cargo_version = "0.0.0".to_string();
2409
2410 assert!(!super::resolver_provenance_is_valid(
2411 &browser_data,
2412 &bindings
2413 ));
2414
2415 let contract = super::target_data_contract_summary(&browser_data, &bindings);
2416 assert!(!contract.resolver_provenance_valid);
2417 assert!(!contract.valid);
2418 }
2419
2420 #[test]
2421 fn browser_data_contract_rejects_unmapped_threshold_tables() {
2422 let mut browser_data = super::browser_threshold_data().clone();
2423 browser_data
2424 .thresholds
2425 .push(super::BrowserFeatureThresholdV0 {
2426 table: "unmapped_feature".to_string(),
2427 browser: "chrome".to_string(),
2428 min_major: 123,
2429 min_minor: 0,
2430 caniuse_key: "css-unmapped-feature".to_string(),
2431 source_quorum: vec![
2432 "caniuse".to_string(),
2433 "web-features".to_string(),
2434 "mdn-bcd".to_string(),
2435 ],
2436 last_verified: browser_data.refreshed_at.clone(),
2437 });
2438 let bindings = super::pass_feature_binding_data().clone();
2439
2440 assert_eq!(
2441 super::unmapped_threshold_table_count(&browser_data, &bindings),
2442 1
2443 );
2444 assert!(super::browser_threshold_data_is_valid(&browser_data));
2445 assert!(!super::pass_feature_binding_data_is_valid(
2446 &browser_data,
2447 &bindings
2448 ));
2449
2450 let contract = super::target_data_contract_summary(&browser_data, &bindings);
2451 assert_eq!(contract.unmapped_threshold_table_count, 1);
2452 assert!(!contract.valid);
2453 }
2454}