# KCL Composition Utilities # Provides utilities for composing and merging KCL configurations # Deep merge utility for KCL schemas # Recursively merges two dictionaries, with right taking precedence def deep_merge [left: any, right: any] -> any { if ($right | describe) == "record" and ($left | describe) == "record" { let merged = {} # Start with all keys from left for key in ($left | columns) { $merged = ($merged | insert $key $left.($key)) } # Merge in keys from right, recursively merging records for key in ($right | columns) { if $key in ($left | columns) { if ($left.($key) | describe) == "record" and ($right.($key) | describe) == "record" { $merged = ($merged | insert $key (deep_merge $left.($key) $right.($key))) } else { # Right takes precedence for non-record values $merged = ($merged | insert $key $right.($key)) } } else { # New key from right $merged = ($merged | insert $key $right.($key)) } } $merged } else { # Non-record types: right takes precedence $right } } # Apply template with overrides # Usage: apply_template base_template overrides def apply_template [base: any, overrides: any = {}] -> any { deep_merge $base $overrides } # Compose multiple templates in order # Later templates override earlier ones def compose_templates [...templates: any] -> any { $templates | reduce {|template, acc| deep_merge $acc $template } } # Apply conditional overrides based on provider def apply_provider_overrides [ base: any, provider: str, overrides: {str: any} = {} ] -> any { let provider_specific = if $provider in $overrides { $overrides.($provider) } else { {} } let common = if "common" in $overrides { $overrides.common } else { {} } # Apply: base -> common -> provider_specific $base | deep_merge $common | deep_merge $provider_specific } # Apply environment-specific overrides def apply_env_overrides [ base: any, environment: str, overrides: {str: any} = {} ] -> any { let env_specific = if $environment in $overrides { $overrides.($environment) } else { {} } deep_merge $base $env_specific } # Validate required fields are present def validate_required [config: any, required_fields: list] -> bool { let missing = $required_fields | filter {|field| not ($field in ($config | columns)) } if ($missing | length) > 0 { error make { msg: $"Missing required fields: ($missing | str join ', ')" } } true } # Create configuration with validation def create_config [ base: any, overrides: any = {}, required: list = [], provider?: str, environment?: str ] -> any { mut result = deep_merge $base $overrides # Apply provider-specific overrides if provider specified if $provider != null { $result = apply_provider_overrides $result $provider $overrides } # Apply environment-specific overrides if environment specified if $environment != null { $result = apply_env_overrides $result $environment $overrides } # Validate required fields if ($required | length) > 0 { validate_required $result $required } $result } # Template composition patterns schema CompositionPatterns { # Layer composition: core -> workspace -> infra layer_compose: def [core: any, workspace: any, infra: any] -> any { compose_templates $core $workspace $infra } # Provider-aware composition provider_compose: def [ base: any, provider: str, provider_configs: {str: any} ] -> any { apply_provider_overrides $base $provider $provider_configs } # Template with variants variant_compose: def [ base_template: any, variant: str, variants: {str: any} ] -> any { let variant_config = if $variant in $variants { $variants.($variant) } else { error make {msg: $"Unknown variant: ($variant)"} } deep_merge $base_template $variant_config } } # Export utilities composition_utils = CompositionPatterns {}