Skip to main content

omena_transform_target/
lib.rs

1//! Target feature matrix planning for Omena CSS transforms.
2//!
3//! This crate owns the target-sensitive lowering decision boundary. It
4//! resolves standard browserslist queries through an embedded Can I Use snapshot,
5//! then folds the resolved browser set into the explicit Omena transform feature
6//! matrix. Named profiles stay available for product defaults and conservative
7//! non-browser environments.
8
9use 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}