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