1use super::*;
2use omena_cascade::SupportsTargetCapabilityV0;
3use omena_parser::{
4 ClosedWorldBundleBuildErrorV0, ClosedWorldBundleV0, ClosedWorldModuleMetadataV0,
5 ClosedWorldSourcePrecisionSummaryV0, OpenWorldSnapshotV0,
6};
7use omena_query_transform_runner::{
8 EmissionOrderingPolicyV0, LinkedEmissionArtifactV0, LinkedStylesheetV0,
9 TransformBundleLinkErrorV0, TransformBundleLinkOptionsV0, TransformBundleModuleInputV0,
10 TransformBundleSemanticReachabilityInputV0, TransformBundleTransformedModuleV0,
11 classify_transform_reachability_precision, link_omena_transform_bundle_modules,
12 link_omena_transform_bundle_modules_with_options,
13 link_omena_transform_bundle_modules_with_semantic_reachability,
14 link_omena_transform_bundle_modules_with_semantic_reachability_and_metadata,
15 materialize_omena_transform_bundle_linked_stylesheet,
16};
17use omena_query_transform_runner::{
18 transform_pass_requires_closed_world_bundle, transform_pass_sort_ordinal,
19};
20use std::path::{Path, PathBuf};
21
22use super::parser_facade::parse_omena_query_omena_parser_style_source;
23
24mod context;
25mod css_modules;
26pub(super) use css_modules::derive_class_name_rewrites_for_transform_context;
27mod design_tokens;
28mod imports;
29mod static_stylesheet;
30
31use context::TransformResolutionContext;
32pub use context::summarize_omena_query_transform_context_from_engine_input;
33
34use context::{
35 derive_omena_query_transform_context_from_engine_input, find_target_style_source,
36 merge_target_options_transform_context, merge_transform_context,
37 summarize_omena_query_transform_context_from_sources_with_resolution_context,
38};
39use imports::resolve_import_inline_replacement_for_transform_context;
40use static_stylesheet::derive_static_scss_module_configurable_variable_names_for_transform_context;
41
42pub(super) struct StaticScssModuleResolutionConfigurationEvidence {
43 pub(super) configuration_signature: String,
44 pub(super) configuration_variable_count: usize,
45 pub(super) configuration_variable_names: Vec<String>,
46 pub(super) module_instance_identity_key: Option<String>,
47}
48
49pub(super) fn derive_static_scss_module_resolution_configuration_evidence(
50 style_source: &str,
51 edge_kind: &str,
52 rule_ordinal: usize,
53 resolved_style_path: Option<&str>,
54) -> StaticScssModuleResolutionConfigurationEvidence {
55 let at_keyword = match edge_kind {
56 "sassUse" => Some("@use"),
57 "sassForward" => Some("@forward"),
58 _ => None,
59 };
60 let variable_overrides = match at_keyword {
61 Some("@forward") => {
62 omena_semantic::derive_sass_module_forward_variable_override_values_at_ordinal(
63 style_source,
64 rule_ordinal,
65 )
66 }
67 Some(at_keyword) => omena_semantic::derive_sass_module_rule_variable_overrides_at_ordinal(
68 style_source,
69 at_keyword,
70 rule_ordinal,
71 ),
72 None => BTreeMap::new(),
73 };
74 let module_instance_identity_key =
75 at_keyword
76 .and(resolved_style_path)
77 .map(|resolved_style_path| {
78 omena_semantic::summarize_sass_module_instance_identity_key(
79 resolved_style_path,
80 &variable_overrides,
81 )
82 });
83
84 StaticScssModuleResolutionConfigurationEvidence {
85 configuration_signature: omena_semantic::summarize_sass_module_configuration_signature(
86 &variable_overrides,
87 ),
88 configuration_variable_count: variable_overrides.len(),
89 configuration_variable_names: variable_overrides.keys().cloned().collect(),
90 module_instance_identity_key,
91 }
92}
93
94pub(super) fn derive_static_scss_module_configurable_variable_names_for_resolution(
95 style_path: &str,
96 style_source: &str,
97 available_style_paths: &BTreeSet<&str>,
98 source_by_path: &BTreeMap<String, String>,
99 package_manifests: &[OmenaQueryStylePackageManifestV0],
100 bundler_path_mappings: &[OmenaResolverBundlerPathAliasMappingV0],
101 tsconfig_path_mappings: &[OmenaResolverTsconfigPathMappingV0],
102) -> BTreeSet<String> {
103 derive_static_scss_module_configurable_variable_names_for_transform_context(
104 style_path,
105 style_source,
106 available_style_paths,
107 source_by_path,
108 TransformResolutionContext {
109 package_manifests,
110 bundler_path_mappings,
111 tsconfig_path_mappings,
112 disk_style_path_identities: &[],
113 },
114 )
115}
116
117pub fn summarize_omena_query_transform_plan_from_source(
118 style_path: &str,
119 style_source: &str,
120 target_label: &str,
121 target_support: OmenaQueryTargetFeatureSupportV0,
122 target_options: OmenaQueryTargetTransformOptionsV0,
123 print_options: OmenaQueryTransformPrintOptionsV0,
124) -> OmenaQueryTransformPlanSummaryV0 {
125 summarize_omena_query_transform_plan_from_source_with_context(
126 style_path,
127 style_source,
128 target_label,
129 target_support,
130 target_options,
131 print_options,
132 &TransformExecutionContextV0::default(),
133 )
134}
135
136pub fn summarize_omena_query_transform_plan_from_source_with_context(
137 style_path: &str,
138 style_source: &str,
139 target_label: &str,
140 target_support: OmenaQueryTargetFeatureSupportV0,
141 target_options: OmenaQueryTargetTransformOptionsV0,
142 print_options: OmenaQueryTransformPrintOptionsV0,
143 context: &TransformExecutionContextV0,
144) -> OmenaQueryTransformPlanSummaryV0 {
145 let dialect = omena_parser_dialect_for_style_path(style_path);
146 let bundle = summarize_omena_transform_bundle_from_source(style_path, style_source, dialect);
147 let target = plan_target_transforms(target_label, target_support, target_options);
148 let mut execution_context = merge_target_options_transform_context(context, target_options);
149 execution_context.supports_target_capability = Some(
150 supports_target_capability_from_feature_support(target_support),
151 );
152 summarize_omena_query_transform_plan_from_parts(TransformPlanPartsV0 {
153 style_path,
154 style_source,
155 dialect,
156 bundle,
157 target,
158 target_query: None,
159 print_options,
160 context: &execution_context,
161 })
162}
163
164pub fn summarize_omena_query_transform_plan_from_target_query(
165 style_path: &str,
166 style_source: &str,
167 target_query: &str,
168 target_options: OmenaQueryTargetTransformOptionsV0,
169 print_options: OmenaQueryTransformPrintOptionsV0,
170) -> OmenaQueryTransformPlanSummaryV0 {
171 summarize_omena_query_transform_plan_from_target_query_with_context(
172 style_path,
173 style_source,
174 target_query,
175 target_options,
176 print_options,
177 &TransformExecutionContextV0::default(),
178 )
179}
180
181pub fn summarize_omena_query_transform_plan_from_target_query_with_context(
182 style_path: &str,
183 style_source: &str,
184 target_query: &str,
185 target_options: OmenaQueryTargetTransformOptionsV0,
186 print_options: OmenaQueryTransformPrintOptionsV0,
187 context: &TransformExecutionContextV0,
188) -> OmenaQueryTransformPlanSummaryV0 {
189 let dialect = omena_parser_dialect_for_style_path(style_path);
190 let bundle = summarize_omena_transform_bundle_from_source(style_path, style_source, dialect);
191 let target_query_plan = plan_target_transforms_from_query(target_query, target_options);
192 let vendor_prefix_policy = target_query_plan.vendor_prefix_policy;
193 let supports_target_capability =
194 supports_target_capability_from_feature_support(target_query_plan.support);
195 let target = target_query_plan.transform_plan.clone();
196 let mut execution_context = merge_target_options_transform_context(context, target_options);
197 execution_context.vendor_prefix_policy = vendor_prefix_policy;
198 execution_context.supports_target_capability = Some(supports_target_capability);
199 summarize_omena_query_transform_plan_from_parts(TransformPlanPartsV0 {
200 style_path,
201 style_source,
202 dialect,
203 bundle,
204 target,
205 target_query: Some(target_query_plan),
206 print_options,
207 context: &execution_context,
208 })
209}
210
211struct TransformPlanPartsV0<'a> {
212 style_path: &'a str,
213 style_source: &'a str,
214 dialect: OmenaParserStyleDialect,
215 bundle: TransformBundleSourceSummaryV0,
216 target: TransformTargetPlanV0,
217 target_query: Option<OmenaQueryTransformTargetQueryPlanV0>,
218 print_options: OmenaQueryTransformPrintOptionsV0,
219 context: &'a TransformExecutionContextV0,
220}
221
222pub struct OmenaQueryBundlePlanInputV0<'a> {
223 pub target_style_path: &'a str,
224 pub style_sources: &'a [OmenaQueryStyleSourceInputV0],
225 pub source_map_sources: &'a [OmenaQueryStyleSourceInputV0],
226 pub requested_pass_ids: &'a [String],
227 pub context: &'a TransformExecutionContextV0,
228 pub resolution_inputs: &'a OmenaQueryStyleResolutionInputsV0,
229 pub asset_rewrites: Vec<TransformBundleAssetUrlRewriteSummaryV0>,
230 pub bundle_entry_style_paths: &'a [String],
231}
232
233fn summarize_omena_query_transform_plan_from_parts(
234 parts: TransformPlanPartsV0<'_>,
235) -> OmenaQueryTransformPlanSummaryV0 {
236 let egg = plan_egg_rewrite_passes_for_source(parts.style_source);
237 let custom_property_fixed_point = summarize_static_css_custom_property_fixed_point_from_source(
238 parts.style_source,
239 parts.dialect,
240 );
241
242 let mut combined_passes = Vec::new();
243 extend_passes_from_ids(&parts.bundle.planned_pass_ids, &mut combined_passes);
244 extend_passes_from_ids(&parts.target.planned_pass_ids, &mut combined_passes);
245 extend_passes_from_ids(&egg.planned_pass_ids, &mut combined_passes);
246 combined_passes.push(TransformPassKind::PrintCss);
247 combined_passes.sort_by_key(|pass| transform_pass_sort_ordinal(*pass));
248 combined_passes.dedup();
249
250 let combined_plan = plan_transform_passes(&combined_passes);
251 let semantic_signature = format!(
252 "omena-query-transform:{}:{}",
253 parts.style_path,
254 parts.style_source.len()
255 );
256 let execution = execute_transform_passes_on_source_with_dialect_and_context(
257 parts.style_source,
258 parts.dialect,
259 &combined_passes,
260 parts.context,
261 );
262 let print = print_transform_execution_artifact_with_dialect_and_source(
263 parts.style_path,
264 parts.style_source,
265 parts.dialect,
266 semantic_signature,
267 &combined_passes,
268 parts.print_options,
269 &execution,
270 );
271 let combined_pass_ids = combined_plan.ordered_pass_ids.clone();
272 let egg_witnesses = execute_egg_rewrite_witnesses_for_css_source(
273 parts.style_source,
274 parts.dialect,
275 &execution.output_css,
276 &combined_pass_ids,
277 );
278 let semantic_removal_count = execution.semantic_removals.len();
279 let combined_violated_dag_edge_count = combined_plan.violated_dag_edge_count;
280
281 OmenaQueryTransformPlanSummaryV0 {
282 schema_version: "0",
283 product: "omena-query.transform-plan",
284 style_path: parts.style_path.to_string(),
285 dialect: omena_parser_style_dialect_label(parts.dialect),
286 bundle: parts.bundle,
287 target: parts.target,
288 target_query: parts.target_query,
289 egg,
290 egg_witnesses,
291 custom_property_fixed_point,
292 print,
293 execution,
294 semantic_removal_count,
295 combined_plan,
296 combined_pass_ids,
297 combined_violated_dag_edge_count,
298 ready_surfaces: vec![
299 "transformBundlePlan",
300 "transformTargetPlan",
301 "transformEggPlan",
302 "transformEggExecutionWitnesses",
303 "customPropertyLeastFixedPoint",
304 "transformPrintArtifact",
305 "transformExecutionRuntime",
306 "cascadeProofObligations",
307 "combinedTransformPassPlan",
308 ],
309 }
310}
311
312pub fn run_omena_query_bundle(
313 input: OmenaQueryBundlePlanInputV0<'_>,
314) -> Result<OmenaQueryBundleArtifactV0, String> {
315 run_omena_query_bundle_with_semantic_inputs(input, &[]).map(|result| result.artifact)
316}
317
318pub fn run_omena_query_bundle_with_semantic_inputs(
319 input: OmenaQueryBundlePlanInputV0<'_>,
320 external_sifs: &[OmenaQueryExternalSifInputV0],
321) -> Result<OmenaQueryBundleResultV0, String> {
322 run_omena_query_bundle_with_semantic_inputs_and_options(
323 input,
324 external_sifs,
325 &OmenaQueryConsumerBuildOptionsV0::default(),
326 )
327}
328
329pub fn run_omena_query_bundle_with_semantic_inputs_and_options(
330 input: OmenaQueryBundlePlanInputV0<'_>,
331 external_sifs: &[OmenaQueryExternalSifInputV0],
332 options: &OmenaQueryConsumerBuildOptionsV0,
333) -> Result<OmenaQueryBundleResultV0, String> {
334 let OmenaQueryBundlePlanInputV0 {
335 target_style_path,
336 style_sources,
337 source_map_sources,
338 requested_pass_ids,
339 context,
340 resolution_inputs,
341 asset_rewrites,
342 bundle_entry_style_paths,
343 } = input;
344 let Some(target_source) = find_target_style_source(target_style_path, style_sources) else {
345 return Err(format!(
346 "target style path {target_style_path:?} was not found in workspace style sources"
347 ));
348 };
349 let base_context = context;
350 let context = merge_workspace_transform_context(
351 target_style_path,
352 style_sources,
353 base_context,
354 TransformResolutionContext::from_resolution_inputs(resolution_inputs),
355 );
356 let effective_pass_ids = consumer_build_pass_set(requested_pass_ids).effective;
357 let legacy_summary =
358 (options.bundle_emission_path == OmenaQueryBundleEmissionPathV0::ImportInlineLegacy)
359 .then(|| {
360 execute_omena_query_consumer_build_style_sources_with_context_resolution_inputs_and_options(
361 target_style_path,
362 style_sources,
363 requested_pass_ids,
364 &context,
365 resolution_inputs,
366 options,
367 )
368 })
369 .transpose()?;
370 let bundle = summarize_omena_transform_bundle_from_source(
371 target_style_path,
372 target_source,
373 omena_parser_dialect_for_style_path(target_style_path),
374 );
375 let source_map_sources = if source_map_sources.is_empty() {
376 style_sources
377 } else {
378 source_map_sources
379 };
380 let code_split_outputs = summarize_omena_query_bundle_code_split_workspace_plan(
381 target_style_path,
382 bundle_entry_style_paths,
383 style_sources,
384 resolution_inputs,
385 )?
386 .outputs;
387 let linked_result =
388 (options.bundle_emission_path == OmenaQueryBundleEmissionPathV0::LinkedOrder).then(|| {
389 link_closed_world_stylesheet_for_style_sources(
390 target_style_path,
391 style_sources,
392 &effective_pass_ids,
393 &context,
394 external_sifs,
395 TransformBundleLinkOptionsV0 {
396 emission_ordering_policy: EmissionOrderingPolicyV0::ModuleIdLegacy,
397 },
398 )
399 });
400 let closed_world_outcome = linked_result.as_ref().map_or_else(
401 || {
402 build_closed_world_outcome_for_style_sources(
403 target_style_path,
404 style_sources,
405 &effective_pass_ids,
406 &context,
407 external_sifs,
408 )
409 },
410 |result| closed_world_outcome_from_link_result(result.clone(), &effective_pass_ids),
411 );
412 let legacy_open_decision = legacy_bundle_open_decision(
413 target_style_path,
414 style_sources,
415 &effective_pass_ids,
416 &context,
417 );
418 let closed_world_decision_parity = OmenaQueryClosedWorldDecisionParityV0 {
419 legacy_open_decision,
420 typed_outcome_open: closed_world_outcome.is_open(),
421 equivalent: legacy_open_decision == closed_world_outcome.is_open(),
422 };
423 validate_omena_query_closed_world_decision_parity(&closed_world_decision_parity)?;
424
425 let (execution, linked_materialization, emission_path) = if let Some(Ok(linked)) =
426 linked_result.as_ref()
427 {
428 let linked_execution = execute_linked_bundle_modules(
429 linked,
430 target_style_path,
431 style_sources,
432 &effective_pass_ids,
433 base_context,
434 resolution_inputs,
435 options,
436 )?;
437 (
438 linked_execution.execution,
439 Some(linked_execution.materialization),
440 OmenaQueryBundleEmissionPathV0::LinkedOrder,
441 )
442 } else if let Some(Err(error)) = linked_result.as_ref() {
443 return Err(format!("linked bundle emission failed: {error:?}"));
444 } else {
445 let Some(summary) = legacy_summary else {
446 return Err("linked bundle emission requires a closed linked stylesheet".to_string());
447 };
448 (
449 summary.execution,
450 None,
451 OmenaQueryBundleEmissionPathV0::ImportInlineLegacy,
452 )
453 };
454 let source_map_v3 = if let Some(materialization) = linked_materialization.as_ref() {
455 summarize_omena_query_linked_bundle_source_map_v3(
456 target_style_path,
457 source_map_sources,
458 &execution,
459 materialization,
460 )?
461 } else {
462 summarize_omena_query_consumer_build_source_map_v3_with_resolution_inputs(
463 target_style_path,
464 source_map_sources,
465 &execution,
466 resolution_inputs,
467 )
468 };
469
470 let artifact = OmenaQueryBundleArtifactV0 {
471 schema_version: "0",
472 product: "omena-query.bundle-artifact",
473 style_path: target_style_path.to_string(),
474 emission_path,
475 output_css: execution.output_css.clone(),
476 bundle,
477 source_map_v3,
478 code_split_outputs,
479 asset_rewrites,
480 per_pass_provenance: execution.outcomes.clone(),
481 execution,
482 ready_surfaces: vec![
483 "bundleOperationFacade",
484 "transformBundlePlan",
485 "transformExecutionRuntime",
486 "sourceMapV3Serializer",
487 "bundleCodeSplitPlan",
488 "transformPassOutcomeContract",
489 ],
490 };
491 Ok(OmenaQueryBundleResultV0 {
492 artifact,
493 closed_world_outcome,
494 closed_world_decision_parity,
495 })
496}
497
498pub fn run_omena_query_bundle_for_style_sources_with_context(
499 target_style_path: &str,
500 style_sources: &[OmenaQueryStyleSourceInputV0],
501 requested_pass_ids: &[String],
502 context: &TransformExecutionContextV0,
503 package_manifests: &[OmenaQueryStylePackageManifestV0],
504 bundle_entry_style_paths: &[String],
505) -> Result<OmenaQueryBundleArtifactV0, String> {
506 run_omena_query_bundle_with_evidence_for_style_sources_with_context(
507 target_style_path,
508 style_sources,
509 requested_pass_ids,
510 context,
511 package_manifests,
512 bundle_entry_style_paths,
513 )
514 .map(|bundle| bundle.artifact)
515}
516
517pub fn run_omena_query_bundle_with_evidence_for_style_sources_with_context(
518 target_style_path: &str,
519 style_sources: &[OmenaQueryStyleSourceInputV0],
520 requested_pass_ids: &[String],
521 context: &TransformExecutionContextV0,
522 package_manifests: &[OmenaQueryStylePackageManifestV0],
523 bundle_entry_style_paths: &[String],
524) -> Result<OmenaQueryBundleWithEvidenceV0, String> {
525 let resolution_inputs = resolution_inputs_for_transform_style_sources(
526 target_style_path,
527 style_sources,
528 package_manifests,
529 );
530 let result = run_omena_query_bundle_with_semantic_inputs(
531 OmenaQueryBundlePlanInputV0 {
532 target_style_path,
533 style_sources,
534 source_map_sources: style_sources,
535 requested_pass_ids,
536 context,
537 resolution_inputs: &resolution_inputs,
538 asset_rewrites: Vec::new(),
539 bundle_entry_style_paths,
540 },
541 &[],
542 )?;
543 let evidence = summarize_omena_query_bundle_evidence(&result);
544 Ok(OmenaQueryBundleWithEvidenceV0 {
545 artifact: result.artifact,
546 closed_world_outcome: result.closed_world_outcome,
547 closed_world_decision_parity: result.closed_world_decision_parity,
548 evidence,
549 })
550}
551
552pub fn summarize_omena_query_bundle_evidence(
553 result: &OmenaQueryBundleResultV0,
554) -> OmenaQueryBundleEvidenceManifestV0 {
555 let artifact = &result.artifact;
556 let (outcome_status, reachability, blockers, interface_hashes, source_precision) = match &result
557 .closed_world_outcome
558 {
559 OmenaQueryClosedWorldOutcomeV0::Closed { bundle } => (
560 "closed",
561 Some(OmenaQueryBundleReachabilityEvidenceV0 {
562 guarantee: omena_evidence_graph::GuaranteeKindV0::NotClaimedExactTraversal,
563 interpretation: "resolved-world exact BFS reachability; world incompleteness is represented by blockers",
564 module_instances: bundle.reachability().module_instances().to_vec(),
565 closure_hash: bundle.closure_hash().to_string(),
566 }),
567 Vec::new(),
568 bundle.interface_hashes().entries().to_vec(),
569 bundle.source_precision(),
570 ),
571 OmenaQueryClosedWorldOutcomeV0::Open { blockers } => {
572 ("open", None, blockers.clone(), Vec::new(), None)
573 }
574 };
575 OmenaQueryBundleEvidenceManifestV0 {
576 schema_version: "0",
577 product: "omena-query.bundle-evidence",
578 style_path: artifact.style_path.clone(),
579 outcome_status,
580 reachability,
581 gates: vec![
582 OmenaQueryBundleEvidenceGateV0 {
583 name: "resolvedWorldLink",
584 passed: outcome_status == "closed",
585 },
586 OmenaQueryBundleEvidenceGateV0 {
587 name: "closedWorldAdmission",
588 passed: outcome_status == "closed" && blockers.is_empty(),
589 },
590 OmenaQueryBundleEvidenceGateV0 {
591 name: "closedWorldDecisionParity",
592 passed: result.closed_world_decision_parity.equivalent,
593 },
594 ],
595 blockers,
596 interface_hashes,
597 source_precision,
598 }
599}
600
601pub fn validate_omena_query_closed_world_decision_parity(
602 parity: &OmenaQueryClosedWorldDecisionParityV0,
603) -> Result<(), String> {
604 if parity.equivalent && parity.legacy_open_decision == parity.typed_outcome_open {
605 return Ok(());
606 }
607 Err(format!(
608 "closed-world decision parity mismatch: legacyOpen={}, typedOutcomeOpen={}",
609 parity.legacy_open_decision, parity.typed_outcome_open
610 ))
611}
612
613pub fn execute_omena_query_transform_passes_from_source(
614 style_path: &str,
615 style_source: &str,
616 requested_pass_ids: &[String],
617) -> OmenaQueryTransformExecuteSummaryV0 {
618 execute_omena_query_transform_passes_from_source_with_context(
619 style_path,
620 style_source,
621 requested_pass_ids,
622 &TransformExecutionContextV0::default(),
623 )
624}
625
626pub fn summarize_omena_query_consumer_check_style_source(
627 style_path: &str,
628 style_source: &str,
629) -> OmenaQueryConsumerCheckSummaryV0 {
630 let dialect = omena_parser_dialect_for_style_path(style_path);
631 let parse_result = parse_omena_query_omena_parser_style_source(style_source, dialect);
632 let runtime_index =
633 omena_semantic::summarize_style_runtime_index_facts_from_source(style_path, style_source);
634 let (class_selector_count, custom_property_count, keyframe_count, index_ready_surface) =
635 if let Some(runtime_index) = runtime_index {
636 (
637 runtime_index.class_selector_names.len(),
638 runtime_index.custom_property_names.len(),
639 runtime_index.keyframe_names.len(),
640 "semanticRuntimeIndexFacts",
641 )
642 } else {
643 let style_facts = summarize_omena_query_omena_parser_style_facts(style_source, dialect);
644 (
645 style_facts.class_selector_names.len(),
646 style_facts.custom_property_names.len(),
647 style_facts.keyframe_names.len(),
648 "parserFactSummary",
649 )
650 };
651
652 OmenaQueryConsumerCheckSummaryV0 {
653 schema_version: "0",
654 product: "omena-query.consumer-check-style-source",
655 style_path: style_path.to_string(),
656 dialect: omena_parser_style_dialect_label(dialect),
657 token_count: parse_result.token_count(),
658 parser_error_count: parse_result.errors().len(),
659 class_selector_count,
660 custom_property_count,
661 keyframe_count,
662 ready_surfaces: vec![
663 "consumerCheckFacade",
664 index_ready_surface,
665 "styleDocumentDiagnostics",
666 ],
667 }
668}
669
670pub fn execute_omena_query_consumer_build_style_source(
671 style_path: &str,
672 style_source: &str,
673 requested_pass_ids: &[String],
674) -> OmenaQueryConsumerBuildSummaryV0 {
675 execute_omena_query_consumer_build_style_source_with_context_and_options(
676 style_path,
677 style_source,
678 requested_pass_ids,
679 &TransformExecutionContextV0::default(),
680 &OmenaQueryConsumerBuildOptionsV0::default(),
681 )
682}
683
684pub fn execute_omena_query_consumer_build_style_source_with_context(
685 style_path: &str,
686 style_source: &str,
687 requested_pass_ids: &[String],
688 context: &TransformExecutionContextV0,
689) -> OmenaQueryConsumerBuildSummaryV0 {
690 execute_omena_query_consumer_build_style_source_with_context_and_options(
691 style_path,
692 style_source,
693 requested_pass_ids,
694 context,
695 &OmenaQueryConsumerBuildOptionsV0::default(),
696 )
697}
698
699pub fn execute_omena_query_consumer_build_style_source_with_context_and_options(
700 style_path: &str,
701 style_source: &str,
702 requested_pass_ids: &[String],
703 context: &TransformExecutionContextV0,
704 options: &OmenaQueryConsumerBuildOptionsV0,
705) -> OmenaQueryConsumerBuildSummaryV0 {
706 execute_omena_query_consumer_build_style_source_with_context_and_reachability_precision(
707 style_path,
708 style_source,
709 requested_pass_ids,
710 context,
711 None,
712 false,
713 options,
714 )
715}
716
717fn execute_omena_query_consumer_build_style_source_with_context_and_reachability_precision(
718 style_path: &str,
719 style_source: &str,
720 requested_pass_ids: &[String],
721 context: &TransformExecutionContextV0,
722 reachability_precision: Option<FactPrecision>,
723 closed_set_enumeration_candidate: bool,
724 options: &OmenaQueryConsumerBuildOptionsV0,
725) -> OmenaQueryConsumerBuildSummaryV0 {
726 let context = merge_single_source_transform_context(style_path, style_source, context);
727 let pass_set = consumer_build_pass_set(requested_pass_ids);
728 let closed_world_outcome =
729 pass_ids_require_closed_world_bundle(&pass_set.effective).then(|| {
730 build_closed_world_outcome_for_single_style_source_context(
731 style_path,
732 style_source,
733 &pass_set.effective,
734 &context,
735 )
736 });
737 if let Some(closed_world_bundle) = closed_world_outcome
738 .as_ref()
739 .and_then(OmenaQueryClosedWorldOutcomeV0::bundle)
740 {
741 let reachability_precision = closed_world_bound_reachability_precision(
742 &context,
743 closed_world_bundle,
744 reachability_precision,
745 closed_set_enumeration_candidate,
746 );
747 return execute_omena_query_consumer_build_style_source_with_context_and_closed_world_bundle(
748 style_path,
749 style_source,
750 &pass_set,
751 &context,
752 closed_world_bundle,
753 reachability_precision,
754 options,
755 );
756 }
757
758 execute_omena_query_consumer_build_style_source_with_open_world_context(
759 style_path,
760 style_source,
761 &pass_set,
762 &context,
763 options,
764 )
765}
766
767struct ConsumerBuildPassSetV0 {
768 requested: Vec<String>,
769 effective: Vec<String>,
770}
771
772fn consumer_build_pass_set(requested_pass_ids: &[String]) -> ConsumerBuildPassSetV0 {
773 ConsumerBuildPassSetV0 {
774 requested: requested_pass_ids.to_vec(),
775 effective: compute_effective_pass_ids(requested_pass_ids),
776 }
777}
778
779fn compute_effective_pass_ids(requested_pass_ids: &[String]) -> Vec<String> {
780 if !requested_pass_ids.is_empty() {
781 return requested_pass_ids.to_vec();
782 }
783
784 all_transform_pass_kinds()
785 .into_iter()
786 .filter(|pass| {
787 *pass != TransformPassKind::NativeCssStaticEval
788 && !transform_pass_requires_closed_world_bundle(*pass)
789 })
790 .map(|pass| pass.id().to_string())
791 .collect()
792}
793
794fn execution_policy_for_build_options(
795 options: &OmenaQueryConsumerBuildOptionsV0,
796) -> TransformExecutionPolicyV0 {
797 match options.verification_profile {
798 OmenaQueryBuildVerificationProfileV0::Descriptive => TransformExecutionPolicyV0::default(),
799 OmenaQueryBuildVerificationProfileV0::Strict => TransformExecutionPolicyV0::for_profile(
800 omena_query_transform_runner::STRICT_VERIFICATION_BUILD_PROFILE_ID_V0,
801 )
802 .unwrap_or_default(),
803 }
804}
805
806fn execute_omena_query_consumer_build_style_source_with_open_world_context(
807 style_path: &str,
808 style_source: &str,
809 pass_set: &ConsumerBuildPassSetV0,
810 context: &TransformExecutionContextV0,
811 options: &OmenaQueryConsumerBuildOptionsV0,
812) -> OmenaQueryConsumerBuildSummaryV0 {
813 let execution_summary =
814 execute_omena_query_transform_passes_from_source_with_open_world_context(
815 style_path,
816 style_source,
817 &pass_set.effective,
818 context,
819 &execution_policy_for_build_options(options),
820 );
821 let open_world_snapshot = open_world_snapshot_for_closed_world_passes(&pass_set.effective);
822 let ready_surfaces = consumer_build_ready_surfaces_with_open_world_snapshot(
823 open_world_snapshot.as_ref(),
824 vec![
825 "consumerBuildFacade",
826 "singleSourceTransformContextProducer",
827 "transformExecutionRuntime",
828 "transformPassOutcomeContract",
829 ],
830 );
831
832 OmenaQueryConsumerBuildSummaryV0 {
833 schema_version: "0",
834 product: "omena-query.consumer-build-style-source",
835 style_path: style_path.to_string(),
836 dialect: omena_parser_style_dialect_label(omena_parser_dialect_for_style_path(style_path)),
837 requested_pass_ids: pass_set.requested.clone(),
838 effective_pass_ids: pass_set.effective.clone(),
839 target_query: None,
840 unknown_pass_ids: execution_summary.unknown_pass_ids,
841 semantic_removal_count: execution_summary.semantic_removal_count,
842 execution: execution_summary.execution,
843 bundle: None,
844 bundle_emission_path: None,
845 source_map_v3: None,
846 open_world_snapshot,
847 ready_surfaces,
848 }
849}
850
851fn execute_omena_query_consumer_build_style_source_with_context_and_closed_world_bundle(
852 style_path: &str,
853 style_source: &str,
854 pass_set: &ConsumerBuildPassSetV0,
855 context: &TransformExecutionContextV0,
856 closed_world_bundle: &ClosedWorldBundleV0,
857 reachability_precision: FactPrecision,
858 options: &OmenaQueryConsumerBuildOptionsV0,
859) -> OmenaQueryConsumerBuildSummaryV0 {
860 let context = merge_single_source_transform_context(style_path, style_source, context);
861 let execution_summary =
862 execute_omena_query_transform_passes_from_source_with_context_and_closed_world_bundle(
863 style_path,
864 style_source,
865 &pass_set.effective,
866 &context,
867 closed_world_bundle,
868 reachability_precision,
869 &execution_policy_for_build_options(options),
870 );
871
872 OmenaQueryConsumerBuildSummaryV0 {
873 schema_version: "0",
874 product: "omena-query.consumer-build-style-source",
875 style_path: style_path.to_string(),
876 dialect: omena_parser_style_dialect_label(omena_parser_dialect_for_style_path(style_path)),
877 requested_pass_ids: pass_set.requested.clone(),
878 effective_pass_ids: pass_set.effective.clone(),
879 target_query: None,
880 unknown_pass_ids: execution_summary.unknown_pass_ids,
881 semantic_removal_count: execution_summary.semantic_removal_count,
882 execution: execution_summary.execution,
883 bundle: None,
884 bundle_emission_path: None,
885 source_map_v3: None,
886 open_world_snapshot: None,
887 ready_surfaces: vec![
888 "consumerBuildFacade",
889 "singleSourceTransformContextProducer",
890 "closedWorldBundle",
891 "transformExecutionRuntime",
892 "transformPassOutcomeContract",
893 ],
894 }
895}
896
897pub fn execute_omena_query_consumer_build_style_source_with_engine_input_context(
898 style_path: &str,
899 style_source: &str,
900 requested_pass_ids: &[String],
901 input: &EngineInputV2,
902 closed_world_requested: bool,
903) -> OmenaQueryConsumerBuildSummaryV0 {
904 let context_derivation = derive_omena_query_transform_context_from_engine_input(
905 input,
906 style_path,
907 closed_world_requested,
908 );
909 let mut summary =
910 execute_omena_query_consumer_build_style_source_with_context_and_reachability_precision(
911 style_path,
912 style_source,
913 requested_pass_ids,
914 &context_derivation.summary.context,
915 context_derivation.reachability_precision,
916 context_derivation.closed_set_enumeration_candidate,
917 &OmenaQueryConsumerBuildOptionsV0::default(),
918 );
919 summary
920 .ready_surfaces
921 .push("semanticReachabilityTransformContext");
922 summary
923 .ready_surfaces
924 .push("expressionDomainSelectorProjection");
925 summary
926}
927
928fn closed_world_bound_reachability_precision(
929 context: &TransformExecutionContextV0,
930 closed_world_bundle: &ClosedWorldBundleV0,
931 open_world_precision: Option<FactPrecision>,
932 closed_set_enumeration_candidate: bool,
933) -> FactPrecision {
934 let fallback = open_world_precision.unwrap_or(FactPrecision::Conservative);
935 if !closed_set_enumeration_candidate
936 || !fallback.satisfies(FactPrecision::Conservative)
937 || context.reachable_class_names.is_empty()
938 {
939 return fallback;
940 }
941
942 let closed_world_class_names = closed_world_bundle
943 .reachability()
944 .class_names()
945 .iter()
946 .map(String::as_str)
947 .collect::<BTreeSet<_>>();
948 let enumerated_class_names = context
949 .reachable_class_names
950 .iter()
951 .cloned()
952 .collect::<BTreeSet<_>>();
953 if enumerated_class_names
954 .iter()
955 .any(|name| !closed_world_class_names.contains(name.as_str()))
956 {
957 return fallback;
958 }
959
960 let value = AbstractClassValueV0::FiniteSet {
961 values: enumerated_class_names.into_iter().collect(),
962 };
963 let witness = OmenaAbstractValuePrecisionWitnessV0 {
964 direction: OmenaAbstractValueCoverageDirectionV0::SupersetOfProducible,
965 basis: OmenaAbstractValuePrecisionBasisV0::ClosedSetEnumeration,
966 authority_digest: Some(closed_world_bundle.closure_hash().to_string()),
967 };
968 fact_precision_from_class_value_with_witness(&value, Some(&witness))
969}
970
971pub fn execute_omena_query_consumer_build_style_sources_with_context(
972 target_style_path: &str,
973 style_sources: &[OmenaQueryStyleSourceInputV0],
974 requested_pass_ids: &[String],
975 context: &TransformExecutionContextV0,
976 package_manifests: &[OmenaQueryStylePackageManifestV0],
977) -> Result<OmenaQueryConsumerBuildSummaryV0, String> {
978 execute_omena_query_consumer_build_style_sources_with_context_and_options(
979 target_style_path,
980 style_sources,
981 requested_pass_ids,
982 context,
983 package_manifests,
984 &OmenaQueryConsumerBuildOptionsV0::default(),
985 )
986}
987
988pub fn execute_omena_query_consumer_build_style_sources_with_context_and_options(
989 target_style_path: &str,
990 style_sources: &[OmenaQueryStyleSourceInputV0],
991 requested_pass_ids: &[String],
992 context: &TransformExecutionContextV0,
993 package_manifests: &[OmenaQueryStylePackageManifestV0],
994 options: &OmenaQueryConsumerBuildOptionsV0,
995) -> Result<OmenaQueryConsumerBuildSummaryV0, String> {
996 let resolution_inputs = resolution_inputs_for_transform_style_sources(
997 target_style_path,
998 style_sources,
999 package_manifests,
1000 );
1001 execute_omena_query_consumer_build_style_sources_with_context_resolution_inputs_and_options(
1002 target_style_path,
1003 style_sources,
1004 requested_pass_ids,
1005 context,
1006 &resolution_inputs,
1007 options,
1008 )
1009}
1010
1011pub fn execute_omena_query_consumer_build_style_sources_with_context_and_resolution_inputs(
1012 target_style_path: &str,
1013 style_sources: &[OmenaQueryStyleSourceInputV0],
1014 requested_pass_ids: &[String],
1015 context: &TransformExecutionContextV0,
1016 resolution_inputs: &OmenaQueryStyleResolutionInputsV0,
1017) -> Result<OmenaQueryConsumerBuildSummaryV0, String> {
1018 execute_omena_query_consumer_build_style_sources_with_context_resolution_inputs_and_options(
1019 target_style_path,
1020 style_sources,
1021 requested_pass_ids,
1022 context,
1023 resolution_inputs,
1024 &OmenaQueryConsumerBuildOptionsV0::default(),
1025 )
1026}
1027
1028pub fn execute_omena_query_consumer_build_style_sources_with_context_resolution_inputs_and_options(
1029 target_style_path: &str,
1030 style_sources: &[OmenaQueryStyleSourceInputV0],
1031 requested_pass_ids: &[String],
1032 context: &TransformExecutionContextV0,
1033 resolution_inputs: &OmenaQueryStyleResolutionInputsV0,
1034 options: &OmenaQueryConsumerBuildOptionsV0,
1035) -> Result<OmenaQueryConsumerBuildSummaryV0, String> {
1036 let Some(target_source) = find_target_style_source(target_style_path, style_sources) else {
1037 return Err(format!(
1038 "target style path {target_style_path:?} was not found in workspace style sources"
1039 ));
1040 };
1041 let context = merge_workspace_transform_context(
1042 target_style_path,
1043 style_sources,
1044 context,
1045 TransformResolutionContext::from_resolution_inputs(resolution_inputs),
1046 );
1047 let pass_set = consumer_build_pass_set(requested_pass_ids);
1048 let closed_world_outcome =
1049 pass_ids_require_closed_world_bundle(&pass_set.effective).then(|| {
1050 build_closed_world_outcome_for_style_sources(
1051 target_style_path,
1052 style_sources,
1053 &pass_set.effective,
1054 &context,
1055 &[],
1056 )
1057 });
1058 let mut summary = if let Some(closed_world_bundle) = closed_world_outcome
1059 .as_ref()
1060 .and_then(OmenaQueryClosedWorldOutcomeV0::bundle)
1061 {
1062 execute_omena_query_consumer_build_style_source_with_context_and_closed_world_bundle(
1063 target_style_path,
1064 target_source,
1065 &pass_set,
1066 &context,
1067 closed_world_bundle,
1068 classify_transform_reachability_precision(&context, true, None),
1069 options,
1070 )
1071 } else {
1072 execute_omena_query_consumer_build_style_source_with_open_world_context(
1073 target_style_path,
1074 target_source,
1075 &pass_set,
1076 &context,
1077 options,
1078 )
1079 };
1080 summary
1081 .ready_surfaces
1082 .push("multiSourceTransformContextProducer");
1083 Ok(summary)
1084}
1085
1086pub fn execute_omena_query_consumer_build_style_sources(
1087 target_style_path: &str,
1088 style_sources: &[OmenaQueryStyleSourceInputV0],
1089 requested_pass_ids: &[String],
1090 package_manifests: &[OmenaQueryStylePackageManifestV0],
1091) -> Result<OmenaQueryConsumerBuildSummaryV0, String> {
1092 execute_omena_query_consumer_build_style_sources_with_context(
1093 target_style_path,
1094 style_sources,
1095 requested_pass_ids,
1096 &TransformExecutionContextV0::default(),
1097 package_manifests,
1098 )
1099}
1100
1101pub fn execute_omena_query_consumer_build_style_source_for_target_query(
1102 style_path: &str,
1103 style_source: &str,
1104 target_query: &str,
1105) -> OmenaQueryConsumerBuildSummaryV0 {
1106 execute_omena_query_consumer_build_style_source_for_target_query_with_options(
1107 style_path,
1108 style_source,
1109 target_query,
1110 conservative_omena_query_target_options(),
1111 )
1112}
1113
1114pub fn execute_omena_query_consumer_build_style_source_for_target_query_with_options(
1115 style_path: &str,
1116 style_source: &str,
1117 target_query: &str,
1118 target_options: OmenaQueryTargetTransformOptionsV0,
1119) -> OmenaQueryConsumerBuildSummaryV0 {
1120 execute_omena_query_consumer_build_style_source_for_target_query_with_context_and_options(
1121 style_path,
1122 style_source,
1123 target_query,
1124 &TransformExecutionContextV0::default(),
1125 target_options,
1126 )
1127}
1128
1129pub fn execute_omena_query_consumer_build_style_source_for_target_query_with_context_and_options(
1130 style_path: &str,
1131 style_source: &str,
1132 target_query: &str,
1133 context: &TransformExecutionContextV0,
1134 target_options: OmenaQueryTargetTransformOptionsV0,
1135) -> OmenaQueryConsumerBuildSummaryV0 {
1136 execute_omena_query_consumer_build_style_source_for_target_query_with_context_options_and_additional_passes(
1137 style_path,
1138 style_source,
1139 target_query,
1140 context,
1141 target_options,
1142 &[],
1143 )
1144}
1145
1146pub fn execute_omena_query_consumer_build_style_source_for_target_query_with_context_options_and_additional_passes(
1147 style_path: &str,
1148 style_source: &str,
1149 target_query: &str,
1150 context: &TransformExecutionContextV0,
1151 target_options: OmenaQueryTargetTransformOptionsV0,
1152 additional_pass_ids: &[String],
1153) -> OmenaQueryConsumerBuildSummaryV0 {
1154 execute_omena_query_consumer_build_style_source_for_target_query_with_context_options_additional_passes_and_build_options(
1155 style_path,
1156 style_source,
1157 target_query,
1158 context,
1159 target_options,
1160 additional_pass_ids,
1161 &OmenaQueryConsumerBuildOptionsV0::default(),
1162 )
1163}
1164
1165pub fn execute_omena_query_consumer_build_style_source_for_target_query_with_context_options_additional_passes_and_build_options(
1166 style_path: &str,
1167 style_source: &str,
1168 target_query: &str,
1169 context: &TransformExecutionContextV0,
1170 target_options: OmenaQueryTargetTransformOptionsV0,
1171 additional_pass_ids: &[String],
1172 build_options: &OmenaQueryConsumerBuildOptionsV0,
1173) -> OmenaQueryConsumerBuildSummaryV0 {
1174 let context = merge_single_source_transform_context(style_path, style_source, context);
1175 let plan = summarize_omena_query_transform_plan_from_target_query_with_context(
1176 style_path,
1177 style_source,
1178 target_query,
1179 target_options,
1180 default_omena_query_transform_print_options(),
1181 &context,
1182 );
1183 let mut requested_pass_ids = plan
1184 .combined_pass_ids
1185 .iter()
1186 .map(|pass_id| (*pass_id).to_string())
1187 .collect::<Vec<_>>();
1188 extend_unique_pass_ids(&mut requested_pass_ids, additional_pass_ids);
1189 let mut execution_context = merge_target_options_transform_context(&context, target_options);
1190 execution_context.vendor_prefix_policy = plan
1191 .target_query
1192 .as_ref()
1193 .and_then(|target_query| target_query.vendor_prefix_policy);
1194 execution_context.supports_target_capability = plan
1195 .target_query
1196 .as_ref()
1197 .map(|target_query| supports_target_capability_from_feature_support(target_query.support));
1198 let execution_summary =
1199 execute_omena_query_consumer_build_style_source_with_context_and_options(
1200 style_path,
1201 style_source,
1202 &requested_pass_ids,
1203 &execution_context,
1204 build_options,
1205 );
1206 let ready_surfaces = extend_ready_surfaces(
1207 execution_summary.ready_surfaces.clone(),
1208 ["targetQueryBuildFacade"],
1209 );
1210 let ready_surfaces = consumer_build_ready_surfaces_with_open_world_snapshot(
1211 execution_summary.open_world_snapshot.as_ref(),
1212 ready_surfaces,
1213 );
1214
1215 OmenaQueryConsumerBuildSummaryV0 {
1216 schema_version: "0",
1217 product: "omena-query.consumer-build-style-source",
1218 style_path: plan.style_path,
1219 dialect: plan.dialect,
1220 requested_pass_ids,
1221 effective_pass_ids: execution_summary.effective_pass_ids,
1222 target_query: plan.target_query,
1223 unknown_pass_ids: execution_summary.unknown_pass_ids,
1224 semantic_removal_count: execution_summary.semantic_removal_count,
1225 execution: execution_summary.execution,
1226 bundle: None,
1227 bundle_emission_path: None,
1228 source_map_v3: None,
1229 open_world_snapshot: execution_summary.open_world_snapshot,
1230 ready_surfaces,
1231 }
1232}
1233
1234pub fn execute_omena_query_consumer_build_style_sources_for_target_query_with_context_and_options(
1235 target_style_path: &str,
1236 style_sources: &[OmenaQueryStyleSourceInputV0],
1237 target_query: &str,
1238 context: &TransformExecutionContextV0,
1239 target_options: OmenaQueryTargetTransformOptionsV0,
1240 package_manifests: &[OmenaQueryStylePackageManifestV0],
1241) -> Result<OmenaQueryConsumerBuildSummaryV0, String> {
1242 let resolution_inputs = resolution_inputs_for_transform_style_sources(
1243 target_style_path,
1244 style_sources,
1245 package_manifests,
1246 );
1247 execute_omena_query_consumer_build_style_sources_for_target_query_with_context_and_options_and_resolution_inputs(
1248 target_style_path,
1249 style_sources,
1250 target_query,
1251 context,
1252 target_options,
1253 &resolution_inputs,
1254 )
1255}
1256
1257pub fn execute_omena_query_consumer_build_style_sources_for_target_query_with_context_and_options_and_resolution_inputs(
1258 target_style_path: &str,
1259 style_sources: &[OmenaQueryStyleSourceInputV0],
1260 target_query: &str,
1261 context: &TransformExecutionContextV0,
1262 target_options: OmenaQueryTargetTransformOptionsV0,
1263 resolution_inputs: &OmenaQueryStyleResolutionInputsV0,
1264) -> Result<OmenaQueryConsumerBuildSummaryV0, String> {
1265 execute_omena_query_consumer_build_style_sources_for_target_query_with_context_options_additional_passes_and_resolution_inputs(
1266 target_style_path,
1267 style_sources,
1268 target_query,
1269 context,
1270 target_options,
1271 &[],
1272 resolution_inputs,
1273 )
1274}
1275
1276pub fn execute_omena_query_consumer_build_style_sources_for_target_query_with_context_options_additional_passes_and_resolution_inputs(
1277 target_style_path: &str,
1278 style_sources: &[OmenaQueryStyleSourceInputV0],
1279 target_query: &str,
1280 context: &TransformExecutionContextV0,
1281 target_options: OmenaQueryTargetTransformOptionsV0,
1282 additional_pass_ids: &[String],
1283 resolution_inputs: &OmenaQueryStyleResolutionInputsV0,
1284) -> Result<OmenaQueryConsumerBuildSummaryV0, String> {
1285 let build_options = OmenaQueryConsumerBuildOptionsV0::default();
1286 execute_omena_query_consumer_build_style_sources_for_target_query_with_context_and_build_inputs(
1287 target_style_path,
1288 style_sources,
1289 target_query,
1290 context,
1291 OmenaQueryTargetConsumerBuildInputsV0 {
1292 target_options,
1293 additional_pass_ids,
1294 resolution_inputs,
1295 build_options: &build_options,
1296 },
1297 )
1298}
1299
1300#[derive(Debug, Clone, Copy)]
1301pub struct OmenaQueryTargetConsumerBuildInputsV0<'a> {
1302 pub target_options: OmenaQueryTargetTransformOptionsV0,
1303 pub additional_pass_ids: &'a [String],
1304 pub resolution_inputs: &'a OmenaQueryStyleResolutionInputsV0,
1305 pub build_options: &'a OmenaQueryConsumerBuildOptionsV0,
1306}
1307
1308pub fn execute_omena_query_consumer_build_style_sources_for_target_query_with_context_and_build_inputs(
1309 target_style_path: &str,
1310 style_sources: &[OmenaQueryStyleSourceInputV0],
1311 target_query: &str,
1312 context: &TransformExecutionContextV0,
1313 inputs: OmenaQueryTargetConsumerBuildInputsV0<'_>,
1314) -> Result<OmenaQueryConsumerBuildSummaryV0, String> {
1315 let OmenaQueryTargetConsumerBuildInputsV0 {
1316 target_options,
1317 additional_pass_ids,
1318 resolution_inputs,
1319 build_options,
1320 } = inputs;
1321 let Some(target_source) = find_target_style_source(target_style_path, style_sources) else {
1322 return Err(format!(
1323 "target style path {target_style_path:?} was not found in workspace style sources"
1324 ));
1325 };
1326 let context = merge_workspace_transform_context(
1327 target_style_path,
1328 style_sources,
1329 context,
1330 TransformResolutionContext::from_resolution_inputs(resolution_inputs),
1331 );
1332 let plan = summarize_omena_query_transform_plan_from_target_query_with_context(
1333 target_style_path,
1334 target_source,
1335 target_query,
1336 target_options,
1337 default_omena_query_transform_print_options(),
1338 &context,
1339 );
1340 let mut requested_pass_ids = plan
1341 .combined_pass_ids
1342 .iter()
1343 .map(|pass_id| (*pass_id).to_string())
1344 .collect::<Vec<_>>();
1345 extend_unique_pass_ids(&mut requested_pass_ids, additional_pass_ids);
1346 let mut execution_context = merge_target_options_transform_context(&context, target_options);
1347 execution_context.vendor_prefix_policy = plan
1348 .target_query
1349 .as_ref()
1350 .and_then(|target_query| target_query.vendor_prefix_policy);
1351 execution_context.supports_target_capability = plan
1352 .target_query
1353 .as_ref()
1354 .map(|target_query| supports_target_capability_from_feature_support(target_query.support));
1355 let execution_summary = execute_omena_query_consumer_build_style_sources_with_context_resolution_inputs_and_options(
1356 target_style_path,
1357 style_sources,
1358 &requested_pass_ids,
1359 &execution_context,
1360 resolution_inputs,
1361 build_options,
1362 )?;
1363 let ready_surfaces = extend_ready_surfaces(
1364 execution_summary.ready_surfaces.clone(),
1365 [
1366 "targetQueryBuildFacade",
1367 "multiSourceTransformContextProducer",
1368 ],
1369 );
1370 let ready_surfaces = consumer_build_ready_surfaces_with_open_world_snapshot(
1371 execution_summary.open_world_snapshot.as_ref(),
1372 ready_surfaces,
1373 );
1374
1375 Ok(OmenaQueryConsumerBuildSummaryV0 {
1376 schema_version: "0",
1377 product: "omena-query.consumer-build-style-source",
1378 style_path: plan.style_path,
1379 dialect: plan.dialect,
1380 requested_pass_ids,
1381 effective_pass_ids: execution_summary.effective_pass_ids,
1382 target_query: plan.target_query,
1383 unknown_pass_ids: execution_summary.unknown_pass_ids,
1384 semantic_removal_count: execution_summary.semantic_removal_count,
1385 execution: execution_summary.execution,
1386 bundle: None,
1387 bundle_emission_path: None,
1388 source_map_v3: None,
1389 open_world_snapshot: execution_summary.open_world_snapshot,
1390 ready_surfaces,
1391 })
1392}
1393
1394fn extend_unique_pass_ids(target: &mut Vec<String>, additional: &[String]) {
1395 for pass_id in additional {
1396 if !target.contains(pass_id) {
1397 target.push(pass_id.clone());
1398 }
1399 }
1400}
1401
1402fn supports_target_capability_from_feature_support(
1403 support: OmenaQueryTargetFeatureSupportV0,
1404) -> SupportsTargetCapabilityV0 {
1405 SupportsTargetCapabilityV0 {
1406 supports_light_dark: support.supports_light_dark,
1407 supports_color_mix: support.supports_color_mix,
1408 supports_oklch_oklab: support.supports_oklch_oklab,
1409 supports_color_function: support.supports_color_function,
1410 supports_relative_color: support.supports_relative_color,
1411 supports_logical_properties: support.supports_logical_properties,
1412 supports_css_nesting: support.supports_css_nesting,
1413 supports_css_scope: support.supports_css_scope,
1414 supports_cascade_layers: support.supports_cascade_layers,
1415 }
1416}
1417
1418pub fn execute_omena_query_consumer_build_style_sources_for_target_query_with_options(
1419 target_style_path: &str,
1420 style_sources: &[OmenaQueryStyleSourceInputV0],
1421 target_query: &str,
1422 target_options: OmenaQueryTargetTransformOptionsV0,
1423 package_manifests: &[OmenaQueryStylePackageManifestV0],
1424) -> Result<OmenaQueryConsumerBuildSummaryV0, String> {
1425 execute_omena_query_consumer_build_style_sources_for_target_query_with_context_and_options(
1426 target_style_path,
1427 style_sources,
1428 target_query,
1429 &TransformExecutionContextV0::default(),
1430 target_options,
1431 package_manifests,
1432 )
1433}
1434
1435pub fn attach_omena_query_consumer_build_bundle_summary(
1436 summary: &mut OmenaQueryConsumerBuildSummaryV0,
1437 style_source: &str,
1438) {
1439 let bundle = summarize_omena_transform_bundle_from_source(
1440 &summary.style_path,
1441 style_source,
1442 omena_parser_dialect_for_style_path(&summary.style_path),
1443 );
1444 summary.bundle = Some(bundle);
1445 if !summary.ready_surfaces.contains(&"bundleAssetUrlResolution") {
1446 summary.ready_surfaces.push("bundleAssetUrlResolution");
1447 }
1448 if summary
1449 .bundle
1450 .as_ref()
1451 .is_some_and(|bundle| bundle.code_splitting_required)
1452 && !summary.ready_surfaces.contains(&"bundleCodeSplitPlan")
1453 {
1454 summary.ready_surfaces.push("bundleCodeSplitPlan");
1455 }
1456}
1457
1458pub fn summarize_omena_query_bundle_code_split_workspace_plan(
1459 primary_entry_style_path: &str,
1460 bundle_entry_style_paths: &[String],
1461 style_sources: &[OmenaQueryStyleSourceInputV0],
1462 resolution_inputs: &OmenaQueryStyleResolutionInputsV0,
1463) -> Result<OmenaQueryBundleCodeSplitWorkspacePlanV0, String> {
1464 let source_by_path = style_sources
1465 .iter()
1466 .map(|source| (source.style_path.as_str(), source.style_source.as_str()))
1467 .collect::<BTreeMap<_, _>>();
1468 let available_style_paths = style_sources
1469 .iter()
1470 .map(|source| source.style_path.as_str())
1471 .collect::<BTreeSet<_>>();
1472 let mut entry_style_paths = vec![primary_entry_style_path.to_string()];
1473 for configured_entry in bundle_entry_style_paths {
1474 if configured_entry != primary_entry_style_path
1475 && !entry_style_paths.contains(configured_entry)
1476 {
1477 entry_style_paths.push(configured_entry.clone());
1478 }
1479 }
1480 for entry_style_path in &entry_style_paths {
1481 if !source_by_path.contains_key(entry_style_path.as_str()) {
1482 return Err(format!(
1483 "bundle entry source is not loaded: {entry_style_path}"
1484 ));
1485 }
1486 }
1487
1488 let entry_style_path_set = entry_style_paths.iter().cloned().collect::<BTreeSet<_>>();
1489 let entry_reachability = collect_omena_query_bundle_code_split_entry_reachability(
1490 entry_style_paths.as_slice(),
1491 &source_by_path,
1492 &available_style_paths,
1493 resolution_inputs,
1494 );
1495
1496 let mut outputs = Vec::new();
1497 for (style_path, reachable_from_entries) in entry_reachability {
1498 let split_boundary = omena_query_bundle_code_split_boundary(
1499 style_path.as_str(),
1500 primary_entry_style_path,
1501 &entry_style_path_set,
1502 reachable_from_entries.len(),
1503 );
1504 outputs.push(OmenaQueryBundleCodeSplitWorkspacePlanOutputV0 {
1505 is_entry: entry_style_path_set.contains(style_path.as_str()),
1506 source_path: style_path,
1507 split_boundary,
1508 reachable_from_entries: reachable_from_entries.into_iter().collect(),
1509 });
1510 }
1511 let configured_entry_count = outputs
1512 .iter()
1513 .filter(|output| output.split_boundary == "entryConfig")
1514 .count();
1515 let shared_boundary_count = outputs
1516 .iter()
1517 .filter(|output| output.split_boundary == "shared")
1518 .count();
1519 let mut ready_surfaces = vec!["bundleCodeSplitPlan", "bundleCodeSplitBoundaryPlan"];
1520 if configured_entry_count > 0 {
1521 ready_surfaces.push("bundleCodeSplitEntryConfig");
1522 }
1523 if shared_boundary_count > 0 {
1524 ready_surfaces.push("bundleCodeSplitSharedChunkPlan");
1525 }
1526
1527 Ok(OmenaQueryBundleCodeSplitWorkspacePlanV0 {
1528 schema_version: "0",
1529 product: "omena-query.bundle-code-split-workspace-plan",
1530 primary_entry_style_path: primary_entry_style_path.to_string(),
1531 configured_entry_count,
1532 output_count: outputs.len(),
1533 shared_boundary_count,
1534 outputs,
1535 ready_surfaces,
1536 })
1537}
1538
1539fn collect_omena_query_bundle_code_split_entry_reachability(
1540 entry_style_paths: &[String],
1541 source_by_path: &BTreeMap<&str, &str>,
1542 available_style_paths: &BTreeSet<&str>,
1543 resolution_inputs: &OmenaQueryStyleResolutionInputsV0,
1544) -> BTreeMap<String, BTreeSet<String>> {
1545 let resolution_context = TransformResolutionContext::from_resolution_inputs(resolution_inputs);
1546 let mut reachability = BTreeMap::<String, BTreeSet<String>>::new();
1547
1548 for entry_style_path in entry_style_paths {
1549 let mut visited = BTreeSet::new();
1550 let mut stack = vec![entry_style_path.clone()];
1551
1552 while let Some(style_path) = stack.pop() {
1553 if !visited.insert(style_path.clone()) {
1554 continue;
1555 }
1556 let Some(source) = source_by_path.get(style_path.as_str()) else {
1557 continue;
1558 };
1559 reachability
1560 .entry(style_path.clone())
1561 .or_default()
1562 .insert(entry_style_path.clone());
1563 let bundle = summarize_omena_transform_bundle_from_source(
1564 style_path.as_str(),
1565 source,
1566 omena_parser_dialect_for_style_path(style_path.as_str()),
1567 );
1568 for edge in bundle.bundle_edges {
1569 if !matches!(
1570 edge.kind,
1571 TransformBundleEdgeKind::CssImport | TransformBundleEdgeKind::LessImport
1572 ) {
1573 continue;
1574 }
1575 let Some(import_source) = edge.import_source.as_deref() else {
1576 continue;
1577 };
1578 let Some(target_path) = resolution_context.resolve_style_module_source(
1579 style_path.as_str(),
1580 import_source,
1581 available_style_paths,
1582 ) else {
1583 continue;
1584 };
1585 if source_by_path.contains_key(target_path.as_str()) {
1586 stack.push(target_path);
1587 }
1588 }
1589 }
1590 }
1591
1592 reachability
1593}
1594
1595fn omena_query_bundle_code_split_boundary(
1596 style_path: &str,
1597 primary_entry_style_path: &str,
1598 entry_style_paths: &BTreeSet<String>,
1599 reachable_entry_count: usize,
1600) -> &'static str {
1601 if style_path == primary_entry_style_path {
1602 return "entry";
1603 }
1604 if entry_style_paths.contains(style_path) {
1605 return "entryConfig";
1606 }
1607 if reachable_entry_count > 1 {
1608 return "shared";
1609 }
1610 "styleDependency"
1611}
1612
1613pub fn attach_omena_query_consumer_build_source_map_v3(
1614 summary: &mut OmenaQueryConsumerBuildSummaryV0,
1615 style_source: &str,
1616) {
1617 let style_source = OmenaQueryStyleSourceInputV0 {
1618 style_path: summary.style_path.clone(),
1619 style_source: style_source.to_string(),
1620 };
1621 attach_omena_query_consumer_build_source_map_v3_with_sources(summary, &[style_source], &[]);
1622}
1623
1624pub fn attach_omena_query_consumer_build_source_map_v3_with_sources(
1625 summary: &mut OmenaQueryConsumerBuildSummaryV0,
1626 style_sources: &[OmenaQueryStyleSourceInputV0],
1627 package_manifests: &[OmenaQueryStylePackageManifestV0],
1628) {
1629 let resolution_inputs = resolution_inputs_for_transform_style_sources(
1630 summary.style_path.as_str(),
1631 style_sources,
1632 package_manifests,
1633 );
1634 attach_omena_query_consumer_build_source_map_v3_with_sources_and_resolution_inputs(
1635 summary,
1636 style_sources,
1637 &resolution_inputs,
1638 );
1639}
1640
1641pub fn attach_omena_query_consumer_build_source_map_v3_with_sources_and_resolution_inputs(
1642 summary: &mut OmenaQueryConsumerBuildSummaryV0,
1643 style_sources: &[OmenaQueryStyleSourceInputV0],
1644 resolution_inputs: &OmenaQueryStyleResolutionInputsV0,
1645) {
1646 let source_map = summarize_omena_query_consumer_build_source_map_v3_with_resolution_inputs(
1647 &summary.style_path,
1648 style_sources,
1649 &summary.execution,
1650 resolution_inputs,
1651 );
1652 summary.source_map_v3 = Some(source_map);
1653 if !summary.ready_surfaces.contains(&"sourceMapV3Serializer") {
1654 summary.ready_surfaces.push("sourceMapV3Serializer");
1655 }
1656 if summary
1657 .source_map_v3
1658 .as_ref()
1659 .is_some_and(|source_map| source_map.sources.len() > 1)
1660 && !summary
1661 .ready_surfaces
1662 .contains(&"bundleSourceMapOriginChain")
1663 {
1664 summary.ready_surfaces.push("bundleSourceMapOriginChain");
1665 }
1666}
1667
1668pub fn summarize_omena_query_consumer_build_source_map_v3(
1669 style_path: &str,
1670 style_sources: &[OmenaQueryStyleSourceInputV0],
1671 execution: &TransformExecutionSummaryV0,
1672 package_manifests: &[OmenaQueryStylePackageManifestV0],
1673) -> OmenaQueryTransformSourceMapV3V0 {
1674 let resolution_inputs =
1675 resolution_inputs_for_transform_style_sources(style_path, style_sources, package_manifests);
1676 summarize_omena_query_consumer_build_source_map_v3_with_resolution_inputs(
1677 style_path,
1678 style_sources,
1679 execution,
1680 &resolution_inputs,
1681 )
1682}
1683
1684pub fn summarize_omena_query_consumer_build_source_map_v3_with_resolution_inputs(
1685 style_path: &str,
1686 style_sources: &[OmenaQueryStyleSourceInputV0],
1687 execution: &TransformExecutionSummaryV0,
1688 resolution_inputs: &OmenaQueryStyleResolutionInputsV0,
1689) -> OmenaQueryTransformSourceMapV3V0 {
1690 let source_by_path = style_sources
1691 .iter()
1692 .map(|source| (source.style_path.as_str(), source.style_source.as_str()))
1693 .collect::<BTreeMap<_, _>>();
1694 let style_source = source_by_path.get(style_path).copied().unwrap_or_default();
1695 let dialect = omena_parser_dialect_for_style_path(style_path);
1696 let artifact = print_transform_execution_artifact_with_dialect_and_source(
1697 style_path,
1698 style_source,
1699 dialect,
1700 format!(
1701 "omena-query-consumer-build-source-map-v3:{}:{}",
1702 style_path,
1703 style_source.len()
1704 ),
1705 &[TransformPassKind::PrintCss],
1706 default_omena_query_transform_print_options(),
1707 execution,
1708 );
1709 let available_style_paths = source_by_path.keys().copied().collect::<BTreeSet<_>>();
1710 let mut segments = artifact.source_map_segments.clone();
1711 segments.extend(import_inline_source_map_segments(
1712 style_path,
1713 execution,
1714 &source_by_path,
1715 &available_style_paths,
1716 TransformResolutionContext::from_resolution_inputs(resolution_inputs),
1717 ));
1718 let source_contents = style_sources
1719 .iter()
1720 .map(|source| (source.style_path.as_str(), source.style_source.as_str()))
1721 .collect::<Vec<_>>();
1722 serialize_transform_source_map_v3_with_source_contents(
1723 style_path,
1724 execution.output_css.as_str(),
1725 style_path,
1726 source_contents.as_slice(),
1727 segments.as_slice(),
1728 )
1729}
1730
1731fn summarize_omena_query_linked_bundle_source_map_v3(
1732 style_path: &str,
1733 style_sources: &[OmenaQueryStyleSourceInputV0],
1734 execution: &TransformExecutionSummaryV0,
1735 materialization: &LinkedEmissionArtifactV0,
1736) -> Result<OmenaQueryTransformSourceMapV3V0, String> {
1737 let segments = linked_bundle_source_map_segments(
1738 style_sources,
1739 execution.output_css.as_str(),
1740 materialization,
1741 )?;
1742 let source_contents = style_sources
1743 .iter()
1744 .map(|source| (source.style_path.as_str(), source.style_source.as_str()))
1745 .collect::<Vec<_>>();
1746 Ok(serialize_transform_source_map_v3_with_source_contents(
1747 style_path,
1748 execution.output_css.as_str(),
1749 style_path,
1750 source_contents.as_slice(),
1751 segments.as_slice(),
1752 ))
1753}
1754
1755fn linked_bundle_source_map_segments(
1756 style_sources: &[OmenaQueryStyleSourceInputV0],
1757 generated_css: &str,
1758 materialization: &LinkedEmissionArtifactV0,
1759) -> Result<Vec<TransformSourceMapSegmentV0>, String> {
1760 let source_by_path = style_sources
1761 .iter()
1762 .map(|source| (source.style_path.as_str(), source.style_source.as_str()))
1763 .collect::<BTreeMap<_, _>>();
1764 materialization
1765 .module_regions
1766 .iter()
1767 .map(|region| {
1768 let source_path = region.module_instance.module().as_str();
1769 let source = source_by_path.get(source_path).copied().ok_or_else(|| {
1770 format!("linked source-map module {source_path:?} has no source document")
1771 })?;
1772 if region.generated_start > region.generated_end
1773 || region.generated_end > generated_css.len()
1774 {
1775 return Err(format!(
1776 "linked source-map region for {source_path:?} is outside generated CSS: {}..{} of {}",
1777 region.generated_start,
1778 region.generated_end,
1779 generated_css.len()
1780 ));
1781 }
1782 Ok(TransformSourceMapSegmentV0 {
1783 source_path: source_path.to_string(),
1784 original_start: 0,
1785 original_end: source.len(),
1786 generated_start: region.generated_start,
1787 generated_end: region.generated_end,
1788 original_start_point: transform_source_map_point(source, 0),
1789 original_end_point: transform_source_map_point(source, source.len()),
1790 generated_start_point: transform_source_map_point(
1791 generated_css,
1792 region.generated_start,
1793 ),
1794 generated_end_point: transform_source_map_point(
1795 generated_css,
1796 region.generated_end,
1797 ),
1798 pass_id: "linked-order-emission",
1799 })
1800 })
1801 .collect()
1802}
1803
1804pub fn summarize_omena_query_bundle_code_split_source_map_v3(
1805 output_file_name: &str,
1806 generated_css: &str,
1807 source_path: &str,
1808 source_content: &str,
1809) -> OmenaQueryTransformSourceMapV3V0 {
1810 let segment = TransformSourceMapSegmentV0 {
1811 source_path: source_path.to_string(),
1812 original_start: 0,
1813 original_end: source_content.len(),
1814 generated_start: 0,
1815 generated_end: generated_css.len(),
1816 original_start_point: transform_source_map_point(source_content, 0),
1817 original_end_point: transform_source_map_point(source_content, source_content.len()),
1818 generated_start_point: transform_source_map_point(generated_css, 0),
1819 generated_end_point: transform_source_map_point(generated_css, generated_css.len()),
1820 pass_id: "code-split-emission",
1821 };
1822 serialize_transform_source_map_v3_with_source_contents(
1823 output_file_name,
1824 generated_css,
1825 source_path,
1826 &[(source_path, source_content)],
1827 &[segment],
1828 )
1829}
1830
1831fn import_inline_source_map_segments(
1832 style_path: &str,
1833 execution: &TransformExecutionSummaryV0,
1834 source_by_path: &BTreeMap<&str, &str>,
1835 available_style_paths: &BTreeSet<&str>,
1836 resolution_context: TransformResolutionContext<'_>,
1837) -> Vec<TransformSourceMapSegmentV0> {
1838 let mut segments = Vec::new();
1839 let mut seen_segments = BTreeSet::new();
1840 extend_import_graph_source_map_segments(
1841 &mut segments,
1842 &mut seen_segments,
1843 style_path,
1844 execution,
1845 source_by_path,
1846 available_style_paths,
1847 resolution_context,
1848 );
1849 let mut search_start = 0;
1850 for inline in &execution.css_import_inlines {
1851 if inline.replacement_css.is_empty() || search_start > execution.output_css.len() {
1852 continue;
1853 }
1854 let Some(resolved_style_path) = resolution_context.resolve_style_module_source(
1855 style_path,
1856 inline.import_source.as_str(),
1857 available_style_paths,
1858 ) else {
1859 continue;
1860 };
1861 let Some(imported_source) = source_by_path.get(resolved_style_path.as_str()).copied()
1862 else {
1863 continue;
1864 };
1865 let Some((generated_start, generated_end, _exact_match)) =
1866 find_import_origin_generated_range(
1867 execution.output_css.as_str(),
1868 search_start..execution.output_css.len(),
1869 &inline.replacement_css,
1870 resolved_style_path.as_str(),
1871 imported_source,
1872 )
1873 else {
1874 continue;
1875 };
1876 push_unique_import_origin_segment(
1877 &mut segments,
1878 &mut seen_segments,
1879 resolved_style_path,
1880 imported_source,
1881 execution.output_css.as_str(),
1882 generated_start,
1883 generated_end,
1884 );
1885 search_start = generated_end;
1886 }
1887 segments
1888}
1889
1890fn extend_import_graph_source_map_segments(
1891 segments: &mut Vec<TransformSourceMapSegmentV0>,
1892 seen_segments: &mut BTreeSet<(String, usize, usize, &'static str)>,
1893 style_path: &str,
1894 execution: &TransformExecutionSummaryV0,
1895 source_by_path: &BTreeMap<&str, &str>,
1896 available_style_paths: &BTreeSet<&str>,
1897 resolution_context: TransformResolutionContext<'_>,
1898) {
1899 let style_sources = source_by_path
1900 .iter()
1901 .map(|(style_path, style_source)| (*style_path, *style_source))
1902 .collect::<Vec<_>>();
1903 let style_fact_entries = collect_omena_query_style_fact_entries(style_sources.as_slice());
1904 let entries_by_path = style_fact_entries
1905 .iter()
1906 .map(|entry| (entry.style_path.as_str(), entry))
1907 .collect::<BTreeMap<_, _>>();
1908 let owned_source_by_path = source_by_path
1909 .iter()
1910 .map(|(style_path, style_source)| ((*style_path).to_string(), (*style_source).to_string()))
1911 .collect::<BTreeMap<_, _>>();
1912 let mut visiting = BTreeSet::new();
1913 let context = ImportGraphSourceMapSegmentContext {
1914 output_css: execution.output_css.as_str(),
1915 entries_by_path: &entries_by_path,
1916 owned_source_by_path: &owned_source_by_path,
1917 source_by_path,
1918 available_style_paths,
1919 resolution_context,
1920 };
1921 collect_import_graph_source_map_segments(
1922 segments,
1923 seen_segments,
1924 style_path,
1925 0,
1926 execution.output_css.len(),
1927 &context,
1928 &mut visiting,
1929 );
1930}
1931
1932struct ImportGraphSourceMapSegmentContext<'a> {
1933 output_css: &'a str,
1934 entries_by_path: &'a BTreeMap<&'a str, &'a OmenaQueryStyleFactEntry>,
1935 owned_source_by_path: &'a BTreeMap<String, String>,
1936 source_by_path: &'a BTreeMap<&'a str, &'a str>,
1937 available_style_paths: &'a BTreeSet<&'a str>,
1938 resolution_context: TransformResolutionContext<'a>,
1939}
1940
1941fn collect_import_graph_source_map_segments(
1942 segments: &mut Vec<TransformSourceMapSegmentV0>,
1943 seen_segments: &mut BTreeSet<(String, usize, usize, &'static str)>,
1944 importer_style_path: &str,
1945 generated_start_bound: usize,
1946 generated_end_bound: usize,
1947 context: &ImportGraphSourceMapSegmentContext<'_>,
1948 visiting: &mut BTreeSet<String>,
1949) {
1950 if !visiting.insert(importer_style_path.to_string()) {
1951 return;
1952 }
1953 let Some(entry) = context.entries_by_path.get(importer_style_path) else {
1954 visiting.remove(importer_style_path);
1955 return;
1956 };
1957
1958 for edge in entry
1959 .facts
1960 .sass_module_edges
1961 .iter()
1962 .filter(|edge| edge.kind == "sassImport")
1963 {
1964 let Some(resolved_style_path) = context.resolution_context.resolve_style_module_source(
1965 importer_style_path,
1966 edge.source.as_str(),
1967 context.available_style_paths,
1968 ) else {
1969 continue;
1970 };
1971 let Some(imported_source) = context
1972 .source_by_path
1973 .get(resolved_style_path.as_str())
1974 .copied()
1975 else {
1976 continue;
1977 };
1978 let Some(replacement_css) = resolve_import_inline_replacement_for_transform_context(
1979 resolved_style_path.as_str(),
1980 context.entries_by_path,
1981 context.available_style_paths,
1982 context.owned_source_by_path,
1983 context.resolution_context,
1984 &mut BTreeSet::new(),
1985 ) else {
1986 continue;
1987 };
1988 if replacement_css.is_empty() || generated_start_bound > generated_end_bound {
1989 continue;
1990 }
1991 let Some((generated_start, generated_end, exact_match)) =
1992 find_import_origin_generated_range(
1993 context.output_css,
1994 generated_start_bound..generated_end_bound,
1995 replacement_css.as_str(),
1996 resolved_style_path.as_str(),
1997 imported_source,
1998 )
1999 else {
2000 continue;
2001 };
2002 push_unique_import_origin_segment(
2003 segments,
2004 seen_segments,
2005 resolved_style_path.clone(),
2006 imported_source,
2007 context.output_css,
2008 generated_start,
2009 generated_end,
2010 );
2011 collect_import_graph_source_map_segments(
2012 segments,
2013 seen_segments,
2014 resolved_style_path.as_str(),
2015 if exact_match {
2016 generated_start
2017 } else {
2018 generated_start_bound
2019 },
2020 if exact_match {
2021 generated_end
2022 } else {
2023 generated_end_bound
2024 },
2025 context,
2026 visiting,
2027 );
2028 }
2029
2030 visiting.remove(importer_style_path);
2031}
2032
2033fn find_import_origin_generated_range(
2034 output_css: &str,
2035 search_range: std::ops::Range<usize>,
2036 replacement_css: &str,
2037 source_path: &str,
2038 source: &str,
2039) -> Option<(usize, usize, bool)> {
2040 if search_range.start > search_range.end || search_range.end > output_css.len() {
2041 return None;
2042 }
2043 if let Some(relative_start) = output_css[search_range.clone()].find(replacement_css) {
2044 let generated_start = search_range.start + relative_start;
2045 return Some((
2046 generated_start,
2047 generated_start + replacement_css.len(),
2048 true,
2049 ));
2050 }
2051
2052 let runtime_index =
2053 omena_semantic::summarize_style_runtime_index_facts_from_source(source_path, source);
2054 let mut candidate_needles = Vec::new();
2055 if let Some(runtime_index) = runtime_index {
2056 candidate_needles.extend(
2057 runtime_index
2058 .class_selector_names
2059 .iter()
2060 .map(|name| format!(".{name}")),
2061 );
2062 candidate_needles.extend(runtime_index.custom_property_names.iter().cloned());
2063 candidate_needles.extend(
2064 runtime_index
2065 .keyframe_names
2066 .iter()
2067 .map(|name| format!("@keyframes {name}")),
2068 );
2069 } else {
2070 let facts = summarize_omena_query_omena_parser_style_facts(
2071 source,
2072 omena_parser_dialect_for_style_path(source_path),
2073 );
2074 candidate_needles.extend(
2075 facts
2076 .class_selector_names
2077 .iter()
2078 .map(|name| format!(".{name}")),
2079 );
2080 candidate_needles.extend(facts.custom_property_names.iter().cloned());
2081 candidate_needles.extend(
2082 facts
2083 .keyframe_names
2084 .iter()
2085 .map(|name| format!("@keyframes {name}")),
2086 );
2087 }
2088
2089 let mut generated_start = None;
2090 let mut generated_end = None;
2091 for needle in candidate_needles {
2092 if needle.is_empty() {
2093 continue;
2094 }
2095 let Some(relative_start) = output_css[search_range.clone()].find(needle.as_str()) else {
2096 continue;
2097 };
2098 let start = search_range.start + relative_start;
2099 let end = start + needle.len();
2100 generated_start = Some(generated_start.map_or(start, |current: usize| current.min(start)));
2101 generated_end = Some(generated_end.map_or(end, |current: usize| current.max(end)));
2102 }
2103
2104 match (generated_start, generated_end) {
2105 (Some(start), Some(end)) if start < end => Some((start, end, false)),
2106 _ => None,
2107 }
2108}
2109
2110fn push_unique_import_origin_segment(
2111 segments: &mut Vec<TransformSourceMapSegmentV0>,
2112 seen_segments: &mut BTreeSet<(String, usize, usize, &'static str)>,
2113 source_path: String,
2114 source: &str,
2115 output_css: &str,
2116 generated_start: usize,
2117 generated_end: usize,
2118) {
2119 let pass_id = TransformPassKind::ImportInline.id();
2120 if !seen_segments.insert((source_path.clone(), generated_start, generated_end, pass_id)) {
2121 return;
2122 }
2123 segments.push(TransformSourceMapSegmentV0 {
2124 source_path,
2125 original_start: 0,
2126 original_end: source.len(),
2127 generated_start,
2128 generated_end,
2129 original_start_point: transform_source_map_point(source, 0),
2130 original_end_point: transform_source_map_point(source, source.len()),
2131 generated_start_point: transform_source_map_point(output_css, generated_start),
2132 generated_end_point: transform_source_map_point(output_css, generated_end),
2133 pass_id,
2134 });
2135}
2136
2137fn derive_single_source_transform_context(
2138 style_path: &str,
2139 style_source: &str,
2140) -> TransformExecutionContextV0 {
2141 summarize_omena_query_transform_context_from_sources(
2142 style_path,
2143 [(style_path, style_source)],
2144 &[],
2145 )
2146 .context
2147}
2148
2149fn resolution_inputs_for_transform_style_sources(
2150 target_style_path: &str,
2151 style_sources: &[OmenaQueryStyleSourceInputV0],
2152 package_manifests: &[OmenaQueryStylePackageManifestV0],
2153) -> OmenaQueryStyleResolutionInputsV0 {
2154 let workspace_uri = infer_transform_workspace_uri(target_style_path, style_sources);
2155 load_omena_query_workspace_style_resolution_inputs(workspace_uri.as_deref(), package_manifests)
2156}
2157
2158fn infer_transform_workspace_uri(
2159 target_style_path: &str,
2160 style_sources: &[OmenaQueryStyleSourceInputV0],
2161) -> Option<String> {
2162 let target_path = path_from_transform_style_path(target_style_path);
2163 let target_parent = target_path.as_deref().and_then(Path::parent);
2164 if let Some(root) = target_parent.and_then(discover_transform_workspace_root) {
2165 return Some(transform_path_to_file_uri(root));
2166 }
2167
2168 style_sources
2169 .iter()
2170 .filter_map(|source| path_from_transform_style_path(source.style_path.as_str()))
2171 .filter_map(|path| {
2172 path.parent()
2173 .and_then(discover_transform_workspace_root)
2174 .map(transform_path_to_file_uri)
2175 })
2176 .next()
2177}
2178
2179fn path_from_transform_style_path(style_path: &str) -> Option<PathBuf> {
2180 if let Some(path) = style_path.strip_prefix("file://") {
2181 return Some(PathBuf::from(path));
2182 }
2183 if style_path.starts_with('/') {
2184 return Some(PathBuf::from(style_path));
2185 }
2186 None
2187}
2188
2189fn discover_transform_workspace_root(path: &Path) -> Option<&Path> {
2190 path.ancestors().find(|candidate| {
2191 [
2192 "tsconfig.json",
2193 "tsconfig.base.json",
2194 "jsconfig.json",
2195 "package.json",
2196 "vite.config.ts",
2197 "vite.config.mts",
2198 "vite.config.cts",
2199 "vite.config.js",
2200 "vite.config.mjs",
2201 "vite.config.cjs",
2202 "webpack.config.ts",
2203 "webpack.config.mts",
2204 "webpack.config.cts",
2205 "webpack.config.js",
2206 "webpack.config.mjs",
2207 "webpack.config.cjs",
2208 "next.config.ts",
2209 "next.config.mts",
2210 "next.config.cts",
2211 "next.config.js",
2212 "next.config.mjs",
2213 "next.config.cjs",
2214 ]
2215 .iter()
2216 .any(|marker| candidate.join(marker).is_file())
2217 })
2218}
2219
2220fn transform_path_to_file_uri(path: &Path) -> String {
2221 format!("file://{}", path.to_string_lossy())
2222}
2223
2224fn merge_single_source_transform_context(
2225 style_path: &str,
2226 style_source: &str,
2227 context: &TransformExecutionContextV0,
2228) -> TransformExecutionContextV0 {
2229 merge_transform_context(
2230 derive_single_source_transform_context(style_path, style_source),
2231 context,
2232 )
2233}
2234
2235fn merge_workspace_transform_context(
2236 target_style_path: &str,
2237 style_sources: &[OmenaQueryStyleSourceInputV0],
2238 context: &TransformExecutionContextV0,
2239 resolution_context: TransformResolutionContext<'_>,
2240) -> TransformExecutionContextV0 {
2241 let style_refs = style_sources
2242 .iter()
2243 .map(|source| (source.style_path.as_str(), source.style_source.as_str()))
2244 .collect::<Vec<_>>();
2245 let derived = summarize_omena_query_transform_context_from_sources_with_resolution_context(
2246 target_style_path,
2247 style_refs,
2248 resolution_context,
2249 )
2250 .context;
2251 merge_transform_context(derived, context)
2252}
2253
2254pub fn list_omena_query_transform_pass_summaries() -> Vec<OmenaQueryTransformPassSummaryV0> {
2255 all_transform_pass_kinds()
2256 .into_iter()
2257 .map(|kind| OmenaQueryTransformPassSummaryV0 {
2258 id: kind.id(),
2259 title: kind.title(),
2260 reads_semantic_graph: kind.reads_semantic_graph(),
2261 reads_cascade_model: kind.reads_cascade_model(),
2262 explicit_opt_in_required: kind.explicit_opt_in_required(),
2263 dialect_restriction: kind.dialect_restriction(),
2264 spec_snapshot: kind.spec_snapshot(),
2265 opt_in_policy: kind.opt_in_policy(),
2266 })
2267 .collect()
2268}
2269
2270pub fn execute_omena_query_transform_passes_from_source_with_context(
2271 style_path: &str,
2272 style_source: &str,
2273 requested_pass_ids: &[String],
2274 context: &TransformExecutionContextV0,
2275) -> OmenaQueryTransformExecuteSummaryV0 {
2276 let context = merge_single_source_transform_context(style_path, style_source, context);
2277 if pass_ids_require_closed_world_bundle(requested_pass_ids)
2278 && let Some(closed_world_bundle) = build_closed_world_bundle_for_single_style_source_context(
2279 style_path,
2280 style_source,
2281 requested_pass_ids,
2282 &context,
2283 )
2284 {
2285 return execute_omena_query_transform_passes_from_source_with_context_and_closed_world_bundle(
2286 style_path,
2287 style_source,
2288 requested_pass_ids,
2289 &context,
2290 &closed_world_bundle,
2291 classify_transform_reachability_precision(&context, true, None),
2292 &TransformExecutionPolicyV0::default(),
2293 );
2294 }
2295
2296 execute_omena_query_transform_passes_from_source_with_open_world_context(
2297 style_path,
2298 style_source,
2299 requested_pass_ids,
2300 &context,
2301 &TransformExecutionPolicyV0::default(),
2302 )
2303}
2304
2305fn execute_omena_query_transform_passes_from_source_with_open_world_context(
2306 style_path: &str,
2307 style_source: &str,
2308 requested_pass_ids: &[String],
2309 context: &TransformExecutionContextV0,
2310 execution_policy: &TransformExecutionPolicyV0,
2311) -> OmenaQueryTransformExecuteSummaryV0 {
2312 let (requested_passes, unknown_pass_ids) =
2313 requested_transform_passes_from_ids(requested_pass_ids);
2314
2315 let (admitted_passes, preflight_refusals) = strict_query_preflight(
2316 requested_pass_ids,
2317 requested_passes,
2318 execution_policy,
2319 false,
2320 );
2321 let expected_decision_count = admitted_passes.len();
2322
2323 let dialect = omena_parser_dialect_for_style_path(style_path);
2324 let mut execution = execute_transform_passes_on_source_with_dialect_context_and_policy(
2325 style_source,
2326 dialect,
2327 &admitted_passes,
2328 context,
2329 execution_policy,
2330 );
2331 merge_strict_preflight_refusals(&mut execution, preflight_refusals);
2332 enforce_strict_decision_coverage(&mut execution, execution_policy, expected_decision_count);
2333 let semantic_removal_count = execution.semantic_removals.len();
2334 let open_world_snapshot = open_world_snapshot_for_closed_world_passes(requested_pass_ids);
2335 let ready_surfaces = transform_execute_ready_surfaces_with_open_world_snapshot(
2336 open_world_snapshot.as_ref(),
2337 vec!["transformExecutionRuntime", "transformPassOutcomeContract"],
2338 );
2339
2340 OmenaQueryTransformExecuteSummaryV0 {
2341 schema_version: "0",
2342 product: "omena-query.transform-execute",
2343 style_path: style_path.to_string(),
2344 requested_pass_ids: requested_pass_ids.to_vec(),
2345 unknown_pass_ids,
2346 execution,
2347 semantic_removal_count,
2348 open_world_snapshot,
2349 ready_surfaces,
2350 }
2351}
2352
2353fn execute_omena_query_transform_passes_from_source_with_context_and_closed_world_bundle(
2354 style_path: &str,
2355 style_source: &str,
2356 requested_pass_ids: &[String],
2357 context: &TransformExecutionContextV0,
2358 closed_world_bundle: &ClosedWorldBundleV0,
2359 reachability_precision: FactPrecision,
2360 execution_policy: &TransformExecutionPolicyV0,
2361) -> OmenaQueryTransformExecuteSummaryV0 {
2362 let (requested_passes, unknown_pass_ids) =
2363 requested_transform_passes_from_ids(requested_pass_ids);
2364
2365 let (admitted_passes, preflight_refusals) =
2366 strict_query_preflight(requested_pass_ids, requested_passes, execution_policy, true);
2367 let expected_decision_count = admitted_passes.len();
2368
2369 let dialect = omena_parser_dialect_for_style_path(style_path);
2370 let mut execution = execute_transform_passes_on_source_with_dialect_context_closed_world_bundle_precision_and_policy(
2371 style_source,
2372 dialect,
2373 &admitted_passes,
2374 context,
2375 closed_world_bundle,
2376 reachability_precision,
2377 execution_policy,
2378 );
2379 merge_strict_preflight_refusals(&mut execution, preflight_refusals);
2380 enforce_strict_decision_coverage(&mut execution, execution_policy, expected_decision_count);
2381 let semantic_removal_count = execution.semantic_removals.len();
2382
2383 OmenaQueryTransformExecuteSummaryV0 {
2384 schema_version: "0",
2385 product: "omena-query.transform-execute",
2386 style_path: style_path.to_string(),
2387 requested_pass_ids: requested_pass_ids.to_vec(),
2388 unknown_pass_ids,
2389 execution,
2390 semantic_removal_count,
2391 open_world_snapshot: None,
2392 ready_surfaces: vec![
2393 "transformExecutionRuntime",
2394 "transformPassOutcomeContract",
2395 "closedWorldBundle",
2396 ],
2397 }
2398}
2399
2400fn strict_query_preflight(
2401 requested_pass_ids: &[String],
2402 requested_passes: Vec<TransformPassKind>,
2403 execution_policy: &TransformExecutionPolicyV0,
2404 has_closed_world_bundle: bool,
2405) -> (Vec<TransformPassKind>, Vec<TransformStrictPolicyEventV0>) {
2406 let Some(policy) = execution_policy.strict_policy.as_ref() else {
2407 return (requested_passes, Vec::new());
2408 };
2409 let requirements = OmenaQueryBuildAdmissionRequirementsV0 {
2410 refuse_unknown_pass_ids: policy.refuse_unknown_pass_ids,
2411 require_closed_world_evidence: policy.require_closed_world_evidence,
2412 require_complete_decisions: policy.require_complete_decisions,
2413 };
2414 let refusals = summarize_omena_query_build_preflight_refusals(
2415 requested_pass_ids,
2416 has_closed_world_bundle,
2417 requirements,
2418 );
2419 let refused_pass_ids = refusals
2420 .iter()
2421 .map(|event| event.pass_id.as_str())
2422 .collect::<BTreeSet<_>>();
2423 let admitted_passes = requested_passes
2424 .into_iter()
2425 .filter(|pass| !refused_pass_ids.contains(pass.id()))
2426 .collect();
2427 (admitted_passes, refusals)
2428}
2429
2430pub fn summarize_omena_query_build_preflight_refusals(
2431 pass_ids: &[String],
2432 has_closed_world_bundle: bool,
2433 requirements: OmenaQueryBuildAdmissionRequirementsV0,
2434) -> Vec<TransformStrictPolicyEventV0> {
2435 let mut seen = BTreeSet::new();
2436 pass_ids
2437 .iter()
2438 .filter(|pass_id| seen.insert(pass_id.as_str()))
2439 .filter_map(|pass_id| match transform_pass_kind_from_id(pass_id) {
2440 None if requirements.refuse_unknown_pass_ids => Some(TransformStrictPolicyEventV0 {
2441 pass_id: pass_id.clone(),
2442 reasons: vec![TransformStrictPolicyReasonV0::UnknownPass],
2443 }),
2444 Some(pass)
2445 if requirements.require_closed_world_evidence
2446 && transform_pass_requires_closed_world_bundle(pass)
2447 && !has_closed_world_bundle =>
2448 {
2449 Some(TransformStrictPolicyEventV0 {
2450 pass_id: pass_id.clone(),
2451 reasons: vec![TransformStrictPolicyReasonV0::ClosedWorldEvidenceUnavailable],
2452 })
2453 }
2454 _ => None,
2455 })
2456 .collect()
2457}
2458
2459pub fn summarize_omena_query_build_decision_coverage_refusal(
2460 decision_coverage_complete: bool,
2461 requirements: OmenaQueryBuildAdmissionRequirementsV0,
2462) -> Option<TransformStrictPolicyEventV0> {
2463 (requirements.require_complete_decisions && !decision_coverage_complete).then(|| {
2464 TransformStrictPolicyEventV0 {
2465 pass_id: "execution-plan".to_string(),
2466 reasons: vec![TransformStrictPolicyReasonV0::DecisionCoverageIncomplete],
2467 }
2468 })
2469}
2470
2471fn merge_strict_preflight_refusals(
2472 execution: &mut TransformExecutionSummaryV0,
2473 refusals: Vec<TransformStrictPolicyEventV0>,
2474) {
2475 for refusal in refusals {
2476 execution
2477 .strict_policy
2478 .record_refusal(refusal.pass_id, refusal.reasons);
2479 }
2480}
2481
2482fn enforce_strict_decision_coverage(
2483 execution: &mut TransformExecutionSummaryV0,
2484 execution_policy: &TransformExecutionPolicyV0,
2485 expected_decision_count: usize,
2486) {
2487 let requirements = execution_policy
2488 .strict_policy
2489 .as_ref()
2490 .map(|policy| OmenaQueryBuildAdmissionRequirementsV0 {
2491 refuse_unknown_pass_ids: policy.refuse_unknown_pass_ids,
2492 require_closed_world_evidence: policy.require_closed_world_evidence,
2493 require_complete_decisions: policy.require_complete_decisions,
2494 })
2495 .unwrap_or_default();
2496 if let Some(refusal) = summarize_omena_query_build_decision_coverage_refusal(
2497 execution.decisions.len() == expected_decision_count,
2498 requirements,
2499 ) {
2500 execution
2501 .strict_policy
2502 .record_refusal(refusal.pass_id, refusal.reasons);
2503 }
2504}
2505
2506#[cfg(feature = "lawvere-trace")]
2507pub fn execute_omena_query_transform_passes_from_source_with_lawvere_trace(
2508 style_path: &str,
2509 style_source: &str,
2510 requested_pass_ids: &[String],
2511) -> OmenaQueryLawvereTransformExecuteSummaryV0 {
2512 let execution = execute_omena_query_transform_passes_from_source(
2513 style_path,
2514 style_source,
2515 requested_pass_ids,
2516 );
2517 let requested_passes = requested_pass_ids
2518 .iter()
2519 .filter_map(|pass_id| transform_pass_kind_from_id(pass_id))
2520 .collect::<Vec<_>>();
2521 let dialect = omena_parser_dialect_for_style_path(style_path);
2522 let (_traced_execution, lawvere_trace) =
2523 execute_transform_passes_on_source_with_lawvere_trace_and_dialect(
2524 style_source,
2525 dialect,
2526 requested_passes.as_slice(),
2527 );
2528 let parallel_plan = plan_transform_passes_parallel_lawvere_layers(requested_passes.as_slice());
2529 let mut reorderability_certificates = Vec::new();
2530 let mut differential_witnesses = Vec::new();
2531
2532 if let Some((left, right)) = requested_passes.first().zip(requested_passes.get(1)) {
2533 let (certificate, witness) = evaluate_lawvere_reorderability_with_differential_corpus(
2534 *left,
2535 *right,
2536 &[style_source],
2537 );
2538 reorderability_certificates.push(certificate);
2539 differential_witnesses.push(witness);
2540 }
2541
2542 OmenaQueryLawvereTransformExecuteSummaryV0 {
2543 schema_version: "0",
2544 product: "omena-query.transform-execute-lawvere-trace",
2545 product_scope: "explicitOptInLawvereTraceProductLane",
2546 default_product_mechanism: false,
2547 global_transform_theorem_claimed: false,
2548 execution,
2549 lawvere_trace,
2550 parallel_plan,
2551 reorderability_certificates,
2552 differential_witnesses,
2553 ready_surfaces: vec![
2554 "queryTransformExecutionHandoff",
2555 "lawvereModelTrace",
2556 "lawvereParallelPlanTrace",
2557 "lawvereDifferentialReorderabilityCertificate",
2558 ],
2559 }
2560}
2561
2562pub fn summarize_omena_query_transform_context_from_sources<'a>(
2563 target_style_path: &str,
2564 styles: impl IntoIterator<Item = (&'a str, &'a str)>,
2565 package_manifests: &[OmenaQueryStylePackageManifestV0],
2566) -> OmenaQueryTransformContextFromSourcesSummaryV0 {
2567 let styles = styles.into_iter().collect::<Vec<_>>();
2568 let style_sources = styles
2569 .iter()
2570 .map(|(style_path, style_source)| OmenaQueryStyleSourceInputV0 {
2571 style_path: (*style_path).to_string(),
2572 style_source: (*style_source).to_string(),
2573 })
2574 .collect::<Vec<_>>();
2575 let resolution_inputs = resolution_inputs_for_transform_style_sources(
2576 target_style_path,
2577 style_sources.as_slice(),
2578 package_manifests,
2579 );
2580 summarize_omena_query_transform_context_from_sources_with_resolution_context(
2581 target_style_path,
2582 styles,
2583 TransformResolutionContext::from_resolution_inputs(&resolution_inputs),
2584 )
2585}
2586
2587pub fn summarize_omena_query_transform_context_from_sources_with_resolution_inputs<'a>(
2588 target_style_path: &str,
2589 styles: impl IntoIterator<Item = (&'a str, &'a str)>,
2590 resolution_inputs: &OmenaQueryStyleResolutionInputsV0,
2591) -> OmenaQueryTransformContextFromSourcesSummaryV0 {
2592 summarize_omena_query_transform_context_from_sources_with_resolution_context(
2593 target_style_path,
2594 styles,
2595 TransformResolutionContext::from_resolution_inputs(resolution_inputs),
2596 )
2597}
2598
2599fn apply_transform_source_replacements(
2600 source: &str,
2601 mut replacements: Vec<(usize, usize, String)>,
2602) -> (String, usize) {
2603 if replacements.is_empty() {
2604 return (source.to_string(), 0);
2605 }
2606 replacements.sort_by_key(|replacement| replacement.0);
2607 let mut output = source.to_string();
2608 let mut mutation_count = 0usize;
2609 for (start, end, replacement) in replacements.into_iter().rev() {
2610 if start > end || end > output.len() {
2611 continue;
2612 }
2613 output.replace_range(start..end, replacement.as_str());
2614 mutation_count += 1;
2615 }
2616 (output, mutation_count)
2617}
2618
2619fn transform_token_start(token: &omena_parser::LexedToken) -> usize {
2620 let start: u32 = token.range.start().into();
2621 start as usize
2622}
2623
2624fn transform_token_end(token: &omena_parser::LexedToken) -> usize {
2625 let end: u32 = token.range.end().into();
2626 end as usize
2627}
2628
2629fn extend_passes_from_ids(ids: &[&'static str], passes: &mut Vec<TransformPassKind>) {
2630 for candidate in all_transform_pass_kinds() {
2631 if ids.contains(&candidate.id()) && !passes.contains(&candidate) {
2632 passes.push(candidate);
2633 }
2634 }
2635}
2636
2637fn requested_transform_passes_from_ids(
2638 requested_pass_ids: &[String],
2639) -> (Vec<TransformPassKind>, Vec<String>) {
2640 let mut requested_passes = Vec::new();
2641 let mut unknown_pass_ids = Vec::new();
2642
2643 for pass_id in requested_pass_ids {
2644 match transform_pass_kind_from_id(pass_id) {
2645 Some(pass) => requested_passes.push(pass),
2646 None => unknown_pass_ids.push(pass_id.clone()),
2647 }
2648 }
2649
2650 (requested_passes, unknown_pass_ids)
2651}
2652
2653fn pass_ids_require_closed_world_bundle(pass_ids: &[String]) -> bool {
2654 pass_ids
2655 .iter()
2656 .filter_map(|pass_id| transform_pass_kind_from_id(pass_id))
2657 .any(transform_pass_requires_closed_world_bundle)
2658}
2659
2660fn open_world_snapshot_for_closed_world_passes(pass_ids: &[String]) -> Option<OpenWorldSnapshotV0> {
2661 if !pass_ids_require_closed_world_bundle(pass_ids) {
2662 return None;
2663 }
2664
2665 Some(OpenWorldSnapshotV0::new(format!(
2666 "closed-world bundle unavailable for requested passes: {}",
2667 pass_ids.join(", ")
2668 )))
2669}
2670
2671fn consumer_build_ready_surfaces_with_open_world_snapshot(
2672 snapshot: Option<&OpenWorldSnapshotV0>,
2673 mut ready_surfaces: Vec<&'static str>,
2674) -> Vec<&'static str> {
2675 if snapshot.is_some() && !ready_surfaces.contains(&"openWorldSnapshot") {
2676 ready_surfaces.push("openWorldSnapshot");
2677 }
2678 ready_surfaces
2679}
2680
2681fn extend_ready_surfaces(
2682 mut ready_surfaces: Vec<&'static str>,
2683 additions: impl IntoIterator<Item = &'static str>,
2684) -> Vec<&'static str> {
2685 for surface in additions {
2686 if !ready_surfaces.contains(&surface) {
2687 ready_surfaces.push(surface);
2688 }
2689 }
2690 ready_surfaces
2691}
2692
2693fn transform_execute_ready_surfaces_with_open_world_snapshot(
2694 snapshot: Option<&OpenWorldSnapshotV0>,
2695 ready_surfaces: Vec<&'static str>,
2696) -> Vec<&'static str> {
2697 consumer_build_ready_surfaces_with_open_world_snapshot(snapshot, ready_surfaces)
2698}
2699
2700fn requested_pass_ids_include_tree_shake(requested_pass_ids: &[String]) -> bool {
2701 requested_pass_ids
2702 .iter()
2703 .filter_map(|pass_id| transform_pass_kind_from_id(pass_id))
2704 .any(|pass| {
2705 matches!(
2706 pass,
2707 TransformPassKind::TreeShakeClass
2708 | TransformPassKind::TreeShakeKeyframes
2709 | TransformPassKind::TreeShakeValue
2710 | TransformPassKind::TreeShakeCustomProperty
2711 )
2712 })
2713}
2714
2715fn build_closed_world_outcome_for_style_sources(
2716 target_style_path: &str,
2717 style_sources: &[OmenaQueryStyleSourceInputV0],
2718 requested_pass_ids: &[String],
2719 context: &TransformExecutionContextV0,
2720 external_sifs: &[OmenaQueryExternalSifInputV0],
2721) -> OmenaQueryClosedWorldOutcomeV0 {
2722 closed_world_outcome_from_link_result(
2723 link_closed_world_stylesheet_for_style_sources(
2724 target_style_path,
2725 style_sources,
2726 requested_pass_ids,
2727 context,
2728 external_sifs,
2729 TransformBundleLinkOptionsV0::default(),
2730 ),
2731 requested_pass_ids,
2732 )
2733}
2734
2735fn link_closed_world_stylesheet_for_style_sources(
2736 target_style_path: &str,
2737 style_sources: &[OmenaQueryStyleSourceInputV0],
2738 requested_pass_ids: &[String],
2739 context: &TransformExecutionContextV0,
2740 external_sifs: &[OmenaQueryExternalSifInputV0],
2741 link_options: TransformBundleLinkOptionsV0,
2742) -> Result<LinkedStylesheetV0, TransformBundleLinkErrorV0> {
2743 let reachability_inputs = if requested_pass_ids_include_tree_shake(requested_pass_ids) {
2744 style_sources
2745 .iter()
2746 .filter_map(|source| {
2747 transform_bundle_semantic_reachability_input_from_context(
2748 source.style_path.as_str(),
2749 context,
2750 )
2751 })
2752 .collect::<Vec<_>>()
2753 } else {
2754 Vec::new()
2755 };
2756 let modules = style_sources_to_transform_bundle_modules(style_sources);
2757 let module_metadata =
2758 style_sources_to_closed_world_metadata(style_sources, &modules, context, external_sifs);
2759 link_omena_transform_bundle_modules_with_options(
2760 &[target_style_path],
2761 &modules,
2762 reachability_inputs.as_slice(),
2763 &module_metadata,
2764 link_options,
2765 )
2766}
2767
2768fn execute_linked_bundle_modules(
2769 linked: &LinkedStylesheetV0,
2770 target_style_path: &str,
2771 style_sources: &[OmenaQueryStyleSourceInputV0],
2772 effective_pass_ids: &[String],
2773 base_context: &TransformExecutionContextV0,
2774 resolution_inputs: &OmenaQueryStyleResolutionInputsV0,
2775 options: &OmenaQueryConsumerBuildOptionsV0,
2776) -> Result<LinkedBundleExecutionV0, String> {
2777 let pass_set = consumer_build_pass_set(effective_pass_ids);
2778 let resolution_context = TransformResolutionContext::from_resolution_inputs(resolution_inputs);
2779 let target_instance = linked
2780 .entrypoints
2781 .first()
2782 .ok_or_else(|| format!("linked bundle has no entrypoint for {target_style_path:?}"))?;
2783 let mut transformed_modules = Vec::with_capacity(linked.module_instances.len());
2784 let mut target_execution = None;
2785
2786 for module_instance in &linked.module_instances {
2787 let style_path = module_instance.module().as_str();
2788 let Some(style_source) = find_target_style_source(style_path, style_sources) else {
2789 return Err(format!(
2790 "linked module {style_path:?} was not found in workspace style sources"
2791 ));
2792 };
2793 let mut module_context = merge_workspace_transform_context(
2794 style_path,
2795 style_sources,
2796 base_context,
2797 resolution_context,
2798 );
2799 for inline in &mut module_context.import_inlines {
2800 inline.replacement_css.clear();
2801 }
2802 let summary =
2803 execute_omena_query_consumer_build_style_source_with_context_and_closed_world_bundle(
2804 style_path,
2805 style_source,
2806 &pass_set,
2807 &module_context,
2808 &linked.closed_world_bundle,
2809 classify_transform_reachability_precision(&module_context, true, None),
2810 options,
2811 );
2812 let non_empty_import_replacement_count = summary
2813 .execution
2814 .css_import_inlines
2815 .iter()
2816 .filter(|inline| !inline.replacement_css.is_empty())
2817 .count();
2818 transformed_modules.push(
2819 TransformBundleTransformedModuleV0::new(
2820 module_instance.clone(),
2821 summary.execution.output_css.clone(),
2822 )
2823 .with_non_empty_import_replacement_count(non_empty_import_replacement_count),
2824 );
2825 if module_instance == target_instance {
2826 target_execution = Some(summary.execution);
2827 }
2828 }
2829
2830 let materialized =
2831 materialize_omena_transform_bundle_linked_stylesheet(linked, &transformed_modules)
2832 .map_err(|error| format!("linked bundle materialization failed: {error:?}"))?;
2833 let Some(mut execution) = target_execution else {
2834 return Err(format!(
2835 "linked entrypoint {target_style_path:?} was not transformed"
2836 ));
2837 };
2838 execution.output_byte_len = materialized.output_css.len();
2839 execution.output_css.clone_from(&materialized.output_css);
2840 Ok(LinkedBundleExecutionV0 {
2841 execution,
2842 materialization: materialized,
2843 })
2844}
2845
2846struct LinkedBundleExecutionV0 {
2847 execution: TransformExecutionSummaryV0,
2848 materialization: LinkedEmissionArtifactV0,
2849}
2850
2851fn legacy_bundle_open_decision(
2852 target_style_path: &str,
2853 style_sources: &[OmenaQueryStyleSourceInputV0],
2854 requested_pass_ids: &[String],
2855 context: &TransformExecutionContextV0,
2856) -> bool {
2857 let modules = style_sources
2858 .iter()
2859 .map(|source| {
2860 TransformBundleModuleInputV0::new(
2861 source.style_path.as_str(),
2862 source.style_source.as_str(),
2863 omena_parser_dialect_for_style_path(source.style_path.as_str()),
2864 )
2865 })
2866 .collect::<Vec<_>>();
2867 let reachability_inputs = if requested_pass_ids_include_tree_shake(requested_pass_ids) {
2868 style_sources
2869 .iter()
2870 .filter_map(|source| {
2871 transform_bundle_semantic_reachability_input_from_context(
2872 source.style_path.as_str(),
2873 context,
2874 )
2875 })
2876 .collect::<Vec<_>>()
2877 } else {
2878 Vec::new()
2879 };
2880 if reachability_inputs.is_empty() {
2881 link_omena_transform_bundle_modules(&[target_style_path], &modules).is_err()
2882 } else {
2883 link_omena_transform_bundle_modules_with_semantic_reachability(
2884 &[target_style_path],
2885 &modules,
2886 reachability_inputs.as_slice(),
2887 )
2888 .is_err()
2889 }
2890}
2891
2892pub(crate) fn build_closed_world_bundle_for_single_style_source_context(
2893 style_path: &str,
2894 style_source: &str,
2895 requested_pass_ids: &[String],
2896 context: &TransformExecutionContextV0,
2897) -> Option<ClosedWorldBundleV0> {
2898 build_closed_world_outcome_for_single_style_source_context(
2899 style_path,
2900 style_source,
2901 requested_pass_ids,
2902 context,
2903 )
2904 .bundle()
2905 .cloned()
2906}
2907
2908pub fn summarize_omena_query_closed_world_outcome_for_style_source(
2909 style_path: &str,
2910 style_source: &str,
2911 requested_pass_ids: &[String],
2912 context: &TransformExecutionContextV0,
2913) -> OmenaQueryClosedWorldOutcomeV0 {
2914 let context = merge_single_source_transform_context(style_path, style_source, context);
2915 build_closed_world_outcome_for_single_style_source_context(
2916 style_path,
2917 style_source,
2918 requested_pass_ids,
2919 &context,
2920 )
2921}
2922
2923fn build_closed_world_outcome_for_single_style_source_context(
2924 style_path: &str,
2925 style_source: &str,
2926 requested_pass_ids: &[String],
2927 context: &TransformExecutionContextV0,
2928) -> OmenaQueryClosedWorldOutcomeV0 {
2929 let source = OmenaQueryStyleSourceInputV0 {
2930 style_path: style_path.to_string(),
2931 style_source: style_source.to_string(),
2932 };
2933 let sources = std::slice::from_ref(&source);
2934 let modules = style_sources_to_transform_bundle_modules(sources);
2935 let module_metadata = style_sources_to_closed_world_metadata(sources, &modules, context, &[]);
2936 let Some(reachability_input) =
2937 transform_bundle_semantic_reachability_input_from_context(style_path, context)
2938 else {
2939 if requested_pass_ids_include_tree_shake(requested_pass_ids) {
2940 return OmenaQueryClosedWorldOutcomeV0::Open {
2941 blockers: vec![OmenaQueryClosedWorldBlockerV0::ClosedWorldPassUnavailable {
2942 requested_pass_ids: requested_pass_ids.to_vec(),
2943 }],
2944 };
2945 }
2946 return closed_world_outcome_from_link_result(
2947 link_omena_transform_bundle_modules_with_semantic_reachability_and_metadata(
2948 &[style_path],
2949 &modules,
2950 &[],
2951 &module_metadata,
2952 ),
2953 requested_pass_ids,
2954 );
2955 };
2956
2957 closed_world_outcome_from_link_result(
2958 link_omena_transform_bundle_modules_with_semantic_reachability_and_metadata(
2959 &[style_path],
2960 &modules,
2961 std::slice::from_ref(&reachability_input),
2962 &module_metadata,
2963 ),
2964 requested_pass_ids,
2965 )
2966}
2967
2968fn style_sources_to_transform_bundle_modules(
2969 style_sources: &[OmenaQueryStyleSourceInputV0],
2970) -> Vec<TransformBundleModuleInputV0> {
2971 style_sources
2972 .iter()
2973 .map(|source| {
2974 TransformBundleModuleInputV0::new(
2975 source.style_path.as_str(),
2976 source.style_source.as_str(),
2977 omena_parser_dialect_for_style_path(source.style_path.as_str()),
2978 )
2979 })
2980 .collect()
2981}
2982
2983fn style_sources_to_closed_world_metadata(
2984 style_sources: &[OmenaQueryStyleSourceInputV0],
2985 modules: &[TransformBundleModuleInputV0],
2986 context: &TransformExecutionContextV0,
2987 external_sifs: &[OmenaQueryExternalSifInputV0],
2988) -> Vec<ClosedWorldModuleMetadataV0> {
2989 let source_precision = closed_world_source_precision_summary(context);
2990 style_sources
2991 .iter()
2992 .zip(modules)
2993 .map(|(source, module)| {
2994 let mut metadata = ClosedWorldModuleMetadataV0::new(module.module_instance_key())
2995 .with_source_precision(source_precision);
2996 if let Some(interface_hash) = external_sifs.iter().find_map(|external_sif| {
2997 sif_matches_style_path(external_sif, source.style_path.as_str()).then(|| {
2998 external_sif
2999 .sif
3000 .fingerprints
3001 .interface_hash
3002 .as_str()
3003 .to_string()
3004 })
3005 }) {
3006 metadata = metadata.with_interface_hash(interface_hash);
3007 }
3008 metadata
3009 })
3010 .collect()
3011}
3012
3013fn closed_world_source_precision_summary(
3014 context: &TransformExecutionContextV0,
3015) -> ClosedWorldSourcePrecisionSummaryV0 {
3016 let precision = classify_transform_reachability_precision(context, true, None);
3017 let mut summary = ClosedWorldSourcePrecisionSummaryV0::default();
3018 match precision {
3019 FactPrecision::Exact => summary.exact_source_count = 1,
3020 FactPrecision::Conservative => summary.conservative_source_count = 1,
3021 FactPrecision::Heuristic => summary.heuristic_source_count = 1,
3022 FactPrecision::Unknown => summary.unknown_source_count = 1,
3023 }
3024 summary
3025}
3026
3027fn sif_matches_style_path(external_sif: &OmenaQueryExternalSifInputV0, style_path: &str) -> bool {
3028 let style_path = normalize_bundle_sif_location(style_path);
3029 [
3030 external_sif.canonical_url.as_str(),
3031 external_sif.sif.canonical_url.as_str(),
3032 ]
3033 .into_iter()
3034 .map(normalize_bundle_sif_location)
3035 .any(|candidate| candidate == style_path)
3036}
3037
3038fn normalize_bundle_sif_location(location: &str) -> String {
3039 location
3040 .strip_prefix("file://")
3041 .unwrap_or(location)
3042 .replace('\\', "/")
3043}
3044
3045fn closed_world_outcome_from_link_result(
3046 result: Result<omena_query_transform_runner::LinkedStylesheetV0, TransformBundleLinkErrorV0>,
3047 requested_pass_ids: &[String],
3048) -> OmenaQueryClosedWorldOutcomeV0 {
3049 match result {
3050 Ok(linked) => OmenaQueryClosedWorldOutcomeV0::Closed {
3051 bundle: Box::new(linked.closed_world_bundle),
3052 },
3053 Err(error) => OmenaQueryClosedWorldOutcomeV0::Open {
3054 blockers: vec![closed_world_blocker_from_link_error(
3055 error,
3056 requested_pass_ids,
3057 )],
3058 },
3059 }
3060}
3061
3062fn closed_world_blocker_from_link_error(
3063 error: TransformBundleLinkErrorV0,
3064 requested_pass_ids: &[String],
3065) -> OmenaQueryClosedWorldBlockerV0 {
3066 match error {
3067 TransformBundleLinkErrorV0::MissingEntrypoint { source_path } => {
3068 OmenaQueryClosedWorldBlockerV0::MissingEntrypoint { source_path }
3069 }
3070 TransformBundleLinkErrorV0::AmbiguousModulePath { source_path } => {
3071 OmenaQueryClosedWorldBlockerV0::AmbiguousModulePath { source_path }
3072 }
3073 TransformBundleLinkErrorV0::MissingDependency {
3074 source_path,
3075 import_source,
3076 } => OmenaQueryClosedWorldBlockerV0::MissingDependency {
3077 source_path,
3078 import_source,
3079 },
3080 TransformBundleLinkErrorV0::ClosedWorldBundle { error } => match error {
3081 ClosedWorldBundleBuildErrorV0::EmptyEntrypoints => {
3082 OmenaQueryClosedWorldBlockerV0::EmptyEntrypoints
3083 }
3084 ClosedWorldBundleBuildErrorV0::MissingEntrypoint { module } => {
3085 OmenaQueryClosedWorldBlockerV0::MissingModuleInstance { module }
3086 }
3087 ClosedWorldBundleBuildErrorV0::MissingDependency { module, dependency } => {
3088 OmenaQueryClosedWorldBlockerV0::MissingModuleDependency { module, dependency }
3089 }
3090 },
3091 TransformBundleLinkErrorV0::InvalidEmissionPlan { .. }
3092 | TransformBundleLinkErrorV0::UnsupportedEmissionCycle { .. } => {
3093 OmenaQueryClosedWorldBlockerV0::ClosedWorldPassUnavailable {
3094 requested_pass_ids: requested_pass_ids.to_vec(),
3095 }
3096 }
3097 }
3098}
3099
3100fn transform_bundle_semantic_reachability_input_from_context(
3101 style_path: &str,
3102 context: &TransformExecutionContextV0,
3103) -> Option<TransformBundleSemanticReachabilityInputV0> {
3104 let input = TransformBundleSemanticReachabilityInputV0 {
3105 source_path: style_path.to_string(),
3106 class_names: context.reachable_class_names.clone(),
3107 keyframe_names: context.reachable_keyframe_names.clone(),
3108 value_names: context.reachable_value_names.clone(),
3109 custom_property_names: context.reachable_custom_property_names.clone(),
3110 };
3111 input.has_reachable_symbols().then_some(input)
3112}
3113
3114fn transform_pass_kind_from_id(pass_id: &str) -> Option<TransformPassKind> {
3115 all_transform_pass_kinds()
3116 .into_iter()
3117 .find(|candidate| candidate.id() == pass_id)
3118}
3119
3120#[cfg(test)]
3121mod linked_source_map_tests {
3122 use super::*;
3123
3124 #[test]
3125 fn linked_bundle_source_map_uses_materialized_module_offsets() -> Result<(), String> {
3126 let style_sources = vec![
3127 OmenaQueryStyleSourceInputV0 {
3128 style_path: "src/app.css".to_string(),
3129 style_source: "@import \"./tokens.css\"; .linked-map-app { color: red; }"
3130 .to_string(),
3131 },
3132 OmenaQueryStyleSourceInputV0 {
3133 style_path: "src/tokens.css".to_string(),
3134 style_source: ".linked-map-tokens { color: blue; }".to_string(),
3135 },
3136 ];
3137 let modules = style_sources
3138 .iter()
3139 .map(|source| {
3140 TransformBundleModuleInputV0::new(
3141 source.style_path.clone(),
3142 source.style_source.clone(),
3143 omena_parser::StyleDialect::Css,
3144 )
3145 })
3146 .collect::<Vec<_>>();
3147 let linked = link_omena_transform_bundle_modules(&["src/app.css"], &modules)
3148 .map_err(|error| format!("source-map fixture should link: {error:?}"))?;
3149 let transformed = linked
3150 .module_instances
3151 .iter()
3152 .map(|module_instance| {
3153 let output_css = match module_instance.module().as_str() {
3154 "src/app.css" => ".linked-map-app { color: red; }",
3155 "src/tokens.css" => ".linked-map-tokens { color: blue; }",
3156 path => return Err(format!("unexpected fixture module {path:?}")),
3157 };
3158 Ok(TransformBundleTransformedModuleV0::new(
3159 module_instance.clone(),
3160 output_css,
3161 ))
3162 })
3163 .collect::<Result<Vec<_>, String>>()?;
3164 let materialization =
3165 materialize_omena_transform_bundle_linked_stylesheet(&linked, &transformed)
3166 .map_err(|error| format!("source-map fixture should materialize: {error:?}"))?;
3167 let segments = linked_bundle_source_map_segments(
3168 &style_sources,
3169 &materialization.output_css,
3170 &materialization,
3171 )?;
3172
3173 assert_eq!(segments.len(), transformed.len());
3174 for segment in &segments {
3175 let marker = match segment.source_path.as_str() {
3176 "src/app.css" => ".linked-map-app",
3177 "src/tokens.css" => ".linked-map-tokens",
3178 path => return Err(format!("unexpected source-map path {path:?}")),
3179 };
3180 let generated_start = materialization
3181 .output_css
3182 .find(marker)
3183 .ok_or_else(|| format!("generated marker {marker:?} should exist"))?;
3184 assert_eq!(segment.generated_start, generated_start);
3185 assert!(segment.generated_end > segment.generated_start);
3186 assert_eq!(segment.generated_start_point.byte_offset, generated_start);
3187 assert_eq!(segment.pass_id, "linked-order-emission");
3188 }
3189 Ok(())
3190 }
3191}
3192
3193#[cfg(test)]
3194mod closed_world_link_error_tests {
3195 use super::closed_world_blocker_from_link_error;
3196 use crate::OmenaQueryClosedWorldBlockerV0;
3197 use omena_query_transform_runner::{TransformBundleEdgeKind, TransformBundleLinkErrorV0};
3198
3199 #[test]
3200 fn engine_only_emission_failures_preserve_the_sdk_blocker_contract() {
3201 let requested_pass_ids = vec!["tree-shake".to_string()];
3202 let expected = OmenaQueryClosedWorldBlockerV0::ClosedWorldPassUnavailable {
3203 requested_pass_ids: requested_pass_ids.clone(),
3204 };
3205
3206 for error in [
3207 TransformBundleLinkErrorV0::InvalidEmissionPlan {
3208 reason: "duplicate order key".to_string(),
3209 },
3210 TransformBundleLinkErrorV0::UnsupportedEmissionCycle {
3211 edge_kind: TransformBundleEdgeKind::SassUse,
3212 },
3213 ] {
3214 assert_eq!(
3215 closed_world_blocker_from_link_error(error, &requested_pass_ids),
3216 expected
3217 );
3218 }
3219 }
3220}
3221
3222#[cfg(test)]
3223mod closed_set_precision_tests {
3224 use super::*;
3225
3226 #[test]
3227 fn sealed_bundle_content_binds_finite_reachability_precision() -> Result<(), String> {
3228 let style_path = "Workspace.module.css";
3229 let style_source = ".card {} .panel {} .toolbar {} .dead {}";
3230 let reachable_class_names = vec![
3231 "card".to_string(),
3232 "panel".to_string(),
3233 "toolbar".to_string(),
3234 ];
3235 let context = TransformExecutionContextV0 {
3236 reachable_class_names: reachable_class_names.clone(),
3237 ..TransformExecutionContextV0::default()
3238 };
3239 let requested_pass_ids = vec!["tree-shake-class".to_string()];
3240 let bundle = build_closed_world_bundle_for_single_style_source_context(
3241 style_path,
3242 style_source,
3243 &requested_pass_ids,
3244 &context,
3245 )
3246 .ok_or_else(|| {
3247 "the finite reachability fixture should produce a sealed bundle".to_string()
3248 })?;
3249 let finite_value = AbstractClassValueV0::FiniteSet {
3250 values: reachable_class_names,
3251 };
3252 let open_world_precision = fact_precision_from_class_value(&finite_value);
3253 let closed_world_precision = closed_world_bound_reachability_precision(
3254 &context,
3255 &bundle,
3256 Some(open_world_precision),
3257 true,
3258 );
3259 let non_enumerated_precision = closed_world_bound_reachability_precision(
3260 &context,
3261 &bundle,
3262 Some(open_world_precision),
3263 false,
3264 );
3265 let missing_member_context = TransformExecutionContextV0 {
3266 reachable_class_names: vec!["card".to_string(), "outside-bundle".to_string()],
3267 ..TransformExecutionContextV0::default()
3268 };
3269 let missing_member_precision = closed_world_bound_reachability_precision(
3270 &missing_member_context,
3271 &bundle,
3272 Some(open_world_precision),
3273 true,
3274 );
3275
3276 assert_eq!(open_world_precision, FactPrecision::Conservative);
3277 assert_eq!(closed_world_precision, FactPrecision::Exact);
3278 assert_eq!(non_enumerated_precision, FactPrecision::Conservative);
3279 assert_eq!(missing_member_precision, FactPrecision::Conservative);
3280
3281 let calibration_report: serde_json::Value = serde_json::from_str(include_str!(
3282 "../../../../omena-precision-calibration-report.json"
3283 ))
3284 .map_err(|error| format!("precision calibration report should be valid JSON: {error}"))?;
3285 assert_eq!(
3286 calibration_report["cases"][1],
3287 serde_json::json!({
3288 "caseId": "closedSetFiniteReachability",
3289 "inputClassCount": 3,
3290 "representation": "finiteSet",
3291 "witnessDirection": "supersetOfProducible",
3292 "witnessBasis": "closedSetEnumeration",
3293 "authority": "closedWorldBundleClosureHash",
3294 "openWorldPrecision": open_world_precision,
3295 "closedWorldPrecision": closed_world_precision,
3296 "nonEnumeratedPrecision": non_enumerated_precision,
3297 "missingMemberPrecision": missing_member_precision,
3298 })
3299 );
3300 Ok(())
3301 }
3302}