typekro 0.8.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.tsbuildinfo +1 -1
- package/dist/alchemy/deployers.d.ts +16 -1
- package/dist/alchemy/deployers.d.ts.map +1 -1
- package/dist/alchemy/deployers.js +131 -18
- package/dist/alchemy/deployers.js.map +1 -1
- package/dist/alchemy/deployment.d.ts +1 -1
- package/dist/alchemy/deployment.d.ts.map +1 -1
- package/dist/alchemy/deployment.js +1 -1
- package/dist/alchemy/deployment.js.map +1 -1
- package/dist/alchemy/kro-delete.d.ts +66 -0
- package/dist/alchemy/kro-delete.d.ts.map +1 -0
- package/dist/alchemy/kro-delete.js +183 -0
- package/dist/alchemy/kro-delete.js.map +1 -0
- package/dist/alchemy/resource-registration.d.ts +16 -0
- package/dist/alchemy/resource-registration.d.ts.map +1 -1
- package/dist/alchemy/resource-registration.js +138 -24
- package/dist/alchemy/resource-registration.js.map +1 -1
- package/dist/alchemy/types.d.ts +8 -4
- package/dist/alchemy/types.d.ts.map +1 -1
- package/dist/compositions/typekro-runtime/typekro-runtime.d.ts +1 -1
- package/dist/compositions/typekro-runtime/typekro-runtime.d.ts.map +1 -1
- package/dist/compositions/typekro-runtime/typekro-runtime.js +27 -9
- package/dist/compositions/typekro-runtime/typekro-runtime.js.map +1 -1
- package/dist/core/composition/context.d.ts +58 -2
- package/dist/core/composition/context.d.ts.map +1 -1
- package/dist/core/composition/context.js +4 -0
- package/dist/core/composition/context.js.map +1 -1
- package/dist/core/composition/imperative.d.ts +9 -0
- package/dist/core/composition/imperative.d.ts.map +1 -1
- package/dist/core/composition/imperative.js +538 -54
- package/dist/core/composition/imperative.js.map +1 -1
- package/dist/core/composition/nested-status-cel.d.ts +34 -1
- package/dist/core/composition/nested-status-cel.d.ts.map +1 -1
- package/dist/core/composition/nested-status-cel.js +379 -41
- package/dist/core/composition/nested-status-cel.js.map +1 -1
- package/dist/core/composition-debugger.d.ts +1 -1
- package/dist/core/composition-debugger.d.ts.map +1 -1
- package/dist/core/composition-debugger.js.map +1 -1
- package/dist/core/constants/brands.d.ts +1 -1
- package/dist/core/constants/brands.d.ts.map +1 -1
- package/dist/core/constants/brands.js +1 -1
- package/dist/core/constants/brands.js.map +1 -1
- package/dist/core/containers/build.d.ts.map +1 -1
- package/dist/core/containers/build.js +40 -12
- package/dist/core/containers/build.js.map +1 -1
- package/dist/core/dependencies/resolver.d.ts +19 -0
- package/dist/core/dependencies/resolver.d.ts.map +1 -1
- package/dist/core/dependencies/resolver.js +261 -1
- package/dist/core/dependencies/resolver.js.map +1 -1
- package/dist/core/deployment/client-provider-manager.d.ts +9 -3
- package/dist/core/deployment/client-provider-manager.d.ts.map +1 -1
- package/dist/core/deployment/client-provider-manager.js +12 -0
- package/dist/core/deployment/client-provider-manager.js.map +1 -1
- package/dist/core/deployment/crd-manager.d.ts +24 -0
- package/dist/core/deployment/crd-manager.d.ts.map +1 -1
- package/dist/core/deployment/crd-manager.js +79 -1
- package/dist/core/deployment/crd-manager.js.map +1 -1
- package/dist/core/deployment/deployment-state-discovery.d.ts +116 -0
- package/dist/core/deployment/deployment-state-discovery.d.ts.map +1 -0
- package/dist/core/deployment/deployment-state-discovery.js +400 -0
- package/dist/core/deployment/deployment-state-discovery.js.map +1 -0
- package/dist/core/deployment/direct-factory.d.ts +65 -6
- package/dist/core/deployment/direct-factory.d.ts.map +1 -1
- package/dist/core/deployment/direct-factory.js +551 -56
- package/dist/core/deployment/direct-factory.js.map +1 -1
- package/dist/core/deployment/engine.d.ts +64 -3
- package/dist/core/deployment/engine.d.ts.map +1 -1
- package/dist/core/deployment/engine.js +194 -27
- package/dist/core/deployment/engine.js.map +1 -1
- package/dist/core/deployment/event-filter.js +1 -1
- package/dist/core/deployment/event-filter.js.map +1 -1
- package/dist/core/deployment/event-monitor.d.ts +11 -0
- package/dist/core/deployment/event-monitor.d.ts.map +1 -1
- package/dist/core/deployment/event-monitor.js +14 -0
- package/dist/core/deployment/event-monitor.js.map +1 -1
- package/dist/core/deployment/handle-tracing.d.ts +14 -0
- package/dist/core/deployment/handle-tracing.d.ts.map +1 -0
- package/dist/core/deployment/handle-tracing.js +38 -0
- package/dist/core/deployment/handle-tracing.js.map +1 -0
- package/dist/core/deployment/kro-factory.d.ts +115 -3
- package/dist/core/deployment/kro-factory.d.ts.map +1 -1
- package/dist/core/deployment/kro-factory.js +906 -265
- package/dist/core/deployment/kro-factory.js.map +1 -1
- package/dist/core/deployment/kro-readiness.d.ts.map +1 -1
- package/dist/core/deployment/kro-readiness.js +21 -12
- package/dist/core/deployment/kro-readiness.js.map +1 -1
- package/dist/core/deployment/nested-composition-status.d.ts +1 -1
- package/dist/core/deployment/nested-composition-status.d.ts.map +1 -1
- package/dist/core/deployment/nested-composition-status.js +96 -53
- package/dist/core/deployment/nested-composition-status.js.map +1 -1
- package/dist/core/deployment/resource-applier.d.ts +15 -2
- package/dist/core/deployment/resource-applier.d.ts.map +1 -1
- package/dist/core/deployment/resource-applier.js +75 -25
- package/dist/core/deployment/resource-applier.js.map +1 -1
- package/dist/core/deployment/resource-tagging.d.ts +220 -0
- package/dist/core/deployment/resource-tagging.d.ts.map +1 -0
- package/dist/core/deployment/resource-tagging.js +292 -0
- package/dist/core/deployment/resource-tagging.js.map +1 -0
- package/dist/core/deployment/rollback-manager.d.ts +25 -4
- package/dist/core/deployment/rollback-manager.d.ts.map +1 -1
- package/dist/core/deployment/rollback-manager.js +70 -57
- package/dist/core/deployment/rollback-manager.js.map +1 -1
- package/dist/core/deployment/shared-utilities.d.ts +6 -0
- package/dist/core/deployment/shared-utilities.d.ts.map +1 -1
- package/dist/core/deployment/shared-utilities.js +32 -2
- package/dist/core/deployment/shared-utilities.js.map +1 -1
- package/dist/core/deployment/singleton-owner-drift.d.ts +16 -0
- package/dist/core/deployment/singleton-owner-drift.d.ts.map +1 -0
- package/dist/core/deployment/singleton-owner-drift.js +54 -0
- package/dist/core/deployment/singleton-owner-drift.js.map +1 -0
- package/dist/core/deployment/strategies/alchemy-strategy.d.ts +3 -1
- package/dist/core/deployment/strategies/alchemy-strategy.d.ts.map +1 -1
- package/dist/core/deployment/strategies/alchemy-strategy.js +121 -18
- package/dist/core/deployment/strategies/alchemy-strategy.js.map +1 -1
- package/dist/core/deployment/strategies/base-strategy.d.ts +9 -3
- package/dist/core/deployment/strategies/base-strategy.d.ts.map +1 -1
- package/dist/core/deployment/strategies/base-strategy.js +32 -4
- package/dist/core/deployment/strategies/base-strategy.js.map +1 -1
- package/dist/core/deployment/strategies/direct-strategy.d.ts +12 -4
- package/dist/core/deployment/strategies/direct-strategy.d.ts.map +1 -1
- package/dist/core/deployment/strategies/direct-strategy.js +112 -8
- package/dist/core/deployment/strategies/direct-strategy.js.map +1 -1
- package/dist/core/errors.d.ts +2 -2
- package/dist/core/errors.d.ts.map +1 -1
- package/dist/core/errors.js.map +1 -1
- package/dist/core/expressions/analysis/cache.d.ts +2 -2
- package/dist/core/expressions/analysis/cache.d.ts.map +1 -1
- package/dist/core/expressions/analysis/cache.js +4 -0
- package/dist/core/expressions/analysis/cache.js.map +1 -1
- package/dist/core/expressions/analysis/fn-toString-self-test.d.ts.map +1 -1
- package/dist/core/expressions/analysis/fn-toString-self-test.js +0 -1
- package/dist/core/expressions/analysis/fn-toString-self-test.js.map +1 -1
- package/dist/core/expressions/analysis/shared-types.d.ts +2 -2
- package/dist/core/expressions/analysis/shared-types.d.ts.map +1 -1
- package/dist/core/expressions/analysis/source-map.d.ts.map +1 -1
- package/dist/core/expressions/analysis/source-map.js +1 -0
- package/dist/core/expressions/analysis/source-map.js.map +1 -1
- package/dist/core/expressions/analysis/types.d.ts +7 -7
- package/dist/core/expressions/analysis/types.d.ts.map +1 -1
- package/dist/core/expressions/composition/composition-analyzer-helpers.d.ts +29 -1
- package/dist/core/expressions/composition/composition-analyzer-helpers.d.ts.map +1 -1
- package/dist/core/expressions/composition/composition-analyzer-helpers.js +348 -17
- package/dist/core/expressions/composition/composition-analyzer-helpers.js.map +1 -1
- package/dist/core/expressions/composition/composition-analyzer-ternary.d.ts +2 -2
- package/dist/core/expressions/composition/composition-analyzer-ternary.d.ts.map +1 -1
- package/dist/core/expressions/composition/composition-analyzer-ternary.js +221 -10
- package/dist/core/expressions/composition/composition-analyzer-ternary.js.map +1 -1
- package/dist/core/expressions/composition/composition-analyzer-traversal.d.ts.map +1 -1
- package/dist/core/expressions/composition/composition-analyzer-traversal.js +67 -2
- package/dist/core/expressions/composition/composition-analyzer-traversal.js.map +1 -1
- package/dist/core/expressions/composition/composition-analyzer-types.d.ts +52 -0
- package/dist/core/expressions/composition/composition-analyzer-types.d.ts.map +1 -1
- package/dist/core/expressions/composition/composition-analyzer.d.ts.map +1 -1
- package/dist/core/expressions/composition/composition-analyzer.js +150 -6
- package/dist/core/expressions/composition/composition-analyzer.js.map +1 -1
- package/dist/core/expressions/composition/expression-analyzer.d.ts.map +1 -1
- package/dist/core/expressions/composition/expression-analyzer.js +21 -4
- package/dist/core/expressions/composition/expression-analyzer.js.map +1 -1
- package/dist/core/expressions/composition/imperative-analyzer.d.ts +1 -1
- package/dist/core/expressions/composition/imperative-analyzer.d.ts.map +1 -1
- package/dist/core/expressions/composition/imperative-analyzer.js +31 -7
- package/dist/core/expressions/composition/imperative-analyzer.js.map +1 -1
- package/dist/core/expressions/composition/scope-manager.d.ts +2 -2
- package/dist/core/expressions/composition/scope-manager.d.ts.map +1 -1
- package/dist/core/expressions/composition/scope-manager.js.map +1 -1
- package/dist/core/expressions/conditional/conditional-expression-processor.d.ts +3 -3
- package/dist/core/expressions/conditional/conditional-expression-processor.d.ts.map +1 -1
- package/dist/core/expressions/conditional/conditional-expression-processor.js.map +1 -1
- package/dist/core/expressions/conditional/conditional-integration.d.ts.map +1 -1
- package/dist/core/expressions/conditional/conditional-integration.js.map +1 -1
- package/dist/core/expressions/context/context-aware-generator.d.ts +5 -5
- package/dist/core/expressions/context/context-aware-generator.d.ts.map +1 -1
- package/dist/core/expressions/context/context-aware-generator.js.map +1 -1
- package/dist/core/expressions/context/context-detector.d.ts +3 -3
- package/dist/core/expressions/context/context-detector.d.ts.map +1 -1
- package/dist/core/expressions/context/context-detector.js.map +1 -1
- package/dist/core/expressions/context/context-validator.d.ts +6 -6
- package/dist/core/expressions/context/context-validator.d.ts.map +1 -1
- package/dist/core/expressions/context/context-validator.js +2 -2
- package/dist/core/expressions/context/context-validator.js.map +1 -1
- package/dist/core/expressions/factory/cel-conversion-engine.d.ts +4 -4
- package/dist/core/expressions/factory/cel-conversion-engine.d.ts.map +1 -1
- package/dist/core/expressions/factory/cel-conversion-engine.js.map +1 -1
- package/dist/core/expressions/factory/dependency-tracker.d.ts +2 -2
- package/dist/core/expressions/factory/dependency-tracker.d.ts.map +1 -1
- package/dist/core/expressions/factory/dependency-tracker.js +21 -5
- package/dist/core/expressions/factory/dependency-tracker.js.map +1 -1
- package/dist/core/expressions/factory/factory-integration.d.ts +2 -4
- package/dist/core/expressions/factory/factory-integration.d.ts.map +1 -1
- package/dist/core/expressions/factory/factory-integration.js +0 -6
- package/dist/core/expressions/factory/factory-integration.js.map +1 -1
- package/dist/core/expressions/factory/factory-pattern-handler.d.ts +3 -3
- package/dist/core/expressions/factory/factory-pattern-handler.d.ts.map +1 -1
- package/dist/core/expressions/factory/factory-pattern-handler.js +1 -0
- package/dist/core/expressions/factory/factory-pattern-handler.js.map +1 -1
- package/dist/core/expressions/factory/migration-helpers.js.map +1 -1
- package/dist/core/expressions/factory/resource-analyzer.d.ts +4 -4
- package/dist/core/expressions/factory/resource-analyzer.d.ts.map +1 -1
- package/dist/core/expressions/factory/resource-analyzer.js.map +1 -1
- package/dist/core/expressions/factory/resource-type-validator.d.ts +5 -5
- package/dist/core/expressions/factory/resource-type-validator.d.ts.map +1 -1
- package/dist/core/expressions/factory/resource-type-validator.js.map +1 -1
- package/dist/core/expressions/factory/status-builder-analyzer.d.ts +6 -7
- package/dist/core/expressions/factory/status-builder-analyzer.d.ts.map +1 -1
- package/dist/core/expressions/factory/status-builder-analyzer.js +0 -3
- package/dist/core/expressions/factory/status-builder-analyzer.js.map +1 -1
- package/dist/core/expressions/factory/status-builder-types.d.ts +1 -1
- package/dist/core/expressions/factory/status-builder-types.d.ts.map +1 -1
- package/dist/core/expressions/factory/status-cel-generation.d.ts +1 -1
- package/dist/core/expressions/factory/status-cel-generation.d.ts.map +1 -1
- package/dist/core/expressions/factory/status-cel-generation.js +1 -1
- package/dist/core/expressions/factory/status-cel-generation.js.map +1 -1
- package/dist/core/expressions/factory/status-field-analysis.d.ts +3 -3
- package/dist/core/expressions/factory/status-field-analysis.d.ts.map +1 -1
- package/dist/core/expressions/factory/status-field-analysis.js.map +1 -1
- package/dist/core/expressions/magic-proxy/magic-assignable-analyzer.d.ts +5 -5
- package/dist/core/expressions/magic-proxy/magic-assignable-analyzer.d.ts.map +1 -1
- package/dist/core/expressions/magic-proxy/magic-assignable-analyzer.js +1 -1
- package/dist/core/expressions/magic-proxy/magic-assignable-analyzer.js.map +1 -1
- package/dist/core/expressions/magic-proxy/magic-proxy-analyzer.d.ts +10 -10
- package/dist/core/expressions/magic-proxy/magic-proxy-analyzer.d.ts.map +1 -1
- package/dist/core/expressions/magic-proxy/magic-proxy-analyzer.js +5 -1
- package/dist/core/expressions/magic-proxy/magic-proxy-analyzer.js.map +1 -1
- package/dist/core/expressions/magic-proxy/magic-proxy-ast.d.ts +2 -2
- package/dist/core/expressions/magic-proxy/magic-proxy-ast.d.ts.map +1 -1
- package/dist/core/expressions/magic-proxy/magic-proxy-ast.js.map +1 -1
- package/dist/core/expressions/magic-proxy/magic-proxy-detector.d.ts +5 -5
- package/dist/core/expressions/magic-proxy/magic-proxy-detector.d.ts.map +1 -1
- package/dist/core/expressions/magic-proxy/magic-proxy-detector.js.map +1 -1
- package/dist/core/expressions/magic-proxy/magic-proxy-types.d.ts +2 -2
- package/dist/core/expressions/magic-proxy/magic-proxy-types.d.ts.map +1 -1
- package/dist/core/expressions/magic-proxy/optionality-handler.d.ts +1 -2
- package/dist/core/expressions/magic-proxy/optionality-handler.d.ts.map +1 -1
- package/dist/core/expressions/magic-proxy/optionality-handler.js +2 -15
- package/dist/core/expressions/magic-proxy/optionality-handler.js.map +1 -1
- package/dist/core/expressions/validation/compile-time-checker.d.ts +1 -1
- package/dist/core/expressions/validation/compile-time-checker.d.ts.map +1 -1
- package/dist/core/expressions/validation/compile-time-checker.js.map +1 -1
- package/dist/core/expressions/validation/compile-time-types.d.ts +2 -2
- package/dist/core/expressions/validation/compile-time-types.d.ts.map +1 -1
- package/dist/core/expressions/validation/kubernetes-field-types.d.ts +4 -4
- package/dist/core/expressions/validation/kubernetes-field-types.d.ts.map +1 -1
- package/dist/core/expressions/validation/kubernetes-field-types.js.map +1 -1
- package/dist/core/expressions/validation/resource-field-utils.d.ts +10 -10
- package/dist/core/expressions/validation/resource-field-utils.d.ts.map +1 -1
- package/dist/core/expressions/validation/resource-field-utils.js.map +1 -1
- package/dist/core/expressions/validation/resource-validation.d.ts +3 -3
- package/dist/core/expressions/validation/resource-validation.d.ts.map +1 -1
- package/dist/core/expressions/validation/resource-validation.js.map +1 -1
- package/dist/core/expressions/validation/type-inference-types.d.ts +2 -2
- package/dist/core/expressions/validation/type-inference-types.d.ts.map +1 -1
- package/dist/core/expressions/validation/type-safety.d.ts +2 -2
- package/dist/core/expressions/validation/type-safety.d.ts.map +1 -1
- package/dist/core/expressions/validation/type-safety.js +1 -0
- package/dist/core/expressions/validation/type-safety.js.map +1 -1
- package/dist/core/kubernetes/bun-api-client.js.map +1 -1
- package/dist/core/kubernetes/bun-http-library.d.ts.map +1 -1
- package/dist/core/kubernetes/bun-http-library.js +29 -3
- package/dist/core/kubernetes/bun-http-library.js.map +1 -1
- package/dist/core/kubernetes/client-provider.d.ts +12 -0
- package/dist/core/kubernetes/client-provider.d.ts.map +1 -1
- package/dist/core/kubernetes/client-provider.js +35 -0
- package/dist/core/kubernetes/client-provider.js.map +1 -1
- package/dist/core/metadata/resource-metadata.d.ts +46 -1
- package/dist/core/metadata/resource-metadata.d.ts.map +1 -1
- package/dist/core/metadata/resource-metadata.js.map +1 -1
- package/dist/core/proxy/create-resource.d.ts +15 -0
- package/dist/core/proxy/create-resource.d.ts.map +1 -1
- package/dist/core/proxy/create-resource.js +56 -2
- package/dist/core/proxy/create-resource.js.map +1 -1
- package/dist/core/readiness/registry.js +2 -2
- package/dist/core/readiness/registry.js.map +1 -1
- package/dist/core/references/cel-evaluator.d.ts +1 -4
- package/dist/core/references/cel-evaluator.d.ts.map +1 -1
- package/dist/core/references/cel-evaluator.js +3 -7
- package/dist/core/references/cel-evaluator.js.map +1 -1
- package/dist/core/references/cel.d.ts +22 -0
- package/dist/core/references/cel.d.ts.map +1 -1
- package/dist/core/references/cel.js +106 -8
- package/dist/core/references/cel.js.map +1 -1
- package/dist/core/references/external-refs.d.ts.map +1 -1
- package/dist/core/references/external-refs.js +3 -0
- package/dist/core/references/external-refs.js.map +1 -1
- package/dist/core/references/resolver.d.ts.map +1 -1
- package/dist/core/references/resolver.js +28 -17
- package/dist/core/references/resolver.js.map +1 -1
- package/dist/core/references/schema-proxy.d.ts +18 -10
- package/dist/core/references/schema-proxy.d.ts.map +1 -1
- package/dist/core/references/schema-proxy.js +174 -23
- package/dist/core/references/schema-proxy.js.map +1 -1
- package/dist/core/runtime-patches/crd-schema-fix.d.ts.map +1 -1
- package/dist/core/runtime-patches/crd-schema-fix.js +4 -1
- package/dist/core/runtime-patches/crd-schema-fix.js.map +1 -1
- package/dist/core/serialization/cel-optimizer.d.ts.map +1 -1
- package/dist/core/serialization/cel-optimizer.js +2 -0
- package/dist/core/serialization/cel-optimizer.js.map +1 -1
- package/dist/core/serialization/cel-references.d.ts +75 -1
- package/dist/core/serialization/cel-references.d.ts.map +1 -1
- package/dist/core/serialization/cel-references.js +692 -156
- package/dist/core/serialization/cel-references.js.map +1 -1
- package/dist/core/serialization/core.d.ts +8 -8
- package/dist/core/serialization/core.d.ts.map +1 -1
- package/dist/core/serialization/core.js +638 -90
- package/dist/core/serialization/core.js.map +1 -1
- package/dist/core/serialization/kro-post-processing.d.ts +1 -1
- package/dist/core/serialization/kro-post-processing.d.ts.map +1 -1
- package/dist/core/serialization/kro-post-processing.js +69 -22
- package/dist/core/serialization/kro-post-processing.js.map +1 -1
- package/dist/core/serialization/schema.d.ts +1 -0
- package/dist/core/serialization/schema.d.ts.map +1 -1
- package/dist/core/serialization/schema.js +378 -50
- package/dist/core/serialization/schema.js.map +1 -1
- package/dist/core/serialization/status-analysis-pipeline.d.ts +1 -1
- package/dist/core/serialization/status-analysis-pipeline.d.ts.map +1 -1
- package/dist/core/serialization/status-analysis-pipeline.js.map +1 -1
- package/dist/core/serialization/yaml.d.ts +3 -2
- package/dist/core/serialization/yaml.d.ts.map +1 -1
- package/dist/core/serialization/yaml.js +385 -55
- package/dist/core/serialization/yaml.js.map +1 -1
- package/dist/core/singleton/singleton.d.ts +16 -0
- package/dist/core/singleton/singleton.d.ts.map +1 -0
- package/dist/core/singleton/singleton.js +135 -0
- package/dist/core/singleton/singleton.js.map +1 -0
- package/dist/core/types/common.d.ts +2 -2
- package/dist/core/types/common.d.ts.map +1 -1
- package/dist/core/types/composable.d.ts +1 -1
- package/dist/core/types/composable.d.ts.map +1 -1
- package/dist/core/types/deployment.d.ts +126 -6
- package/dist/core/types/deployment.d.ts.map +1 -1
- package/dist/core/types/deployment.js +1 -1
- package/dist/core/types/deployment.js.map +1 -1
- package/dist/core/types/kubernetes.d.ts +25 -17
- package/dist/core/types/kubernetes.d.ts.map +1 -1
- package/dist/core/types/references.d.ts +1 -1
- package/dist/core/types/references.d.ts.map +1 -1
- package/dist/core/types/references.js.map +1 -1
- package/dist/core/types/resource-graph.d.ts +1 -1
- package/dist/core/types/resource-graph.d.ts.map +1 -1
- package/dist/core/types/schema.d.ts +1 -1
- package/dist/core/types/schema.d.ts.map +1 -1
- package/dist/core/types/serialization.d.ts +24 -6
- package/dist/core/types/serialization.d.ts.map +1 -1
- package/dist/core/validation/cel-validator.d.ts +15 -2
- package/dist/core/validation/cel-validator.d.ts.map +1 -1
- package/dist/core/validation/cel-validator.js +144 -63
- package/dist/core/validation/cel-validator.js.map +1 -1
- package/dist/factories/apisix/compositions/apisix-bootstrap.d.ts +2 -41
- package/dist/factories/apisix/compositions/apisix-bootstrap.d.ts.map +1 -1
- package/dist/factories/apisix/compositions/apisix-bootstrap.js +262 -217
- package/dist/factories/apisix/compositions/apisix-bootstrap.js.map +1 -1
- package/dist/factories/apisix/index.d.ts +2 -2
- package/dist/factories/apisix/index.js +2 -2
- package/dist/factories/apisix/resources/helm.d.ts +2 -2
- package/dist/factories/apisix/resources/helm.d.ts.map +1 -1
- package/dist/factories/apisix/resources/helm.js.map +1 -1
- package/dist/factories/apisix/types.d.ts +21 -11
- package/dist/factories/apisix/types.d.ts.map +1 -1
- package/dist/factories/apisix/types.js +106 -4
- package/dist/factories/apisix/types.js.map +1 -1
- package/dist/factories/apisix/utils/admin-credentials.d.ts +5 -3
- package/dist/factories/apisix/utils/admin-credentials.d.ts.map +1 -1
- package/dist/factories/apisix/utils/admin-credentials.js +14 -10
- package/dist/factories/apisix/utils/admin-credentials.js.map +1 -1
- package/dist/factories/apisix/utils/helm-values-mapper.d.ts.map +1 -1
- package/dist/factories/apisix/utils/helm-values-mapper.js +4 -2
- package/dist/factories/apisix/utils/helm-values-mapper.js.map +1 -1
- package/dist/factories/cert-manager/resources/challenges.js.map +1 -1
- package/dist/factories/cert-manager/types.d.ts +3 -3
- package/dist/factories/cert-manager/types.d.ts.map +1 -1
- package/dist/factories/cilium/compositions/cilium-bootstrap.d.ts +4 -4
- package/dist/factories/cilium/types.d.ts +3 -3
- package/dist/factories/cilium/types.d.ts.map +1 -1
- package/dist/factories/cnpg/compositions/cnpg-bootstrap.d.ts +1 -0
- package/dist/factories/cnpg/compositions/cnpg-bootstrap.d.ts.map +1 -1
- package/dist/factories/cnpg/compositions/cnpg-bootstrap.js +48 -0
- package/dist/factories/cnpg/compositions/cnpg-bootstrap.js.map +1 -1
- package/dist/factories/cnpg/resources/cluster.js +1 -1
- package/dist/factories/cnpg/resources/cluster.js.map +1 -1
- package/dist/factories/cnpg/resources/helm.d.ts.map +1 -1
- package/dist/factories/cnpg/resources/helm.js +1 -0
- package/dist/factories/cnpg/resources/helm.js.map +1 -1
- package/dist/factories/cnpg/resources/pooler.js +1 -1
- package/dist/factories/cnpg/resources/pooler.js.map +1 -1
- package/dist/factories/cnpg/types.d.ts +9 -8
- package/dist/factories/cnpg/types.d.ts.map +1 -1
- package/dist/factories/cnpg/types.js +11 -0
- package/dist/factories/cnpg/types.js.map +1 -1
- package/dist/factories/external-dns/compositions/external-dns-bootstrap.d.ts.map +1 -1
- package/dist/factories/external-dns/compositions/external-dns-bootstrap.js +153 -41
- package/dist/factories/external-dns/compositions/external-dns-bootstrap.js.map +1 -1
- package/dist/factories/external-dns/resources/dns-endpoint.js +1 -1
- package/dist/factories/external-dns/resources/dns-endpoint.js.map +1 -1
- package/dist/factories/external-dns/resources/helm.d.ts +1 -1
- package/dist/factories/external-dns/resources/helm.d.ts.map +1 -1
- package/dist/factories/external-dns/resources/helm.js +17 -10
- package/dist/factories/external-dns/resources/helm.js.map +1 -1
- package/dist/factories/external-dns/types.d.ts +5 -2
- package/dist/factories/external-dns/types.d.ts.map +1 -1
- package/dist/factories/external-dns/types.js.map +1 -1
- package/dist/factories/flux/git-repository.d.ts.map +1 -1
- package/dist/factories/flux/git-repository.js +1 -1
- package/dist/factories/flux/git-repository.js.map +1 -1
- package/dist/factories/flux/kustomize/kustomization.d.ts +2 -2
- package/dist/factories/flux/kustomize/kustomization.d.ts.map +1 -1
- package/dist/factories/flux/kustomize/readiness-evaluators.d.ts +1 -1
- package/dist/factories/flux/kustomize/readiness-evaluators.d.ts.map +1 -1
- package/dist/factories/flux/kustomize/readiness-evaluators.js +1 -1
- package/dist/factories/flux/kustomize/readiness-evaluators.js.map +1 -1
- package/dist/factories/helm/helm-release.d.ts +3 -2
- package/dist/factories/helm/helm-release.d.ts.map +1 -1
- package/dist/factories/helm/helm-release.js +1 -0
- package/dist/factories/helm/helm-release.js.map +1 -1
- package/dist/factories/helm/helm-repository.d.ts +1 -1
- package/dist/factories/helm/helm-repository.d.ts.map +1 -1
- package/dist/factories/helm/helm-repository.js +6 -4
- package/dist/factories/helm/helm-repository.js.map +1 -1
- package/dist/factories/helm/readiness-evaluators.d.ts +6 -6
- package/dist/factories/helm/readiness-evaluators.d.ts.map +1 -1
- package/dist/factories/helm/readiness-evaluators.js +15 -9
- package/dist/factories/helm/readiness-evaluators.js.map +1 -1
- package/dist/factories/helm/types.d.ts +5 -1
- package/dist/factories/helm/types.d.ts.map +1 -1
- package/dist/factories/inngest/compositions/inngest-bootstrap.d.ts +1 -0
- package/dist/factories/inngest/compositions/inngest-bootstrap.d.ts.map +1 -1
- package/dist/factories/inngest/compositions/inngest-bootstrap.js +4 -3
- package/dist/factories/inngest/compositions/inngest-bootstrap.js.map +1 -1
- package/dist/factories/inngest/resources/helm.js +1 -1
- package/dist/factories/inngest/resources/helm.js.map +1 -1
- package/dist/factories/inngest/types.d.ts +5 -4
- package/dist/factories/inngest/types.d.ts.map +1 -1
- package/dist/factories/inngest/types.js +2 -0
- package/dist/factories/inngest/types.js.map +1 -1
- package/dist/factories/kro/kro-custom-resource.js +1 -1
- package/dist/factories/kro/kro-custom-resource.js.map +1 -1
- package/dist/factories/kubernetes/config/config-map.d.ts +2 -2
- package/dist/factories/kubernetes/config/config-map.d.ts.map +1 -1
- package/dist/factories/kubernetes/config/secret.d.ts +2 -2
- package/dist/factories/kubernetes/config/secret.d.ts.map +1 -1
- package/dist/factories/kubernetes/networking/service.js +1 -1
- package/dist/factories/kubernetes/networking/service.js.map +1 -1
- package/dist/factories/kubernetes/yaml/yaml-directory.d.ts.map +1 -1
- package/dist/factories/kubernetes/yaml/yaml-directory.js +9 -0
- package/dist/factories/kubernetes/yaml/yaml-directory.js.map +1 -1
- package/dist/factories/kubernetes/yaml/yaml-file.d.ts.map +1 -1
- package/dist/factories/kubernetes/yaml/yaml-file.js +9 -0
- package/dist/factories/kubernetes/yaml/yaml-file.js.map +1 -1
- package/dist/factories/pebble/resources/helm.js.map +1 -1
- package/dist/factories/pebble/types.d.ts +2 -2
- package/dist/factories/searxng/compositions/searxng-bootstrap.d.ts +3 -2
- package/dist/factories/searxng/compositions/searxng-bootstrap.d.ts.map +1 -1
- package/dist/factories/searxng/compositions/searxng-bootstrap.js +205 -167
- package/dist/factories/searxng/compositions/searxng-bootstrap.js.map +1 -1
- package/dist/factories/searxng/resources/searxng.d.ts +1 -1
- package/dist/factories/searxng/resources/searxng.js +1 -1
- package/dist/factories/searxng/types.d.ts +5 -4
- package/dist/factories/searxng/types.d.ts.map +1 -1
- package/dist/factories/searxng/types.js +8 -7
- package/dist/factories/searxng/types.js.map +1 -1
- package/dist/factories/searxng/utils/settings-builder.d.ts +4 -3
- package/dist/factories/searxng/utils/settings-builder.d.ts.map +1 -1
- package/dist/factories/searxng/utils/settings-builder.js +4 -3
- package/dist/factories/searxng/utils/settings-builder.js.map +1 -1
- package/dist/factories/simple/config/config-map.d.ts +2 -2
- package/dist/factories/simple/config/config-map.d.ts.map +1 -1
- package/dist/factories/simple/config/secret.d.ts +2 -2
- package/dist/factories/simple/config/secret.d.ts.map +1 -1
- package/dist/factories/simple/helm/index.d.ts +1 -1
- package/dist/factories/simple/helm/index.d.ts.map +1 -1
- package/dist/factories/simple/helm/index.js.map +1 -1
- package/dist/factories/simple/storage/persistent-volume.js.map +1 -1
- package/dist/factories/simple/types.d.ts +11 -1
- package/dist/factories/simple/types.d.ts.map +1 -1
- package/dist/factories/simple/workloads/deployment.d.ts.map +1 -1
- package/dist/factories/simple/workloads/deployment.js +3 -0
- package/dist/factories/simple/workloads/deployment.js.map +1 -1
- package/dist/factories/valkey/compositions/valkey-bootstrap.d.ts +1 -0
- package/dist/factories/valkey/compositions/valkey-bootstrap.d.ts.map +1 -1
- package/dist/factories/valkey/compositions/valkey-bootstrap.js +116 -0
- package/dist/factories/valkey/compositions/valkey-bootstrap.js.map +1 -1
- package/dist/factories/valkey/resources/valkey.js +1 -1
- package/dist/factories/valkey/resources/valkey.js.map +1 -1
- package/dist/factories/valkey/types.d.ts +6 -5
- package/dist/factories/valkey/types.d.ts.map +1 -1
- package/dist/factories/valkey/types.js +10 -0
- package/dist/factories/valkey/types.js.map +1 -1
- package/dist/factories/webapp/compositions/web-app-with-processing.d.ts +90 -6
- package/dist/factories/webapp/compositions/web-app-with-processing.d.ts.map +1 -1
- package/dist/factories/webapp/compositions/web-app-with-processing.js +180 -20
- package/dist/factories/webapp/compositions/web-app-with-processing.js.map +1 -1
- package/dist/factories/webapp/index.d.ts +3 -4
- package/dist/factories/webapp/index.d.ts.map +1 -1
- package/dist/factories/webapp/index.js +3 -4
- package/dist/factories/webapp/index.js.map +1 -1
- package/dist/factories/webapp/types.d.ts +60 -2
- package/dist/factories/webapp/types.d.ts.map +1 -1
- package/dist/factories/webapp/types.js +80 -3
- package/dist/factories/webapp/types.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/shared/brands.d.ts +18 -8
- package/dist/shared/brands.d.ts.map +1 -1
- package/dist/shared/brands.js +19 -9
- package/dist/shared/brands.js.map +1 -1
- package/dist/utils/cel-escape.d.ts +12 -0
- package/dist/utils/cel-escape.d.ts.map +1 -0
- package/dist/utils/cel-escape.js +19 -0
- package/dist/utils/cel-escape.js.map +1 -0
- package/package.json +3 -2
|
@@ -4,18 +4,21 @@
|
|
|
4
4
|
* This module provides the main serialization functions to convert
|
|
5
5
|
* TypeScript resource definitions to Kro ResourceGraphDefinition YAML manifests.
|
|
6
6
|
*/
|
|
7
|
-
import { createCompositionContext, runWithCompositionContext } from '../composition/context.js';
|
|
7
|
+
import { createCompositionContext, getCurrentCompositionContext, runInStatusBuilderContext, runWithCompositionContext, } from '../composition/context.js';
|
|
8
8
|
import { createDirectResourceFactory } from '../deployment/direct-factory.js';
|
|
9
9
|
import { createKroResourceFactory } from '../deployment/kro-factory.js';
|
|
10
10
|
import { ensureError, ValidationError } from '../errors.js';
|
|
11
|
+
import { CEL_EXPRESSION_BRAND, KUBERNETES_REF_MARKER_SOURCE } from '../../shared/brands.js';
|
|
11
12
|
import { analyzeCompositionBody, applyAnalysisToResources, } from '../expressions/composition/composition-analyzer.js';
|
|
13
|
+
import { remapResourceStatusReferences } from '../expressions/composition/composition-analyzer-helpers.js';
|
|
12
14
|
import { StatusBuilderAnalyzer } from '../expressions/factory/status-builder-analyzer.js';
|
|
13
15
|
import { getComponentLogger } from '../logging/index.js';
|
|
14
|
-
import { setResourceId } from '../metadata/index.js';
|
|
16
|
+
import { getMetadataField, setResourceId } from '../metadata/index.js';
|
|
15
17
|
import { createExternalRefWithoutRegistration, createSchemaProxy } from '../references/index.js';
|
|
16
18
|
import { getKindInfo, getSemanticCandidateKinds } from '../resources/factory-registry.js';
|
|
17
19
|
import { validateResourceGraphDefinition } from '../validation/cel-validator.js';
|
|
18
20
|
import { optimizeStatusMappings } from './cel-optimizer.js';
|
|
21
|
+
import { finalizeCelForKro } from './cel-references.js';
|
|
19
22
|
import { applyTernaryConditionalsToResources } from './kro-post-processing.js';
|
|
20
23
|
import { generateKroSchemaFromArktype } from './schema.js';
|
|
21
24
|
import { runStatusAnalysisPipeline } from './status-analysis-pipeline.js';
|
|
@@ -247,9 +250,9 @@ function processCompositionBodyAnalysis(statusMappings, resourcesWithKeys, analy
|
|
|
247
250
|
try {
|
|
248
251
|
const resourceIds = new Set(Object.keys(resourcesWithKeys));
|
|
249
252
|
const specJson = schemaDefinition?.spec?.json;
|
|
250
|
-
const optionalFieldNames = specJson
|
|
251
|
-
|
|
252
|
-
|
|
253
|
+
const optionalFieldNames = specJson ? collectOptionalSpecPaths(specJson) : undefined;
|
|
254
|
+
const nestedStatusDescriptor = Object.getOwnPropertyDescriptor(statusMappings, '__nestedStatusCel');
|
|
255
|
+
const nestedStatusCel = nestedStatusDescriptor?.value;
|
|
253
256
|
compositionAnalysis = analyzeCompositionBody(originalCompositionFnForAnalysis, resourceIds, optionalFieldNames);
|
|
254
257
|
// Differential execution to capture untaken-branch resources.
|
|
255
258
|
//
|
|
@@ -270,10 +273,78 @@ function processCompositionBodyAnalysis(statusMappings, resourcesWithKeys, analy
|
|
|
270
273
|
// (so we set them to a sentinel that prevents the composition from
|
|
271
274
|
// dereferencing undefined). Both runs use the same composition
|
|
272
275
|
// function, so resource IDs and factory calls are deterministic.
|
|
273
|
-
|
|
276
|
+
//
|
|
277
|
+
// SKIP when this composition is being executed as a nested call
|
|
278
|
+
// (`context.isNestedCall === true`). The inner composition's own
|
|
279
|
+
// definition-time pass already captured its hybrid-branch analysis
|
|
280
|
+
// with the INNER schema proxy. Re-running that hybrid capture here —
|
|
281
|
+
// against the fresh inner schema proxy that `captureHybridRunResources`
|
|
282
|
+
// creates — would emit differential CEL conditionals that reference
|
|
283
|
+
// inner-schema fields (e.g., `has(schema.spec.secretKeyRef)`) which
|
|
284
|
+
// don't exist in the outer RGD. The outer composition is the
|
|
285
|
+
// authority on branch conditions for its own calls; the inner's
|
|
286
|
+
// branch shape is driven by what the outer passed in.
|
|
287
|
+
// ── Resource-status ternary compilation (Phases 3+4) ────────────
|
|
288
|
+
//
|
|
289
|
+
// When the AST detects ternaries conditioned on resource status
|
|
290
|
+
// fields (e.g., `cache.status.ready ? 'redis' : 'memory'`), proxy
|
|
291
|
+
// JS evaluation does not necessarily match CEL truth evaluation.
|
|
292
|
+
// To emit the CEL conditional, re-execute explicit true and false
|
|
293
|
+
// branches using `liveStatusMap`, then diff those branch outputs.
|
|
294
|
+
//
|
|
295
|
+
// To avoid false positives (other fields changing due to the status
|
|
296
|
+
// flip), direct factory calls diff only `callSiteResourceId`; nested
|
|
297
|
+
// composition call arguments diff only resources registered under known
|
|
298
|
+
// nested composition base IDs.
|
|
299
|
+
const resourceStatusTernaries = compositionAnalysis.resourceStatusTernaries;
|
|
300
|
+
// Deduplicate by call site and condition. Conditionalization is scoped to
|
|
301
|
+
// one callSiteResourceId, so two resources using the same status condition
|
|
302
|
+
// must both be processed.
|
|
303
|
+
const seenConditions = new Set();
|
|
304
|
+
const uniqueTernaries = resourceStatusTernaries.filter((t) => {
|
|
305
|
+
const key = `${t.callSiteResourceId}:${t.variableName}:${t.conditionExpression ?? t.statusField}`;
|
|
306
|
+
if (seenConditions.has(key))
|
|
307
|
+
return false;
|
|
308
|
+
seenConditions.add(key);
|
|
309
|
+
return true;
|
|
310
|
+
});
|
|
311
|
+
// Process EACH resource-status ternary independently to avoid
|
|
312
|
+
// cross-contamination: flip ONE condition → run → diff → apply.
|
|
313
|
+
// Multiple ternaries on the same resource get independent conditionals.
|
|
314
|
+
for (const ternary of uniqueTernaries) {
|
|
315
|
+
const resId = compositionAnalysis.variableToResourceId.get(ternary.variableName) ??
|
|
316
|
+
ternary.variableName;
|
|
317
|
+
if (!resourceIds.has(resId))
|
|
318
|
+
continue;
|
|
319
|
+
const conditionCel = ternary.conditionExpression
|
|
320
|
+
? remapResourceStatusReferences(ternary.conditionExpression, new Map(compositionAnalysis.variableToResourceId).set(ternary.variableName, resId))
|
|
321
|
+
: `${resId}.status.${ternary.statusField}`;
|
|
322
|
+
const trueCtx = runResourceStatusBranch(originalCompositionFnForAnalysis, schemaDefinition, compositionAnalysis, ternary, true);
|
|
323
|
+
const falseCtx = runResourceStatusBranch(originalCompositionFnForAnalysis, schemaDefinition, compositionAnalysis, ternary, false);
|
|
324
|
+
// Diff ONLY the targeted resource(s)
|
|
325
|
+
const targetIds = ternary.callSiteResourceId && ternary.callSiteResourceId !== '__non_factory_call__'
|
|
326
|
+
? [ternary.callSiteResourceId]
|
|
327
|
+
: getNestedResourceStatusTargetIds(trueCtx.resources, trueCtx.nestedCompositionIds);
|
|
328
|
+
for (const id of targetIds) {
|
|
329
|
+
const targetRes = resourcesWithKeys[id];
|
|
330
|
+
const trueRes = trueCtx.resources[id];
|
|
331
|
+
const falseRes = falseCtx.resources[id];
|
|
332
|
+
if (targetRes && trueRes && falseRes) {
|
|
333
|
+
applyResourceStatusBranchDiff(targetRes, trueRes, falseRes, conditionCel, nestedStatusCel, resourceIds);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
serializationLogger.debug('Resource-status branch runs applied', {
|
|
337
|
+
conditionCel,
|
|
338
|
+
targetIds,
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
const currentCtx = getCurrentCompositionContext();
|
|
342
|
+
const skipHybridCapture = currentCtx?.isNestedCall === true;
|
|
343
|
+
if (!skipHybridCapture &&
|
|
344
|
+
schemaDefinition &&
|
|
274
345
|
(compositionAnalysis.unregisteredFactories.length > 0 ||
|
|
275
346
|
collectOverridableOptionalFields(schemaDefinition, compositionAnalysis).size > 0)) {
|
|
276
|
-
const { captured, overriddenFields } = captureHybridRunResources(originalCompositionFnForAnalysis, schemaDefinition, compositionAnalysis);
|
|
347
|
+
const { captured, overriddenFields, overrideConditions } = captureHybridRunResources(originalCompositionFnForAnalysis, schemaDefinition, compositionAnalysis);
|
|
277
348
|
// (a) Merge resources that exist ONLY in the hybrid run — these
|
|
278
349
|
// come from branches the proxy run didn't take (e.g., `if (!spec.x)`).
|
|
279
350
|
// The AST analyzer has already attached the appropriate includeWhen.
|
|
@@ -306,11 +377,27 @@ function processCompositionBodyAnalysis(statusMappings, resourcesWithKeys, analy
|
|
|
306
377
|
// so multiple `toYaml()` calls on the resulting TypedResourceGraph
|
|
307
378
|
// all see a consistent final state.
|
|
308
379
|
if (overriddenFields.size > 0) {
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
380
|
+
const baselineResources = Object.fromEntries(Object.entries(resourcesWithKeys).map(([id, resource]) => [
|
|
381
|
+
id,
|
|
382
|
+
cloneResourceTree(resource),
|
|
383
|
+
]));
|
|
384
|
+
const differentialFields = collectDifferentialOptionalFields(compositionAnalysis);
|
|
385
|
+
const fieldsToDiff = Array.from(differentialFields).filter((field) => overriddenFields.has(field));
|
|
386
|
+
for (const field of fieldsToDiff) {
|
|
387
|
+
const singleFieldSet = new Set([field]);
|
|
388
|
+
const { captured: fieldCaptured } = captureHybridRunResources(originalCompositionFnForAnalysis, schemaDefinition, compositionAnalysis, singleFieldSet);
|
|
389
|
+
const fieldConditions = new Map();
|
|
390
|
+
const explicitCondition = overrideConditions.get(field);
|
|
391
|
+
if (explicitCondition) {
|
|
392
|
+
fieldConditions.set(field, explicitCondition);
|
|
393
|
+
}
|
|
394
|
+
for (const id of Object.keys(resourcesWithKeys)) {
|
|
395
|
+
const proxyRes = resourcesWithKeys[id];
|
|
396
|
+
const baselineRes = baselineResources[id];
|
|
397
|
+
const hybridRes = fieldCaptured[id];
|
|
398
|
+
if (proxyRes && baselineRes && hybridRes) {
|
|
399
|
+
applyDifferentialFieldConditionals(proxyRes, baselineRes, hybridRes, singleFieldSet, fieldConditions, nestedStatusCel, resourceIds);
|
|
400
|
+
}
|
|
314
401
|
}
|
|
315
402
|
}
|
|
316
403
|
}
|
|
@@ -369,23 +456,66 @@ function collectOverridableOptionalFields(schemaDefinition, analysis) {
|
|
|
369
456
|
const specJson = schemaDefinition.spec.json;
|
|
370
457
|
if (!specJson)
|
|
371
458
|
return new Set();
|
|
372
|
-
const
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
459
|
+
const differentialFields = collectDifferentialOptionalFields(analysis);
|
|
460
|
+
return new Set(Array.from(collectOptionalSpecPaths(specJson))
|
|
461
|
+
.filter((field) => differentialFields.has(field)));
|
|
462
|
+
}
|
|
463
|
+
function collectOptionalSpecPaths(schemaJson, prefix = '') {
|
|
464
|
+
const paths = new Set();
|
|
465
|
+
if (!schemaJson || typeof schemaJson !== 'object')
|
|
466
|
+
return paths;
|
|
467
|
+
const node = schemaJson;
|
|
468
|
+
for (const entry of node.optional ?? []) {
|
|
469
|
+
if (!entry.key)
|
|
470
|
+
continue;
|
|
471
|
+
const path = prefix ? `${prefix}.${entry.key}` : entry.key;
|
|
472
|
+
paths.add(path);
|
|
473
|
+
for (const childPath of collectOptionalSpecPaths(entry.value, path)) {
|
|
474
|
+
paths.add(childPath);
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
for (const entry of node.required ?? []) {
|
|
478
|
+
if (!entry.key)
|
|
479
|
+
continue;
|
|
480
|
+
const path = prefix ? `${prefix}.${entry.key}` : entry.key;
|
|
481
|
+
for (const childPath of collectOptionalSpecPaths(entry.value, path)) {
|
|
482
|
+
paths.add(childPath);
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
return paths;
|
|
486
|
+
}
|
|
487
|
+
function collectDifferentialOptionalFields(analysis) {
|
|
488
|
+
const fields = new Set(analysis.hybridOverrideConditions.keys());
|
|
489
|
+
for (const field of analysis.differentialConditionFields) {
|
|
490
|
+
fields.add(field);
|
|
491
|
+
}
|
|
492
|
+
for (const controlFlow of analysis.resources.values()) {
|
|
493
|
+
for (const condition of controlFlow.includeWhen) {
|
|
494
|
+
const expression = condition.expression;
|
|
495
|
+
const matches = expression.matchAll(/schema\.spec\.([a-zA-Z0-9_]+(?:\.[a-zA-Z0-9_]+)*)/g);
|
|
496
|
+
for (const match of matches) {
|
|
497
|
+
const field = match[1];
|
|
498
|
+
if (field) {
|
|
499
|
+
fields.add(field);
|
|
384
500
|
}
|
|
385
501
|
}
|
|
386
502
|
}
|
|
387
503
|
}
|
|
388
|
-
return
|
|
504
|
+
return fields;
|
|
505
|
+
}
|
|
506
|
+
function collectHybridOverrideValues(analysis) {
|
|
507
|
+
const overrideValues = new Map();
|
|
508
|
+
for (const [field, expression] of analysis.hybridOverrideConditions.entries()) {
|
|
509
|
+
const match = expression.match(/^schema\.spec\.([a-zA-Z0-9_.]+)\s*!=\s*false$/);
|
|
510
|
+
const path = match?.[1];
|
|
511
|
+
if (!path || path !== field || overrideValues.has(field))
|
|
512
|
+
continue;
|
|
513
|
+
overrideValues.set(field, false);
|
|
514
|
+
}
|
|
515
|
+
return overrideValues;
|
|
516
|
+
}
|
|
517
|
+
function collectHybridOverrideConditions(analysis) {
|
|
518
|
+
return new Map(analysis.hybridOverrideConditions);
|
|
389
519
|
}
|
|
390
520
|
/**
|
|
391
521
|
* Re-execute the composition function in an isolated composition context
|
|
@@ -412,11 +542,17 @@ function collectOverridableOptionalFields(schemaDefinition, analysis) {
|
|
|
412
542
|
* effects a second time during this re-execution — see integration-skill
|
|
413
543
|
* rule #30 for the full contract.
|
|
414
544
|
*/
|
|
415
|
-
function captureHybridRunResources(compositionFn, schemaDefinition, analysis) {
|
|
545
|
+
function captureHybridRunResources(compositionFn, schemaDefinition, analysis, fieldsToOverride) {
|
|
416
546
|
try {
|
|
417
|
-
const
|
|
418
|
-
|
|
419
|
-
|
|
547
|
+
const allOverriddenFields = collectOverridableOptionalFields(schemaDefinition, analysis);
|
|
548
|
+
const overriddenFields = fieldsToOverride
|
|
549
|
+
? new Set(Array.from(allOverriddenFields).filter((field) => fieldsToOverride.has(field)))
|
|
550
|
+
: allOverriddenFields;
|
|
551
|
+
if (overriddenFields.size === 0) {
|
|
552
|
+
return { captured: {}, overriddenFields, overrideConditions: new Map() };
|
|
553
|
+
}
|
|
554
|
+
const overrideValues = new Map(Array.from(collectHybridOverrideValues(analysis)).filter(([field]) => overriddenFields.has(field)));
|
|
555
|
+
const overrideConditions = new Map(Array.from(collectHybridOverrideConditions(analysis)).filter(([field]) => overriddenFields.has(field)));
|
|
420
556
|
// Build the hybrid spec: the real schema proxy (so all other field
|
|
421
557
|
// accesses produce KubernetesRef values) wrapped in a Proxy that
|
|
422
558
|
// intercepts the overridden keys and returns `undefined`. Accessing
|
|
@@ -424,21 +560,8 @@ function captureHybridRunResources(compositionFn, schemaDefinition, analysis) {
|
|
|
424
560
|
// will throw — but that's exactly the code path the override is
|
|
425
561
|
// meant to skip, so the thrown access lives inside the `else`
|
|
426
562
|
// branch that this run intentionally does not execute.
|
|
427
|
-
const realSchema = createSchemaProxy();
|
|
428
|
-
const hybridSpec =
|
|
429
|
-
get(target, prop, receiver) {
|
|
430
|
-
if (typeof prop === 'string' && overriddenFields.has(prop)) {
|
|
431
|
-
return undefined;
|
|
432
|
-
}
|
|
433
|
-
return Reflect.get(target, prop, receiver);
|
|
434
|
-
},
|
|
435
|
-
has(target, prop) {
|
|
436
|
-
if (typeof prop === 'string' && overriddenFields.has(prop)) {
|
|
437
|
-
return false;
|
|
438
|
-
}
|
|
439
|
-
return Reflect.has(target, prop);
|
|
440
|
-
},
|
|
441
|
-
});
|
|
563
|
+
const realSchema = createSchemaProxy(schemaDefinition.spec?.json, schemaDefinition.status?.json);
|
|
564
|
+
const hybridSpec = createHybridSpecProxy(realSchema.spec, overriddenFields, overrideValues);
|
|
442
565
|
const tempCtx = createCompositionContext('hybrid-capture');
|
|
443
566
|
runWithCompositionContext(tempCtx, () => {
|
|
444
567
|
compositionFn(hybridSpec);
|
|
@@ -446,15 +569,72 @@ function captureHybridRunResources(compositionFn, schemaDefinition, analysis) {
|
|
|
446
569
|
return {
|
|
447
570
|
captured: tempCtx.resources,
|
|
448
571
|
overriddenFields,
|
|
572
|
+
overrideConditions,
|
|
449
573
|
};
|
|
450
574
|
}
|
|
451
575
|
catch {
|
|
452
576
|
// Best-effort: compositions that throw when running with a hybrid spec
|
|
453
577
|
// degrade gracefully — stub resources still cover the missing factories
|
|
454
578
|
// and the proxy-run resources are used as-is.
|
|
455
|
-
return { captured: {}, overriddenFields: new Set() };
|
|
579
|
+
return { captured: {}, overriddenFields: new Set(), overrideConditions: new Map() };
|
|
456
580
|
}
|
|
457
581
|
}
|
|
582
|
+
function createHybridSpecProxy(target, overriddenFields, overrideValues, pathPrefix = '') {
|
|
583
|
+
return new Proxy(target, {
|
|
584
|
+
get(proxyTarget, prop, receiver) {
|
|
585
|
+
if (typeof prop !== 'string') {
|
|
586
|
+
return Reflect.get(proxyTarget, prop, receiver);
|
|
587
|
+
}
|
|
588
|
+
const path = pathPrefix ? `${pathPrefix}.${prop}` : prop;
|
|
589
|
+
if (overriddenFields.has(path)) {
|
|
590
|
+
return overrideValues.has(path) ? overrideValues.get(path) : undefined;
|
|
591
|
+
}
|
|
592
|
+
const hasNestedOverride = Array.from(overriddenFields).some((field) => field.startsWith(`${path}.`));
|
|
593
|
+
const value = Reflect.get(proxyTarget, prop, receiver);
|
|
594
|
+
if (hasNestedOverride && value && (typeof value === 'object' || typeof value === 'function')) {
|
|
595
|
+
return createHybridSpecProxy(value, overriddenFields, overrideValues, path);
|
|
596
|
+
}
|
|
597
|
+
return value;
|
|
598
|
+
},
|
|
599
|
+
has(proxyTarget, prop) {
|
|
600
|
+
if (typeof prop !== 'string') {
|
|
601
|
+
return Reflect.has(proxyTarget, prop);
|
|
602
|
+
}
|
|
603
|
+
const path = pathPrefix ? `${pathPrefix}.${prop}` : prop;
|
|
604
|
+
if (overriddenFields.has(path)) {
|
|
605
|
+
return overrideValues.has(path);
|
|
606
|
+
}
|
|
607
|
+
return Reflect.has(proxyTarget, prop);
|
|
608
|
+
},
|
|
609
|
+
});
|
|
610
|
+
}
|
|
611
|
+
function cloneResourceTree(value) {
|
|
612
|
+
if (Array.isArray(value)) {
|
|
613
|
+
return value.map((item) => cloneResourceTree(item));
|
|
614
|
+
}
|
|
615
|
+
if (isPlainObject(value)) {
|
|
616
|
+
return Object.fromEntries(Object.entries(value).map(([key, entryValue]) => [key, cloneResourceTree(entryValue)]));
|
|
617
|
+
}
|
|
618
|
+
return value;
|
|
619
|
+
}
|
|
620
|
+
function structuralEquals(a, b) {
|
|
621
|
+
if (isLeafValue(a) || isLeafValue(b)) {
|
|
622
|
+
return leafEquals(a, b);
|
|
623
|
+
}
|
|
624
|
+
if (Array.isArray(a) && Array.isArray(b)) {
|
|
625
|
+
return a.length === b.length && a.every((item, index) => structuralEquals(item, b[index]));
|
|
626
|
+
}
|
|
627
|
+
if (isWalkableRecord(a) && isWalkableRecord(b)) {
|
|
628
|
+
const keys = new Set([...Object.keys(a), ...Object.keys(b)]);
|
|
629
|
+
for (const key of keys) {
|
|
630
|
+
if (!structuralEquals(a[key], b[key])) {
|
|
631
|
+
return false;
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
return true;
|
|
635
|
+
}
|
|
636
|
+
return a === b;
|
|
637
|
+
}
|
|
458
638
|
/**
|
|
459
639
|
* Walk `proxyRes` and `hybridRes` in parallel and replace any leaf value
|
|
460
640
|
* in `proxyRes` that differs from the corresponding leaf in `hybridRes`
|
|
@@ -482,59 +662,72 @@ function captureHybridRunResources(compositionFn, schemaDefinition, analysis) {
|
|
|
482
662
|
* are converted to their dollar-wrapped forms before embedding in
|
|
483
663
|
* the conditional.
|
|
484
664
|
*/
|
|
485
|
-
function applyDifferentialFieldConditionals(
|
|
486
|
-
walkAndConditionalize(
|
|
665
|
+
function applyDifferentialFieldConditionals(currentRes, baselineRes, hybridRes, overriddenFields, overrideConditions, nestedStatusCel, resourceIds) {
|
|
666
|
+
walkAndConditionalize(currentRes, baselineRes, hybridRes, overriddenFields, overrideConditions, nestedStatusCel, resourceIds);
|
|
487
667
|
}
|
|
488
|
-
function walkAndConditionalize(
|
|
489
|
-
if (Array.isArray(
|
|
490
|
-
|
|
668
|
+
function walkAndConditionalize(current, baseline, hybrid, overriddenFields, overrideConditions, nestedStatusCel, resourceIds) {
|
|
669
|
+
if (Array.isArray(current) && Array.isArray(baseline) && Array.isArray(hybrid)) {
|
|
670
|
+
if (baseline.length !== hybrid.length) {
|
|
671
|
+
return structuralEquals(current, baseline)
|
|
672
|
+
? buildCelConditional(baseline, hybrid, overriddenFields, overrideConditions, nestedStatusCel, resourceIds)
|
|
673
|
+
: current;
|
|
674
|
+
}
|
|
675
|
+
const maxLen = Math.max(current.length, hybrid.length);
|
|
491
676
|
for (let i = 0; i < maxLen; i++) {
|
|
492
|
-
const
|
|
677
|
+
const c = current[i];
|
|
678
|
+
const b = baseline[i];
|
|
493
679
|
const h = hybrid[i];
|
|
494
|
-
if (i >=
|
|
680
|
+
if (i >= current.length) {
|
|
495
681
|
// New element added by hybrid run — copy it over.
|
|
496
|
-
|
|
682
|
+
current.push(h);
|
|
497
683
|
continue;
|
|
498
684
|
}
|
|
499
685
|
if (i >= hybrid.length) {
|
|
500
686
|
// Element removed in hybrid — leave the proxy value as-is.
|
|
501
687
|
continue;
|
|
502
688
|
}
|
|
503
|
-
if (isLeafValue(
|
|
504
|
-
if (!leafEquals(
|
|
505
|
-
|
|
689
|
+
if (isLeafValue(b) && isLeafValue(h)) {
|
|
690
|
+
if (!leafEquals(b, h) && leafEquals(c, b)) {
|
|
691
|
+
current[i] = buildCelConditional(b, h, overriddenFields, overrideConditions, nestedStatusCel, resourceIds);
|
|
506
692
|
}
|
|
507
693
|
}
|
|
508
694
|
else {
|
|
509
|
-
walkAndConditionalize(
|
|
695
|
+
const conditionalized = walkAndConditionalize(c, b, h, overriddenFields, overrideConditions, nestedStatusCel, resourceIds);
|
|
696
|
+
if (conditionalized !== c) {
|
|
697
|
+
current[i] = conditionalized;
|
|
698
|
+
}
|
|
510
699
|
}
|
|
511
700
|
}
|
|
512
|
-
return
|
|
701
|
+
return current;
|
|
513
702
|
}
|
|
514
|
-
if (
|
|
515
|
-
const keys = new Set([...Object.keys(
|
|
703
|
+
if (isWalkableRecord(current) && isWalkableRecord(baseline) && isWalkableRecord(hybrid)) {
|
|
704
|
+
const keys = new Set([...Object.keys(current), ...Object.keys(baseline), ...Object.keys(hybrid)]);
|
|
516
705
|
for (const key of keys) {
|
|
517
|
-
const
|
|
706
|
+
const c = current[key];
|
|
707
|
+
const b = baseline[key];
|
|
518
708
|
const h = hybrid[key];
|
|
519
709
|
if (!(key in hybrid)) {
|
|
520
|
-
continue;
|
|
710
|
+
continue;
|
|
521
711
|
}
|
|
522
|
-
if (!(key in
|
|
523
|
-
|
|
712
|
+
if (!(key in current)) {
|
|
713
|
+
current[key] = h;
|
|
524
714
|
continue;
|
|
525
715
|
}
|
|
526
|
-
if (isLeafValue(
|
|
527
|
-
if (!leafEquals(
|
|
528
|
-
|
|
716
|
+
if (isLeafValue(b) && isLeafValue(h)) {
|
|
717
|
+
if (!leafEquals(b, h) && leafEquals(c, b)) {
|
|
718
|
+
current[key] = buildCelConditional(b, h, overriddenFields, overrideConditions, nestedStatusCel, resourceIds);
|
|
529
719
|
}
|
|
530
720
|
}
|
|
531
721
|
else {
|
|
532
|
-
walkAndConditionalize(
|
|
722
|
+
const conditionalized = walkAndConditionalize(c, b, h, overriddenFields, overrideConditions, nestedStatusCel, resourceIds);
|
|
723
|
+
if (conditionalized !== c) {
|
|
724
|
+
current[key] = conditionalized;
|
|
725
|
+
}
|
|
533
726
|
}
|
|
534
727
|
}
|
|
535
|
-
return
|
|
728
|
+
return current;
|
|
536
729
|
}
|
|
537
|
-
return
|
|
730
|
+
return current;
|
|
538
731
|
}
|
|
539
732
|
function isLeafValue(v) {
|
|
540
733
|
return (v === null ||
|
|
@@ -542,9 +735,213 @@ function isLeafValue(v) {
|
|
|
542
735
|
typeof v === 'string' ||
|
|
543
736
|
typeof v === 'number' ||
|
|
544
737
|
typeof v === 'boolean' ||
|
|
738
|
+
isCelExpressionLike(v) ||
|
|
545
739
|
// KubernetesRef proxies register as functions (typeof fn is 'function')
|
|
546
740
|
typeof v === 'function');
|
|
547
741
|
}
|
|
742
|
+
function runResourceStatusBranch(compositionFn, schemaDefinition, analysis, ternary, desiredConditionValue) {
|
|
743
|
+
const branchCtx = createCompositionContext('resource-status-branch', {
|
|
744
|
+
isReExecution: true,
|
|
745
|
+
});
|
|
746
|
+
branchCtx.liveStatusMap = createResourceStatusBranchMap(analysis, ternary, desiredConditionValue);
|
|
747
|
+
const branchSchema = createSchemaProxy(schemaDefinition?.spec?.json, schemaDefinition?.status?.json);
|
|
748
|
+
const specOverrides = createSpecConditionOverrideMap(ternary.conditionExpression, desiredConditionValue);
|
|
749
|
+
const branchSpec = specOverrides.size > 0
|
|
750
|
+
? createSpecOverrideProxy(branchSchema.spec, specOverrides)
|
|
751
|
+
: branchSchema.spec;
|
|
752
|
+
runWithCompositionContext(branchCtx, () => {
|
|
753
|
+
runInStatusBuilderContext(() => {
|
|
754
|
+
compositionFn(branchSpec);
|
|
755
|
+
});
|
|
756
|
+
});
|
|
757
|
+
return branchCtx;
|
|
758
|
+
}
|
|
759
|
+
function createResourceStatusBranchMap(analysis, ternary, desiredConditionValue) {
|
|
760
|
+
const conditionExpression = ternary.conditionExpression ?? `${ternary.variableName}.status.${ternary.statusField}`;
|
|
761
|
+
const statusRefs = collectStatusRefs(conditionExpression);
|
|
762
|
+
if (statusRefs.length === 0) {
|
|
763
|
+
statusRefs.push({ variableName: ternary.variableName, statusField: ternary.statusField });
|
|
764
|
+
}
|
|
765
|
+
const statusMap = new Map();
|
|
766
|
+
for (const statusRef of statusRefs) {
|
|
767
|
+
const resourceId = analysis.variableToResourceId.get(statusRef.variableName) ?? statusRef.variableName;
|
|
768
|
+
const existing = statusMap.get(resourceId) ?? {};
|
|
769
|
+
setNestedBranchStatusValue(existing, statusRef.statusField, getBranchStatusValue(conditionExpression, `${statusRef.variableName}.status.${statusRef.statusField}`, desiredConditionValue));
|
|
770
|
+
statusMap.set(resourceId, existing);
|
|
771
|
+
}
|
|
772
|
+
return statusMap;
|
|
773
|
+
}
|
|
774
|
+
function setNestedBranchStatusValue(target, statusField, value) {
|
|
775
|
+
const parts = statusField.split('.').filter(Boolean);
|
|
776
|
+
if (parts.length === 0)
|
|
777
|
+
return;
|
|
778
|
+
let cursor = target;
|
|
779
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
780
|
+
const part = parts[i];
|
|
781
|
+
if (!part)
|
|
782
|
+
continue;
|
|
783
|
+
const next = cursor[part];
|
|
784
|
+
if (!isPlainObject(next)) {
|
|
785
|
+
cursor[part] = {};
|
|
786
|
+
}
|
|
787
|
+
cursor = cursor[part];
|
|
788
|
+
}
|
|
789
|
+
const leaf = parts[parts.length - 1];
|
|
790
|
+
if (leaf) {
|
|
791
|
+
cursor[leaf] = value;
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
function collectStatusRefs(conditionExpression) {
|
|
795
|
+
const refs = [];
|
|
796
|
+
const seen = new Set();
|
|
797
|
+
const statusRefPattern = /\b([A-Za-z_$][\w$]*)\.status\.([A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)*)/g;
|
|
798
|
+
for (const match of conditionExpression.matchAll(statusRefPattern)) {
|
|
799
|
+
const variableName = match[1];
|
|
800
|
+
const statusField = match[2];
|
|
801
|
+
if (!variableName || !statusField)
|
|
802
|
+
continue;
|
|
803
|
+
const key = `${variableName}:${statusField}`;
|
|
804
|
+
if (seen.has(key))
|
|
805
|
+
continue;
|
|
806
|
+
seen.add(key);
|
|
807
|
+
refs.push({ variableName, statusField });
|
|
808
|
+
}
|
|
809
|
+
return refs;
|
|
810
|
+
}
|
|
811
|
+
function createSpecConditionOverrideMap(conditionExpression, desiredConditionValue) {
|
|
812
|
+
const overrides = new Map();
|
|
813
|
+
if (!conditionExpression)
|
|
814
|
+
return overrides;
|
|
815
|
+
const specRefPattern = /\b(?:schema\.)?spec\.([A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)*)/g;
|
|
816
|
+
for (const match of conditionExpression.matchAll(specRefPattern)) {
|
|
817
|
+
const specPath = match[1];
|
|
818
|
+
const fullRef = match[0];
|
|
819
|
+
if (!specPath || !fullRef || overrides.has(specPath))
|
|
820
|
+
continue;
|
|
821
|
+
overrides.set(specPath, getBranchStatusValue(conditionExpression, fullRef, desiredConditionValue));
|
|
822
|
+
}
|
|
823
|
+
return overrides;
|
|
824
|
+
}
|
|
825
|
+
function createSpecOverrideProxy(target, overrides, path = []) {
|
|
826
|
+
return new Proxy(target, {
|
|
827
|
+
get(obj, prop, receiver) {
|
|
828
|
+
if (typeof prop !== 'string')
|
|
829
|
+
return Reflect.get(obj, prop, receiver);
|
|
830
|
+
const fullPath = [...path, prop].join('.');
|
|
831
|
+
if (overrides.has(fullPath))
|
|
832
|
+
return overrides.get(fullPath);
|
|
833
|
+
const hasNestedOverride = [...overrides.keys()].some((key) => key.startsWith(`${fullPath}.`));
|
|
834
|
+
const value = Reflect.get(obj, prop, receiver);
|
|
835
|
+
if (hasNestedOverride && value && (typeof value === 'object' || typeof value === 'function')) {
|
|
836
|
+
return createSpecOverrideProxy(value, overrides, [...path, prop]);
|
|
837
|
+
}
|
|
838
|
+
return value;
|
|
839
|
+
},
|
|
840
|
+
ownKeys: (obj) => Reflect.ownKeys(obj),
|
|
841
|
+
getOwnPropertyDescriptor: (obj, prop) => Reflect.getOwnPropertyDescriptor(obj, prop),
|
|
842
|
+
});
|
|
843
|
+
}
|
|
844
|
+
function getBranchStatusValue(conditionExpression, statusRefExpression, desiredConditionValue) {
|
|
845
|
+
const escapedRef = statusRefExpression.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
846
|
+
const comparison = conditionExpression.match(new RegExp(`${escapedRef}\\s*(>=|>|<=|<|==|!=)\\s*(-?\\d+(?:\\.\\d+)?)`));
|
|
847
|
+
if (comparison?.[1] && comparison[2] !== undefined) {
|
|
848
|
+
const operator = comparison[1];
|
|
849
|
+
const numberValue = Number(comparison[2]);
|
|
850
|
+
if (operator === '>=')
|
|
851
|
+
return desiredConditionValue ? numberValue : numberValue - 1;
|
|
852
|
+
if (operator === '>')
|
|
853
|
+
return desiredConditionValue ? numberValue + 1 : numberValue;
|
|
854
|
+
if (operator === '<=')
|
|
855
|
+
return desiredConditionValue ? numberValue : numberValue + 1;
|
|
856
|
+
if (operator === '<')
|
|
857
|
+
return desiredConditionValue ? numberValue - 1 : numberValue;
|
|
858
|
+
if (operator === '==')
|
|
859
|
+
return desiredConditionValue ? numberValue : numberValue + 1;
|
|
860
|
+
if (operator === '!=')
|
|
861
|
+
return desiredConditionValue ? numberValue + 1 : numberValue;
|
|
862
|
+
}
|
|
863
|
+
const stringComparison = conditionExpression.match(new RegExp(`${escapedRef}\\s*(==|!=)\\s*(['"])(.*?)\\2`));
|
|
864
|
+
if (stringComparison?.[1] && stringComparison[3] !== undefined) {
|
|
865
|
+
const operator = stringComparison[1];
|
|
866
|
+
const stringValue = stringComparison[3];
|
|
867
|
+
if (operator === '==')
|
|
868
|
+
return desiredConditionValue ? stringValue : `__typekro_not_${stringValue}`;
|
|
869
|
+
if (operator === '!=')
|
|
870
|
+
return desiredConditionValue ? `__typekro_not_${stringValue}` : stringValue;
|
|
871
|
+
}
|
|
872
|
+
const booleanComparison = conditionExpression.match(new RegExp(`${escapedRef}\\s*(==|!=)\\s*(true|false)`));
|
|
873
|
+
if (booleanComparison?.[1] && booleanComparison[2] !== undefined) {
|
|
874
|
+
const operator = booleanComparison[1];
|
|
875
|
+
const booleanValue = booleanComparison[2] === 'true';
|
|
876
|
+
if (operator === '==')
|
|
877
|
+
return desiredConditionValue ? booleanValue : !booleanValue;
|
|
878
|
+
if (operator === '!=')
|
|
879
|
+
return desiredConditionValue ? !booleanValue : booleanValue;
|
|
880
|
+
}
|
|
881
|
+
const negatedRef = new RegExp(`!\\s*${escapedRef}(?![A-Za-z0-9_$.])`).test(conditionExpression);
|
|
882
|
+
return negatedRef ? !desiredConditionValue : desiredConditionValue;
|
|
883
|
+
}
|
|
884
|
+
function applyResourceStatusBranchDiff(targetRes, trueRes, falseRes, conditionCel, nestedStatusCel, resourceIds) {
|
|
885
|
+
for (const key of new Set([...Object.keys(trueRes), ...Object.keys(falseRes)])) {
|
|
886
|
+
if (key === '__resourceId' || key === 'id' || key.startsWith('__'))
|
|
887
|
+
continue;
|
|
888
|
+
const tv = trueRes[key];
|
|
889
|
+
const fv = falseRes[key];
|
|
890
|
+
if (tv === undefined && fv !== undefined) {
|
|
891
|
+
const falseRepr = celValueRepr(fv, nestedStatusCel, resourceIds);
|
|
892
|
+
targetRes[key] = `\${${conditionCel} ? omit() : ${falseRepr}}`;
|
|
893
|
+
continue;
|
|
894
|
+
}
|
|
895
|
+
if (fv === undefined && tv !== undefined) {
|
|
896
|
+
const trueRepr = celValueRepr(tv, nestedStatusCel, resourceIds);
|
|
897
|
+
targetRes[key] = `\${${conditionCel} ? ${trueRepr} : omit()}`;
|
|
898
|
+
continue;
|
|
899
|
+
}
|
|
900
|
+
if (tv === undefined || fv === undefined)
|
|
901
|
+
continue;
|
|
902
|
+
if (isCelExpressionLike(tv) || isCelExpressionLike(fv)) {
|
|
903
|
+
if (!leafEquals(tv, fv)) {
|
|
904
|
+
const trueRepr = celValueRepr(tv, nestedStatusCel, resourceIds);
|
|
905
|
+
const falseRepr = celValueRepr(fv, nestedStatusCel, resourceIds);
|
|
906
|
+
targetRes[key] = `\${${conditionCel} ? ${trueRepr} : ${falseRepr}}`;
|
|
907
|
+
}
|
|
908
|
+
continue;
|
|
909
|
+
}
|
|
910
|
+
const targetValue = targetRes[key];
|
|
911
|
+
if (isPlainObject(tv) && isPlainObject(fv)) {
|
|
912
|
+
if (!isPlainObject(targetValue))
|
|
913
|
+
targetRes[key] = {};
|
|
914
|
+
applyResourceStatusBranchDiff(targetRes[key], tv, fv, conditionCel, nestedStatusCel, resourceIds);
|
|
915
|
+
}
|
|
916
|
+
else if (Array.isArray(tv) && Array.isArray(fv) && tv.length === fv.length) {
|
|
917
|
+
if (!Array.isArray(targetValue))
|
|
918
|
+
targetRes[key] = [...tv];
|
|
919
|
+
const targetArray = targetRes[key];
|
|
920
|
+
for (let i = 0; i < tv.length; i++) {
|
|
921
|
+
if (isPlainObject(tv[i]) && isPlainObject(fv[i])) {
|
|
922
|
+
if (!isPlainObject(targetArray[i]))
|
|
923
|
+
targetArray[i] = {};
|
|
924
|
+
applyResourceStatusBranchDiff(targetArray[i], tv[i], fv[i], conditionCel, nestedStatusCel, resourceIds);
|
|
925
|
+
}
|
|
926
|
+
else if (!leafEquals(tv[i], fv[i])) {
|
|
927
|
+
const trueRepr = celValueRepr(tv[i], nestedStatusCel, resourceIds);
|
|
928
|
+
const falseRepr = celValueRepr(fv[i], nestedStatusCel, resourceIds);
|
|
929
|
+
targetArray[i] = `\${${conditionCel} ? ${trueRepr} : ${falseRepr}}`;
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
else if (Array.isArray(tv) && Array.isArray(fv) && tv.length !== fv.length) {
|
|
934
|
+
const trueRepr = celValueRepr(tv, nestedStatusCel, resourceIds);
|
|
935
|
+
const falseRepr = celValueRepr(fv, nestedStatusCel, resourceIds);
|
|
936
|
+
targetRes[key] = `\${${conditionCel} ? ${trueRepr} : ${falseRepr}}`;
|
|
937
|
+
}
|
|
938
|
+
else if (!leafEquals(tv, fv)) {
|
|
939
|
+
const trueRepr = celValueRepr(tv, nestedStatusCel, resourceIds);
|
|
940
|
+
const falseRepr = celValueRepr(fv, nestedStatusCel, resourceIds);
|
|
941
|
+
targetRes[key] = `\${${conditionCel} ? ${trueRepr} : ${falseRepr}}`;
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
}
|
|
548
945
|
function leafEquals(a, b) {
|
|
549
946
|
// For KubernetesRef proxies, compare their string coercions (marker tokens).
|
|
550
947
|
if (typeof a === 'function' || typeof b === 'function') {
|
|
@@ -552,8 +949,45 @@ function leafEquals(a, b) {
|
|
|
552
949
|
}
|
|
553
950
|
return a === b;
|
|
554
951
|
}
|
|
952
|
+
function getNestedResourceStatusTargetIds(resources, nestedCompositionIds) {
|
|
953
|
+
if (!nestedCompositionIds || nestedCompositionIds.size === 0)
|
|
954
|
+
return [];
|
|
955
|
+
return Object.entries(resources)
|
|
956
|
+
.filter(([resourceId, resource]) => [...nestedCompositionIds].some((nestedId) => isNestedCompositionChild(resourceId, resource, nestedId)))
|
|
957
|
+
.map(([resourceId]) => resourceId);
|
|
958
|
+
}
|
|
959
|
+
function isNestedCompositionChild(resourceId, resource, nestedId) {
|
|
960
|
+
if (resourceId === nestedId)
|
|
961
|
+
return true;
|
|
962
|
+
const boundaryChar = resourceId[nestedId.length];
|
|
963
|
+
if (resourceId.startsWith(nestedId) && boundaryChar !== undefined && /[A-Z_-]/.test(boundaryChar)) {
|
|
964
|
+
return true;
|
|
965
|
+
}
|
|
966
|
+
const aliases = getMetadataField(resource, 'resourceAliases');
|
|
967
|
+
return aliases?.some((alias) => {
|
|
968
|
+
if (alias === nestedId)
|
|
969
|
+
return true;
|
|
970
|
+
const aliasBoundaryChar = alias[nestedId.length];
|
|
971
|
+
return alias.startsWith(nestedId) && aliasBoundaryChar !== undefined && /[A-Z_-]/.test(aliasBoundaryChar);
|
|
972
|
+
}) ?? false;
|
|
973
|
+
}
|
|
555
974
|
function isPlainObject(v) {
|
|
556
|
-
return typeof v === 'object' && v !== null && !Array.isArray(v);
|
|
975
|
+
return typeof v === 'object' && v !== null && !Array.isArray(v) && !isCelExpressionLike(v);
|
|
976
|
+
}
|
|
977
|
+
function isWalkableRecord(v) {
|
|
978
|
+
if (isPlainObject(v)) {
|
|
979
|
+
return true;
|
|
980
|
+
}
|
|
981
|
+
return typeof v === 'function' && Object.keys(v).length > 0;
|
|
982
|
+
}
|
|
983
|
+
/** Duck-type check for CelExpression objects — avoids importing from cel.ts (cycle risk). */
|
|
984
|
+
function isCelExpressionLike(v) {
|
|
985
|
+
if (typeof v !== 'object' || v === null)
|
|
986
|
+
return false;
|
|
987
|
+
// Plain Cel.expr() objects carry the symbol brand; template expressions also
|
|
988
|
+
// carry `__isTemplate`. Support both shapes without importing cel.ts.
|
|
989
|
+
return 'expression' in v && typeof v.expression === 'string'
|
|
990
|
+
&& (v[CEL_EXPRESSION_BRAND] === true || '__isTemplate' in v);
|
|
557
991
|
}
|
|
558
992
|
/**
|
|
559
993
|
* Build a CEL conditional string from two diverging leaf values.
|
|
@@ -564,11 +998,44 @@ function isPlainObject(v) {
|
|
|
564
998
|
* `${has(schema.spec.X) ? <proxy repr> : <hybrid repr>}` that later
|
|
565
999
|
* serialization phases treat as a final CEL expression.
|
|
566
1000
|
*/
|
|
567
|
-
function buildCelConditional(proxyValue, hybridValue, overriddenFields) {
|
|
1001
|
+
function buildCelConditional(proxyValue, hybridValue, overriddenFields, overrideConditions, nestedStatusCel, resourceIds) {
|
|
568
1002
|
const field = pickConditionField(proxyValue, hybridValue, overriddenFields);
|
|
569
|
-
const proxyRepr = celValueRepr(proxyValue);
|
|
570
|
-
const hybridRepr = celValueRepr(hybridValue);
|
|
571
|
-
|
|
1003
|
+
const proxyRepr = celValueRepr(proxyValue, nestedStatusCel, resourceIds);
|
|
1004
|
+
const hybridRepr = celValueRepr(hybridValue, nestedStatusCel, resourceIds);
|
|
1005
|
+
const explicitCondition = overrideConditions.get(field);
|
|
1006
|
+
if (explicitCondition) {
|
|
1007
|
+
return `\${${explicitCondition} ? ${proxyRepr} : ${hybridRepr}}`;
|
|
1008
|
+
}
|
|
1009
|
+
// Chain has() guards when the proxy value references a sub-field deeper
|
|
1010
|
+
// than the controlling optional field. A single has(schema.spec.X) is
|
|
1011
|
+
// insufficient when the value accesses X.Y — the user may provide X: {}
|
|
1012
|
+
// without Y, and KRO would fail with "no such key: Y".
|
|
1013
|
+
const guardField = `schema.spec.${field}`;
|
|
1014
|
+
let guard = `has(${guardField})`;
|
|
1015
|
+
// Extract the full schema path from the proxy repr to check depth.
|
|
1016
|
+
// proxyRepr may be a bare path like `schema.spec.cnpgOperator.version`
|
|
1017
|
+
// or wrapped in string() like `string(schema.spec.cnpgOperator.version)`.
|
|
1018
|
+
// Chain has() guards for ALL intermediate levels between the controlling
|
|
1019
|
+
// field and the leaf. For `cnpgOperator.monitoring.enabled`, we need:
|
|
1020
|
+
// has(cnpgOperator) && has(cnpgOperator.monitoring) && has(cnpgOperator.monitoring.enabled)
|
|
1021
|
+
const schemaPathMatch = proxyRepr.match(/schema\.spec\.([a-zA-Z0-9_.]+)/);
|
|
1022
|
+
if (schemaPathMatch) {
|
|
1023
|
+
const fullRefPath = schemaPathMatch[1]?.replace(/\.+$/, '');
|
|
1024
|
+
if (!fullRefPath)
|
|
1025
|
+
return `\${has(${guardField}) ? ${proxyRepr} : ${hybridRepr}}`;
|
|
1026
|
+
const fullPath = `schema.spec.${fullRefPath}`;
|
|
1027
|
+
if (fullPath !== guardField && fullPath.startsWith(`${guardField}.`)) {
|
|
1028
|
+
const guardSegments = field.split('.').length;
|
|
1029
|
+
const leafSegments = fullRefPath.split('.');
|
|
1030
|
+
const guards = [`has(${guardField})`];
|
|
1031
|
+
for (let j = guardSegments + 1; j <= leafSegments.length; j++) {
|
|
1032
|
+
const intermediatePath = `schema.spec.${leafSegments.slice(0, j).join('.')}`;
|
|
1033
|
+
guards.push(`has(${intermediatePath})`);
|
|
1034
|
+
}
|
|
1035
|
+
guard = guards.join(' && ');
|
|
1036
|
+
}
|
|
1037
|
+
}
|
|
1038
|
+
return `\${${guard} ? ${proxyRepr} : ${hybridRepr}}`;
|
|
572
1039
|
}
|
|
573
1040
|
/**
|
|
574
1041
|
* Pick the "controlling" optional field for a diverging leaf. If exactly
|
|
@@ -612,11 +1079,18 @@ function pickConditionField(proxyValue, hybridValue, overriddenFields) {
|
|
|
612
1079
|
* result is valid CEL, not just a raw string)
|
|
613
1080
|
* - Numbers and booleans are emitted verbatim
|
|
614
1081
|
*/
|
|
615
|
-
|
|
1082
|
+
// Re-export from shared utility for local use. This was previously an
|
|
1083
|
+
// inline copy; the canonical implementation lives in utils/cel-escape.ts.
|
|
1084
|
+
import { escapeCelString as escapeCelLiteral } from '../../utils/cel-escape.js';
|
|
1085
|
+
function celValueRepr(value, nestedStatusCel, resourceIds) {
|
|
616
1086
|
if (value === null || value === undefined)
|
|
617
1087
|
return '""';
|
|
618
1088
|
if (typeof value === 'number' || typeof value === 'boolean')
|
|
619
1089
|
return String(value);
|
|
1090
|
+
// CelExpression object — use the expression string directly.
|
|
1091
|
+
if (isCelExpressionLike(value)) {
|
|
1092
|
+
return unwrapKroExpression(finalizeCelForKro(value.expression, nestedStatusCel, createBranchCelContext(nestedStatusCel, resourceIds)));
|
|
1093
|
+
}
|
|
620
1094
|
if (typeof value === 'function') {
|
|
621
1095
|
// KubernetesRef proxy — toString yields the marker token which we
|
|
622
1096
|
// convert to a bare CEL path via the marker → CEL rules.
|
|
@@ -627,16 +1101,40 @@ function celValueRepr(value) {
|
|
|
627
1101
|
return markerStringToCelExpr(value);
|
|
628
1102
|
}
|
|
629
1103
|
// Plain string literal — escape for CEL embedding
|
|
630
|
-
return `"${value
|
|
1104
|
+
return `"${escapeCelLiteral(value)}"`;
|
|
1105
|
+
}
|
|
1106
|
+
if (Array.isArray(value)) {
|
|
1107
|
+
return `[${value.map((item) => celValueRepr(item, nestedStatusCel, resourceIds)).join(', ')}]`;
|
|
1108
|
+
}
|
|
1109
|
+
if (isPlainObject(value)) {
|
|
1110
|
+
return `{${Object.entries(value)
|
|
1111
|
+
.map(([key, entryValue]) => `"${escapeCelLiteral(key)}": ${celValueRepr(entryValue, nestedStatusCel, resourceIds)}`)
|
|
1112
|
+
.join(', ')}}`;
|
|
631
1113
|
}
|
|
632
1114
|
return '""';
|
|
633
1115
|
}
|
|
1116
|
+
function createBranchCelContext(nestedStatusCel, resourceIds) {
|
|
1117
|
+
if (!nestedStatusCel && !resourceIds)
|
|
1118
|
+
return undefined;
|
|
1119
|
+
return {
|
|
1120
|
+
celPrefix: '',
|
|
1121
|
+
resourceIdStrategy: 'deterministic',
|
|
1122
|
+
...(nestedStatusCel ? { nestedStatusCel } : {}),
|
|
1123
|
+
...(resourceIds ? { resourceIds } : {}),
|
|
1124
|
+
};
|
|
1125
|
+
}
|
|
1126
|
+
function unwrapKroExpression(value) {
|
|
1127
|
+
if (value.startsWith('${') && value.endsWith('}') && value.indexOf('${', 2) === -1) {
|
|
1128
|
+
return value.slice(2, -1);
|
|
1129
|
+
}
|
|
1130
|
+
return value;
|
|
1131
|
+
}
|
|
634
1132
|
/**
|
|
635
1133
|
* Convert a single-marker string (the whole string is one marker) to
|
|
636
1134
|
* its bare CEL path form: `schema.spec.X` or `resources.X.field`.
|
|
637
1135
|
*/
|
|
638
1136
|
function markerStringToCelBare(str) {
|
|
639
|
-
const m = str.match(
|
|
1137
|
+
const m = str.match(new RegExp(`^${KUBERNETES_REF_MARKER_SOURCE}$`));
|
|
640
1138
|
if (!m)
|
|
641
1139
|
return markerStringToCelExpr(str);
|
|
642
1140
|
const [, resourceId, fieldPath] = m;
|
|
@@ -648,8 +1146,9 @@ function markerStringToCelBare(str) {
|
|
|
648
1146
|
* embedding inside a CEL ternary.
|
|
649
1147
|
*/
|
|
650
1148
|
function markerStringToCelExpr(str) {
|
|
1149
|
+
const markerSource = KUBERNETES_REF_MARKER_SOURCE;
|
|
651
1150
|
// Fast path: whole string is a single marker
|
|
652
|
-
const singleMatch = str.match(
|
|
1151
|
+
const singleMatch = str.match(new RegExp(`^${markerSource}$`));
|
|
653
1152
|
if (singleMatch) {
|
|
654
1153
|
const [, resourceId, fieldPath] = singleMatch;
|
|
655
1154
|
return resourceId === '__schema__' ? `schema.${fieldPath}` : `${resourceId}.${fieldPath}`;
|
|
@@ -657,15 +1156,19 @@ function markerStringToCelExpr(str) {
|
|
|
657
1156
|
// Slow path: interleave literal text and markers via CEL string concatenation
|
|
658
1157
|
const parts = [];
|
|
659
1158
|
let lastIndex = 0;
|
|
660
|
-
const pattern =
|
|
1159
|
+
const pattern = new RegExp(markerSource, 'g');
|
|
661
1160
|
let m = pattern.exec(str);
|
|
662
1161
|
while (m !== null) {
|
|
663
1162
|
if (m.index > lastIndex) {
|
|
664
1163
|
const literal = str.slice(lastIndex, m.index);
|
|
665
|
-
parts.push(`"${literal
|
|
1164
|
+
parts.push(`"${escapeCelLiteral(literal)}"`);
|
|
666
1165
|
}
|
|
667
1166
|
const resourceId = m[1];
|
|
668
1167
|
const fieldPath = m[2];
|
|
1168
|
+
if (!resourceId || !fieldPath) {
|
|
1169
|
+
m = pattern.exec(str);
|
|
1170
|
+
continue;
|
|
1171
|
+
}
|
|
669
1172
|
const celPath = resourceId === '__schema__' ? `schema.${fieldPath}` : `${resourceId}.${fieldPath}`;
|
|
670
1173
|
parts.push(`string(${celPath})`);
|
|
671
1174
|
lastIndex = m.index + m[0].length;
|
|
@@ -673,9 +1176,10 @@ function markerStringToCelExpr(str) {
|
|
|
673
1176
|
}
|
|
674
1177
|
if (lastIndex < str.length) {
|
|
675
1178
|
const literal = str.slice(lastIndex);
|
|
676
|
-
parts.push(`"${literal
|
|
1179
|
+
parts.push(`"${escapeCelLiteral(literal)}"`);
|
|
677
1180
|
}
|
|
678
|
-
|
|
1181
|
+
const [firstPart] = parts;
|
|
1182
|
+
return parts.length === 1 && firstPart !== undefined ? firstPart : parts.join(' + ');
|
|
679
1183
|
}
|
|
680
1184
|
// =============================================================================
|
|
681
1185
|
// Extracted helper: Direct factory status re-analysis
|
|
@@ -828,10 +1332,14 @@ function createTypedResourceGraph(definition, resourceBuilder, statusBuilder, op
|
|
|
828
1332
|
const schemaDefinition = {
|
|
829
1333
|
apiVersion: definition.apiVersion || 'v1alpha1',
|
|
830
1334
|
kind: definition.kind,
|
|
1335
|
+
...(definition.group && { group: definition.group }),
|
|
831
1336
|
spec: definition.spec,
|
|
832
1337
|
status: definition.status,
|
|
833
1338
|
};
|
|
834
|
-
|
|
1339
|
+
// Pass the Arktype JSON so the proxy is shape-aware — spread
|
|
1340
|
+
// (`{ ...spec.X }`) and `Object.keys(spec.X)` enumerate declared
|
|
1341
|
+
// fields instead of returning an opaque empty object.
|
|
1342
|
+
const schema = createSchemaProxy(definition.spec?.json, definition.status?.json);
|
|
835
1343
|
const builderResult = resourceBuilder(schema);
|
|
836
1344
|
const { resources: resourcesWithKeys, closures } = separateResourcesAndClosures(builderResult);
|
|
837
1345
|
// 3. Analyze status builder and convert JS expressions to CEL
|
|
@@ -856,6 +1364,18 @@ function createTypedResourceGraph(definition, resourceBuilder, statusBuilder, op
|
|
|
856
1364
|
// Evaluate and optimize CEL expressions
|
|
857
1365
|
const evaluationContext = { resources: resourcesWithKeys, schema };
|
|
858
1366
|
const { mappings: optimizedStatusMappings, optimizations } = optimizeStatusMappings(analyzedStatusMappings, evaluationContext);
|
|
1367
|
+
for (const metadataKey of [
|
|
1368
|
+
'__originalCompositionFn',
|
|
1369
|
+
'__nestedCompositionFns',
|
|
1370
|
+
'__nestedCompositionDefinitions',
|
|
1371
|
+
'__nestedCompositionResources',
|
|
1372
|
+
'__nestedCompositionSpecMappings',
|
|
1373
|
+
]) {
|
|
1374
|
+
const descriptor = Object.getOwnPropertyDescriptor(statusMappings, metadataKey);
|
|
1375
|
+
if (descriptor) {
|
|
1376
|
+
Object.defineProperty(optimizedStatusMappings, metadataKey, descriptor);
|
|
1377
|
+
}
|
|
1378
|
+
}
|
|
859
1379
|
if (optimizations.length > 0) {
|
|
860
1380
|
serializationLogger.info('CEL expression optimizations applied', { optimizations });
|
|
861
1381
|
}
|
|
@@ -890,6 +1410,11 @@ function createTypedResourceGraph(definition, resourceBuilder, statusBuilder, op
|
|
|
890
1410
|
statusMappings: directStatusMappings,
|
|
891
1411
|
compositionFn: declarativeCompositionFn,
|
|
892
1412
|
compositionDefinition: definition,
|
|
1413
|
+
...(this._singletonDefinitions
|
|
1414
|
+
? {
|
|
1415
|
+
singletonDefinitions: this._singletonDefinitions,
|
|
1416
|
+
}
|
|
1417
|
+
: {}),
|
|
893
1418
|
});
|
|
894
1419
|
}
|
|
895
1420
|
else if (mode === 'kro') {
|
|
@@ -898,13 +1423,22 @@ function createTypedResourceGraph(definition, resourceBuilder, statusBuilder, op
|
|
|
898
1423
|
closures,
|
|
899
1424
|
factoryType: 'kro',
|
|
900
1425
|
compositionFn: declarativeCompositionFn,
|
|
1426
|
+
compositionAnalysis,
|
|
1427
|
+
...(this._singletonDefinitions
|
|
1428
|
+
? {
|
|
1429
|
+
singletonDefinitions: this._singletonDefinitions,
|
|
1430
|
+
}
|
|
1431
|
+
: {}),
|
|
901
1432
|
});
|
|
902
1433
|
}
|
|
903
1434
|
else {
|
|
904
1435
|
throw new ValidationError(`Unsupported factory mode: ${mode}`, 'ResourceGraphDefinition', definition.name, 'mode', ['Use "kro" or "direct" as the factory mode']);
|
|
905
1436
|
}
|
|
906
1437
|
},
|
|
907
|
-
toYaml() {
|
|
1438
|
+
toYaml(spec) {
|
|
1439
|
+
if (spec !== undefined) {
|
|
1440
|
+
return this.factory('kro').toYaml(spec);
|
|
1441
|
+
}
|
|
908
1442
|
// Apply composition body analysis results (guard: only once)
|
|
909
1443
|
if (compositionAnalysis && !analysisState.appliedToResources) {
|
|
910
1444
|
analysisState.appliedToResources = true;
|
|
@@ -921,10 +1455,13 @@ function createTypedResourceGraph(definition, resourceBuilder, statusBuilder, op
|
|
|
921
1455
|
// Collect nested composition status CEL mappings from the composition context.
|
|
922
1456
|
// These enable inlining the inner composition's real CEL expressions instead
|
|
923
1457
|
// of referencing virtual nested composition IDs.
|
|
924
|
-
// Extract nested composition status CEL mappings attached by
|
|
925
|
-
//
|
|
926
|
-
//
|
|
927
|
-
|
|
1458
|
+
// Extract nested composition status CEL mappings attached by
|
|
1459
|
+
// executeCompositionCore via Reflect.set. Must use
|
|
1460
|
+
// Object.getOwnPropertyDescriptor to bypass the Enhanced proxy's
|
|
1461
|
+
// get handler which would return a KubernetesRef instead of the
|
|
1462
|
+
// actual Record<string, string>.
|
|
1463
|
+
const nestedStatusDescriptor = Object.getOwnPropertyDescriptor(statusMappings, '__nestedStatusCel');
|
|
1464
|
+
const nestedStatusCel = nestedStatusDescriptor?.value ?? {};
|
|
928
1465
|
serializationLogger.debug('Nested status CEL extraction', {
|
|
929
1466
|
hasNestedStatusCel: Object.keys(nestedStatusCel).length > 0,
|
|
930
1467
|
keys: Object.keys(nestedStatusCel),
|
|
@@ -934,6 +1471,17 @@ function createTypedResourceGraph(definition, resourceBuilder, statusBuilder, op
|
|
|
934
1471
|
if (definition.group) {
|
|
935
1472
|
kroSchema.group = definition.group;
|
|
936
1473
|
}
|
|
1474
|
+
// Attach nested status CEL mappings to the schema as a non-enumerable
|
|
1475
|
+
// property (same pattern as __ternaryConditionals, __omitFields).
|
|
1476
|
+
// Non-enumerable so it doesn't appear in the YAML output, but
|
|
1477
|
+
// accessible via KroSimpleSchemaWithMetadata for the YAML serializer
|
|
1478
|
+
// to resolve virtual composition IDs in resource templates.
|
|
1479
|
+
if (Object.keys(nestedStatusCel).length > 0) {
|
|
1480
|
+
Object.defineProperty(kroSchema, '__nestedStatusCel', {
|
|
1481
|
+
value: nestedStatusCel,
|
|
1482
|
+
enumerable: false,
|
|
1483
|
+
});
|
|
1484
|
+
}
|
|
937
1485
|
// Inject status overrides into schema status section.
|
|
938
1486
|
// Convert "..." to '...' in CEL string literals for YAML compatibility.
|
|
939
1487
|
const statusOverrides = compositionAnalysis?.statusOverrides ?? [];
|
|
@@ -955,7 +1503,7 @@ function createTypedResourceGraph(definition, resourceBuilder, statusBuilder, op
|
|
|
955
1503
|
if (!analysisState.ternaryAndOmitApplied) {
|
|
956
1504
|
analysisState.ternaryAndOmitApplied = true;
|
|
957
1505
|
if (kroSchema.__ternaryConditionals?.length) {
|
|
958
|
-
applyTernaryConditionalsToResources(resourcesWithKeys, kroSchema.__ternaryConditionals);
|
|
1506
|
+
applyTernaryConditionalsToResources(resourcesWithKeys, kroSchema.__ternaryConditionals, kroSchema.__nestedStatusCel);
|
|
959
1507
|
}
|
|
960
1508
|
}
|
|
961
1509
|
return serializeResourceGraphToYaml(definition.name, resourcesWithKeys, options, kroSchema);
|