Skip to main content

omena_query_transform_runner/
lib.rs

1//! Transform-runner boundary for the `omena-query` facade.
2//!
3//! `omena-query` remains the consumer-facing analysis facade, but it should not
4//! directly depend on every transform-family crate. This boundary groups the
5//! transform planner, executor, target planner, printer, and egg witness surface
6//! behind one dependency while preserving the existing query API.
7
8use serde::Serialize;
9
10mod plugin_api;
11mod plugins;
12
13pub use plugin_api::*;
14pub use plugins::{built_in_omena_plugins, execute_built_in_omena_plugin};
15
16#[allow(deprecated)]
17pub use omena_bundler::{
18    BundleDependencyResolutionDisclosureV0, BundleResolutionAuthorityV0, EmissionCycleClassV0,
19    EmissionCycleDialectV0, EmissionOrderingPolicyV0, InstanceReachabilityDerivationV0,
20    LinkedEmissionArtifactV0, LinkedEmissionItemMaterializationErrorV0,
21    LinkedEmissionMaterializationErrorV0, LinkedEmissionModuleRegionV0,
22    LinkedEmissionOrderEntryRegionV0, LinkedStylesheetV0, LinkedStylesheetWithEmissionItemsV0,
23    TransformBundleAssetUrlRewriteSummaryV0, TransformBundleDependencyResolutionV0,
24    TransformBundleEdgeKind, TransformBundleEmissionAdmissionV0,
25    TransformBundleEmissionItemProjectionV0, TransformBundleInstanceReachabilityInputV0,
26    TransformBundleLinkErrorV0, TransformBundleLinkOptionsV0, TransformBundleLinkProjectionSetV0,
27    TransformBundleLinkerProjectionV0, TransformBundleModuleInputV0,
28    TransformBundleParsedModuleInputV0, TransformBundleReachabilityAnalysisV0,
29    TransformBundleReachabilityUnanalyzedCauseV0, TransformBundleResolvedDependencyV0,
30    TransformBundleSemanticReachabilityInputV0, TransformBundleSourceSummaryV0,
31    TransformBundleTransformedModuleV0, bundle_edge_is_module_dependency,
32    evaluate_omena_transform_bundle_projection_emission_admission_with_resolved_dependencies_and_options,
33    link_legacy_path_inferred_bundle, link_omena_transform_bundle_modules,
34    link_omena_transform_bundle_modules_with_options,
35    link_omena_transform_bundle_modules_with_semantic_reachability,
36    link_omena_transform_bundle_modules_with_semantic_reachability_and_metadata,
37    link_omena_transform_bundle_projection_with_emission_items_and_resolved_dependencies_and_options,
38    link_omena_transform_bundle_projection_with_resolved_dependencies_and_options,
39    link_resolved_bundle, materialize_omena_transform_bundle_linked_stylesheet,
40    materialize_omena_transform_bundle_linked_stylesheet_with_emission_items,
41    normalize_omena_transform_bundle_path,
42    project_omena_transform_bundle_linker_and_emission_items,
43    project_omena_transform_bundle_linker_and_emission_items_from_parsed_modules,
44    project_omena_transform_bundle_linker_and_emission_items_from_parsed_modules_with_instance_reachability,
45    project_omena_transform_bundle_linker_inputs,
46    project_omena_transform_bundle_linker_inputs_from_parsed_modules,
47    rewrite_omena_transform_bundle_asset_urls_in_source,
48    summarize_omena_transform_bundle_from_source,
49};
50pub use omena_transform_cst::{
51    MinifyPassClassificationV0, MinifyPassProfileClassV0,
52    NATIVE_CSS_STATIC_EVAL_DIALECT_RESTRICTION_V0, NATIVE_CSS_STATIC_EVAL_OPT_IN_POLICY_V0,
53    NATIVE_CSS_STATIC_EVAL_SPEC_SNAPSHOT_V0, STRICT_VERIFICATION_BUILD_PROFILE_ID_V0,
54    TRANSFORM_PASS_CATALOG_LEN, TransformBuildProfileV0, TransformPassKind,
55    TransformStrictPolicyDescriptorV0, all_transform_pass_kinds, closed_world_minify_build_profile,
56    default_minify_build_profiles, default_minify_pass_classifications, safe_minify_build_profile,
57    semantic_minify_build_profile, strict_policy_descriptor_for_profile,
58    strict_verification_build_profile, transform_pass_requires_closed_world_bundle,
59    transform_pass_sort_ordinal, with_transform_pass_sort_ordinal_overrides_for_test,
60};
61pub use omena_transform_egg::{
62    EggRewriteSourceWitnessV0, TransformEggPlanV0, execute_egg_rewrite_witnesses_for_css_source,
63    plan_egg_rewrite_passes_for_source,
64};
65pub use omena_transform_passes::{
66    CssModuleTokenCollisionPathScopeV0, CssModuleTokenCollisionV0,
67    CssModuleTokenInterfaceMismatchV0, CssModuleTokenOwnershipCensusV0, CssModuleTokenOwnershipV0,
68    CustomPropertyLeastFixedPointSummaryV0, ExternalCssCstCoverageV0,
69    ExternalCssSemanticChangeClassificationV0, ExternalCssSemanticChangeKindV0,
70    ExternalCssSemanticChangeV0, ExternalCssSemanticDiffV0, ExternalCssSemanticEntryV0,
71    RollbackReceiptV0, RollbackScopeV0, TransformCascadeEnvironmentDeclarationV0,
72    TransformCascadeEnvironmentV0, TransformClassNameRewriteV0,
73    TransformCssModuleComposesResolutionV0, TransformCssModuleValueResolutionV0, TransformDecision,
74    TransformDesignTokenRouteV0, TransformExecutionContextV0, TransformExecutionPolicyV0,
75    TransformExecutionSummaryV0, TransformImportInlineV0, TransformLessInlineLiteralPlaceholderV0,
76    TransformModuleCssModuleContextV0, TransformModuleEvaluationNativeEditV0,
77    TransformModuleEvaluationNativeReplacementV0, TransformModuleEvaluationOracleV0,
78    TransformModuleEvaluationV0, TransformModuleQualifiedExecutionErrorV0,
79    TransformModuleQualifiedShakeSummaryV0, TransformPassExecutionOutcomeV0, TransformPassPlanV0,
80    TransformSemanticGuaranteeTierV0, TransformSemanticPreservationDecisionV0,
81    TransformStrictPolicyEventV0, TransformStrictPolicyReasonV0, TransformStrictPolicySummaryV0,
82    TransformVendorPrefixPolicyV0, TransformWinnerEqualityAbsenceReasonV0,
83    TransformWinnerEqualityAbsenceV0, TransformWinnerEqualityAxisV0,
84    classify_transform_reachability_precision, compare_external_css_semantic_changes_v0,
85    compare_transform_css_semantics_v0,
86    execute_transform_passes_on_module_with_dialect_context_and_closed_world_bundle,
87    execute_transform_passes_on_module_with_dialect_context_policy_and_closed_world_bundle,
88    execute_transform_passes_on_module_with_dialect_context_policy_and_closed_world_bundle_and_retained_class_names,
89    execute_transform_passes_on_source_with_dialect_and_context,
90    execute_transform_passes_on_source_with_dialect_context_and_closed_world_bundle,
91    execute_transform_passes_on_source_with_dialect_context_and_policy,
92    execute_transform_passes_on_source_with_dialect_context_closed_world_bundle_and_precision,
93    execute_transform_passes_on_source_with_dialect_context_closed_world_bundle_precision_and_policy,
94    expand_css_nested_selector, external_css_adoption_boundary_is_complete_v0,
95    external_css_semantic_diff_is_total_v0, inline_css_imports,
96    inline_css_imports_for_static_module_evaluation, parse_static_css_cascade_value,
97    plan_transform_passes, reduce_static_numeric_expression,
98    resolve_static_css_modules_local_value_resolutions_from_source,
99    restore_less_inline_literal_placeholders,
100    summarize_static_css_custom_property_fixed_point_from_source,
101};
102/// Compatibility aliases for the superseded trace name.
103/// Owner: `omena-query-transform-runner` maintainers. Removal condition: not
104/// before 1.0, after downstream migration and zero audited non-compat uses.
105#[cfg(feature = "transform-catalog-trace")]
106#[allow(deprecated)]
107#[deprecated(
108    since = "0.4.0",
109    note = "use the TransformCatalog trace surface; removal is not before 1.0 and requires downstream migration plus zero audited non-compatibility uses"
110)]
111pub use omena_transform_passes::{
112    LawvereDifferentialCommutativityWitnessV0, LawvereModelTraceV0, TransformPassParallelPlanV0,
113    evaluate_lawvere_reorderability_with_differential_corpus,
114    execute_transform_passes_on_source_with_lawvere_trace_and_dialect,
115    plan_transform_passes_parallel_lawvere_layers,
116};
117#[cfg(feature = "transform-catalog-trace")]
118pub use omena_transform_passes::{
119    ReorderabilityCertificateV0, TransformCatalogDifferentialCommutativityWitnessV0,
120    TransformCatalogModelTraceV0, TransformCatalogTransformPassParallelPlanV0,
121    evaluate_transform_catalog_reorderability_with_differential_corpus,
122    execute_transform_passes_on_source_with_transform_catalog_trace_and_dialect,
123    plan_transform_passes_parallel_transform_catalog_layers,
124};
125pub use omena_transform_print::{
126    PrettyFormatOptionsV0, StyleDialect, TransformPrintArtifactV0, TransformPrintMode,
127    TransformPrintOptionsV0, TransformSourceMapCompositionV0, TransformSourceMapPointV0,
128    TransformSourceMapSegmentV0, TransformSourceMapV3V0,
129    compose_transform_source_map_v3_with_upstream_map, default_print_options,
130    print_transform_cst_source_with_dialect_and_pretty_options,
131    print_transform_execution_artifact_with_dialect_and_source, serialize_transform_source_map_v3,
132    serialize_transform_source_map_v3_with_source_contents, transform_source_map_point,
133};
134pub use omena_transform_target::{
135    TargetFeatureSupportV0, TargetTransformOptionsV0, TransformTargetPlanV0,
136    TransformTargetQueryPlanV0, conservative_target_options, modern_feature_support,
137    plan_target_transforms, plan_target_transforms_from_query,
138};
139
140pub const OMENA_QUERY_TRANSFORM_RUNNER_COLLAPSED_CRATES_V0: [&str; 6] = [
141    "omena-bundler",
142    "omena-transform-cst",
143    "omena-transform-egg",
144    "omena-transform-passes",
145    "omena-transform-print",
146    "omena-transform-target",
147];
148
149pub fn materialize_transform_module_evaluation_native_edits(
150    input_css: &str,
151    native_edits: &[TransformModuleEvaluationNativeEditV0],
152) -> Option<String> {
153    if native_edits.is_empty() {
154        return None;
155    }
156
157    let mut edits = native_edits.to_vec();
158    edits.sort_by_key(|edit| edit.start);
159
160    let mut previous_end = 0usize;
161    for edit in &edits {
162        if edit.start < previous_end
163            || edit.start > edit.end
164            || edit.end > input_css.len()
165            || !input_css.is_char_boundary(edit.start)
166            || !input_css.is_char_boundary(edit.end)
167        {
168            return None;
169        }
170        previous_end = edit.end;
171    }
172
173    let mut output = input_css.to_string();
174    for edit in edits.iter().rev() {
175        output.replace_range(edit.start..edit.end, edit.replacement.as_str());
176    }
177    Some(output)
178}
179
180#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
181#[serde(rename_all = "camelCase")]
182pub struct OmenaQueryTransformRunnerBoundaryV0 {
183    pub schema_version: &'static str,
184    pub product: &'static str,
185    pub boundary_kind: &'static str,
186    pub collapsed_transform_crate_count: usize,
187    pub collapsed_transform_crates: Vec<&'static str>,
188    pub direct_query_dependency_replacement: &'static str,
189    pub ready_surfaces: Vec<&'static str>,
190}
191
192pub fn summarize_omena_query_transform_runner_boundary_with_transform_catalog_v0()
193-> OmenaQueryTransformRunnerBoundaryV0 {
194    let ready_surfaces = vec![
195        "transformPlannerBoundary",
196        "transformExecutorBoundary",
197        "transformPrinterBoundary",
198        "transformSourceMapV3SerializerBoundary",
199        "transformTargetPlannerBoundary",
200        "transformEggWitnessBoundary",
201    ];
202    #[cfg(feature = "transform-catalog-trace")]
203    let mut ready_surfaces = ready_surfaces;
204    #[cfg(feature = "transform-catalog-trace")]
205    ready_surfaces.push("transformCatalogTraceBoundary");
206
207    OmenaQueryTransformRunnerBoundaryV0 {
208        schema_version: "0",
209        product: "omena-query-transform-runner.boundary",
210        boundary_kind: "query-transform-runner-split",
211        collapsed_transform_crate_count: OMENA_QUERY_TRANSFORM_RUNNER_COLLAPSED_CRATES_V0.len(),
212        collapsed_transform_crates: OMENA_QUERY_TRANSFORM_RUNNER_COLLAPSED_CRATES_V0.to_vec(),
213        direct_query_dependency_replacement: "omena-query-transform-runner",
214        ready_surfaces,
215    }
216}
217
218#[deprecated(
219    since = "0.4.0",
220    note = "use summarize_omena_query_transform_runner_boundary_with_transform_catalog_v0; removal is not before 1.0 and requires downstream migration plus zero audited non-compatibility uses"
221)]
222pub fn summarize_omena_query_transform_runner_boundary_v0() -> OmenaQueryTransformRunnerBoundaryV0 {
223    #[cfg(feature = "transform-catalog-trace")]
224    let mut summary = summarize_omena_query_transform_runner_boundary_with_transform_catalog_v0();
225    #[cfg(not(feature = "transform-catalog-trace"))]
226    let summary = summarize_omena_query_transform_runner_boundary_with_transform_catalog_v0();
227    #[cfg(feature = "transform-catalog-trace")]
228    if let Some(surface) = summary
229        .ready_surfaces
230        .iter_mut()
231        .find(|surface| **surface == "transformCatalogTraceBoundary")
232    {
233        *surface = "transformLawvereTraceBoundary";
234    }
235    summary
236}
237
238#[cfg(test)]
239mod tests {
240    use super::*;
241
242    /// Exact pre-1.0 transform-runner boundary projection retained for wire
243    /// compatibility. Owner: `omena-query-transform-runner` maintainers.
244    /// Removal is not before 1.0 and requires downstream migration plus zero
245    /// audited non-compatibility uses.
246    #[cfg(feature = "transform-catalog-trace")]
247    #[deprecated(
248        since = "0.4.0",
249        note = "compatibility projection; removal is not before 1.0 and requires downstream migration plus zero audited non-compatibility uses"
250    )]
251    const COMPATIBILITY_TRANSFORM_RUNNER_BOUNDARY_JSON_V0: &str = r#"{"schemaVersion":"0","product":"omena-query-transform-runner.boundary","boundaryKind":"query-transform-runner-split","collapsedTransformCrateCount":6,"collapsedTransformCrates":["omena-bundler","omena-transform-cst","omena-transform-egg","omena-transform-passes","omena-transform-print","omena-transform-target"],"directQueryDependencyReplacement":"omena-query-transform-runner","readySurfaces":["transformPlannerBoundary","transformExecutorBoundary","transformPrinterBoundary","transformSourceMapV3SerializerBoundary","transformTargetPlannerBoundary","transformEggWitnessBoundary","transformLawvereTraceBoundary"]}"#;
252
253    #[test]
254    fn transform_runner_boundary_collapses_transform_family_for_query() {
255        let summary = summarize_omena_query_transform_runner_boundary_with_transform_catalog_v0();
256
257        assert_eq!(summary.collapsed_transform_crate_count, 6);
258        assert_eq!(
259            summary.collapsed_transform_crates,
260            OMENA_QUERY_TRANSFORM_RUNNER_COLLAPSED_CRATES_V0
261        );
262        assert!(
263            summary
264                .ready_surfaces
265                .contains(&"transformExecutorBoundary")
266        );
267        assert!(
268            summary
269                .ready_surfaces
270                .contains(&"transformTargetPlannerBoundary")
271        );
272    }
273
274    #[cfg(feature = "transform-catalog-trace")]
275    #[test]
276    #[allow(deprecated)]
277    fn transform_runner_boundary_preserves_legacy_and_canonical_serialized_projections()
278    -> Result<(), serde_json::Error> {
279        let legacy = serde_json::to_string(&summarize_omena_query_transform_runner_boundary_v0())?;
280        let canonical = serde_json::to_string(
281            &summarize_omena_query_transform_runner_boundary_with_transform_catalog_v0(),
282        )?;
283
284        assert_eq!(legacy, COMPATIBILITY_TRANSFORM_RUNNER_BOUNDARY_JSON_V0);
285        assert_eq!(
286            canonical,
287            r#"{"schemaVersion":"0","product":"omena-query-transform-runner.boundary","boundaryKind":"query-transform-runner-split","collapsedTransformCrateCount":6,"collapsedTransformCrates":["omena-bundler","omena-transform-cst","omena-transform-egg","omena-transform-passes","omena-transform-print","omena-transform-target"],"directQueryDependencyReplacement":"omena-query-transform-runner","readySurfaces":["transformPlannerBoundary","transformExecutorBoundary","transformPrinterBoundary","transformSourceMapV3SerializerBoundary","transformTargetPlannerBoundary","transformEggWitnessBoundary","transformCatalogTraceBoundary"]}"#
288        );
289
290        Ok(())
291    }
292
293    #[test]
294    fn transform_runner_materializes_sorted_native_edits() {
295        let input_css = ".button { color: red; margin: 1px; }";
296        let color_start = input_css.find("red").unwrap_or(input_css.len());
297        let margin_start = input_css.find("1px").unwrap_or(input_css.len());
298
299        let output = materialize_transform_module_evaluation_native_edits(
300            input_css,
301            &[
302                TransformModuleEvaluationNativeEditV0 {
303                    start: margin_start,
304                    end: margin_start + "1px".len(),
305                    replacement: "2px".to_string(),
306                    edit_kind: "value".to_string(),
307                    abstract_value: None,
308                    abstract_value_kind: None,
309                },
310                TransformModuleEvaluationNativeEditV0 {
311                    start: color_start,
312                    end: color_start + "red".len(),
313                    replacement: "blue".to_string(),
314                    edit_kind: "value".to_string(),
315                    abstract_value: None,
316                    abstract_value_kind: None,
317                },
318            ],
319        );
320
321        assert_eq!(
322            output.as_deref(),
323            Some(".button { color: blue; margin: 2px; }")
324        );
325    }
326
327    #[test]
328    fn transform_runner_rejects_overlapping_native_edits() {
329        let output = materialize_transform_module_evaluation_native_edits(
330            "abcdef",
331            &[
332                TransformModuleEvaluationNativeEditV0 {
333                    start: 1,
334                    end: 4,
335                    replacement: "x".to_string(),
336                    edit_kind: "value".to_string(),
337                    abstract_value: None,
338                    abstract_value_kind: None,
339                },
340                TransformModuleEvaluationNativeEditV0 {
341                    start: 3,
342                    end: 5,
343                    replacement: "y".to_string(),
344                    edit_kind: "value".to_string(),
345                    abstract_value: None,
346                    abstract_value_kind: None,
347                },
348            ],
349        );
350
351        assert_eq!(output, None);
352    }
353}