typekro 0.8.0 → 0.10.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/aspects.d.ts +3 -0
- package/dist/aspects.d.ts.map +1 -0
- package/dist/aspects.js +2 -0
- package/dist/aspects.js.map +1 -0
- 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/aspects/apply.d.ts +30 -0
- package/dist/core/aspects/apply.d.ts.map +1 -0
- package/dist/core/aspects/apply.js +369 -0
- package/dist/core/aspects/apply.js.map +1 -0
- package/dist/core/aspects/dev-aspects.d.ts +14 -0
- package/dist/core/aspects/dev-aspects.d.ts.map +1 -0
- package/dist/core/aspects/dev-aspects.js +70 -0
- package/dist/core/aspects/dev-aspects.js.map +1 -0
- package/dist/core/aspects/index.d.ts +31 -0
- package/dist/core/aspects/index.d.ts.map +1 -0
- package/dist/core/aspects/index.js +30 -0
- package/dist/core/aspects/index.js.map +1 -0
- package/dist/core/aspects/metadata-aspects.d.ts +12 -0
- package/dist/core/aspects/metadata-aspects.d.ts.map +1 -0
- package/dist/core/aspects/metadata-aspects.js +24 -0
- package/dist/core/aspects/metadata-aspects.js.map +1 -0
- package/dist/core/aspects/metadata.d.ts +16 -0
- package/dist/core/aspects/metadata.d.ts.map +1 -0
- package/dist/core/aspects/metadata.js +24 -0
- package/dist/core/aspects/metadata.js.map +1 -0
- package/dist/core/aspects/primitives.d.ts +23 -0
- package/dist/core/aspects/primitives.d.ts.map +1 -0
- package/dist/core/aspects/primitives.js +258 -0
- package/dist/core/aspects/primitives.js.map +1 -0
- package/dist/core/aspects/targets.d.ts +9 -0
- package/dist/core/aspects/targets.d.ts.map +1 -0
- package/dist/core/aspects/targets.js +23 -0
- package/dist/core/aspects/targets.js.map +1 -0
- package/dist/core/aspects/types.d.ts +361 -0
- package/dist/core/aspects/types.d.ts.map +1 -0
- package/dist/core/aspects/types.js +16 -0
- package/dist/core/aspects/types.js.map +1 -0
- package/dist/core/aspects/workload-aspects.d.ts +17 -0
- package/dist/core/aspects/workload-aspects.d.ts.map +1 -0
- package/dist/core/aspects/workload-aspects.js +40 -0
- package/dist/core/aspects/workload-aspects.js.map +1 -0
- 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 +584 -62
- 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 +922 -278
- 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 +49 -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 +79 -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 +672 -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 +129 -9
- 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 +2 -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/index.d.ts +3 -3
- package/dist/factories/simple/index.d.ts.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 +4 -1
- package/dist/factories/simple/workloads/deployment.d.ts.map +1 -1
- package/dist/factories/simple/workloads/deployment.js +9 -2
- package/dist/factories/simple/workloads/deployment.js.map +1 -1
- package/dist/factories/simple/workloads/stateful-set.d.ts +4 -1
- package/dist/factories/simple/workloads/stateful-set.d.ts.map +1 -1
- package/dist/factories/simple/workloads/stateful-set.js +6 -2
- package/dist/factories/simple/workloads/stateful-set.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 +5 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -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 +7 -2
|
@@ -9,9 +9,15 @@
|
|
|
9
9
|
* conversion. No other module should duplicate this logic.
|
|
10
10
|
*/
|
|
11
11
|
import { isCelExpression, isKubernetesRef } from '../../utils/type-guards.js';
|
|
12
|
+
import { escapeCelString } from '../../utils/cel-escape.js';
|
|
13
|
+
import { KUBERNETES_REF_MARKER_SOURCE } from '../../shared/brands.js';
|
|
14
|
+
import { remapVariableNames } from '../composition/nested-status-cel.js';
|
|
12
15
|
import { getComponentLogger } from '../logging/index.js';
|
|
13
16
|
import { copyResourceMetadata } from '../metadata/index.js';
|
|
14
17
|
const logger = getComponentLogger('cel-references');
|
|
18
|
+
function escapeRegExpLiteral(value) {
|
|
19
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
20
|
+
}
|
|
15
21
|
// ---------------------------------------------------------------------------
|
|
16
22
|
// Primitive helpers
|
|
17
23
|
// ---------------------------------------------------------------------------
|
|
@@ -28,46 +34,563 @@ export function getInnerCelPath(ref) {
|
|
|
28
34
|
const resourceId = ref.resourceId === '__schema__' ? 'schema' : ref.resourceId;
|
|
29
35
|
return `${resourceId}.${ref.fieldPath}`;
|
|
30
36
|
}
|
|
37
|
+
function resolveResourceIdAlias(resourceId, context) {
|
|
38
|
+
if (resourceId === '__schema__') {
|
|
39
|
+
return 'schema';
|
|
40
|
+
}
|
|
41
|
+
return context?.resourceAliases?.get(resourceId) ?? resourceId;
|
|
42
|
+
}
|
|
31
43
|
// ---------------------------------------------------------------------------
|
|
32
44
|
// Single-ref → CEL
|
|
33
45
|
// ---------------------------------------------------------------------------
|
|
34
46
|
/**
|
|
35
|
-
* If `celPath` is
|
|
36
|
-
*
|
|
37
|
-
* `has(...) ? ... : omit()`. Otherwise
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
47
|
+
* If `celPath` is a `schema.spec.<dotted.path>` reference whose path
|
|
48
|
+
* — or any of its ancestor prefixes — is listed in the context's omit
|
|
49
|
+
* set, wrap it with KRO 0.9+ `has(...) ? ... : omit()`. Otherwise
|
|
50
|
+
* return it unchanged.
|
|
51
|
+
*
|
|
52
|
+
* The wrapper is only applied to single-ref expressions — never to
|
|
53
|
+
* mixed templates like `${string(schema.spec.name)}-suffix` because
|
|
54
|
+
* those produce string values, not fields, and `omit()` operates at
|
|
55
|
+
* the field level.
|
|
56
|
+
*
|
|
57
|
+
* **Ancestor-prefix lookup:** `omitFields` may contain either leaf
|
|
58
|
+
* paths (`database.storageClass`) or parent paths (`env`, `cache`).
|
|
59
|
+
* The walk goes leaf-to-root and guards with `has()` on the *deepest*
|
|
60
|
+
* ancestor that's in the set. This covers three cases in one pass:
|
|
61
|
+
*
|
|
62
|
+
* 1. Exact leaf match — `database.storageClass?` → guard
|
|
63
|
+
* `has(schema.spec.database.storageClass)`.
|
|
64
|
+
* 2. Whole-object optional — `env?: {...}` → any child ref
|
|
65
|
+
* `schema.spec.env.FOO` is guarded by `has(schema.spec.env)`
|
|
66
|
+
* because leaf access throws when the parent is absent.
|
|
67
|
+
* 3. Mixed — both `cache?` (parent) and `cache.replicas?` (child)
|
|
68
|
+
* in the set → a ref to `schema.spec.cache.replicas` prefers
|
|
69
|
+
* the *deeper* `cache.replicas` guard so a partially-populated
|
|
70
|
+
* `cache: { shards: 1 }` (parent present, child absent) still
|
|
71
|
+
* omits correctly.
|
|
45
72
|
*/
|
|
46
73
|
function maybeWrapWithOmit(celPath, stringWrap, omitFields) {
|
|
47
74
|
const value = stringWrap ? `string(${celPath})` : celPath;
|
|
48
75
|
if (!omitFields || omitFields.size === 0)
|
|
49
76
|
return value;
|
|
50
|
-
//
|
|
51
|
-
const
|
|
52
|
-
const
|
|
53
|
-
if (!
|
|
77
|
+
// Only handle refs rooted at `schema.spec.`.
|
|
78
|
+
const refMatch = /^schema\.spec\.([A-Za-z_$][\w$.]*)$/.exec(celPath);
|
|
79
|
+
const refPath = refMatch?.[1];
|
|
80
|
+
if (!refPath)
|
|
54
81
|
return value;
|
|
55
|
-
|
|
82
|
+
// Walk the full path and collect every optional prefix that applies.
|
|
83
|
+
// We chain from the SHALLOWEST optional ancestor through the leaf.
|
|
84
|
+
//
|
|
85
|
+
// Why shallowest? If both a parent object and a leaf field are optional
|
|
86
|
+
// (e.g. `cnpgOperator?` and `cnpgOperator.monitoring.enabled?`), guarding
|
|
87
|
+
// only the deepest leaf with `has(schema.spec.cnpgOperator.monitoring.enabled)`
|
|
88
|
+
// is not sufficient when the parent object is absent. KRO still evaluates
|
|
89
|
+
// the full path through its optional ancestors. Chaining every level from
|
|
90
|
+
// the first optional ancestor keeps both cases safe:
|
|
91
|
+
// - ancestor optional, leaf required
|
|
92
|
+
// - ancestor optional, leaf optional
|
|
93
|
+
// - multiple optional intermediates
|
|
94
|
+
const segments = refPath.split('.');
|
|
95
|
+
let shallowestOptionalIndex = null;
|
|
96
|
+
for (let i = 1; i <= segments.length; i++) {
|
|
97
|
+
const prefix = segments.slice(0, i).join('.');
|
|
98
|
+
if (omitFields.has(prefix)) {
|
|
99
|
+
shallowestOptionalIndex ??= i;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
if (shallowestOptionalIndex !== null) {
|
|
103
|
+
const guards = [];
|
|
104
|
+
for (let i = shallowestOptionalIndex; i <= segments.length; i++) {
|
|
105
|
+
const guardPath = `schema.spec.${segments.slice(0, i).join('.')}`;
|
|
106
|
+
guards.push(`has(${guardPath})`);
|
|
107
|
+
}
|
|
108
|
+
return `${guards.join(' && ')} ? ${value} : omit()`;
|
|
109
|
+
}
|
|
110
|
+
return value;
|
|
56
111
|
}
|
|
57
112
|
/**
|
|
58
113
|
* Wrap a {@link KubernetesRef} in `${…}` for Kro YAML output.
|
|
114
|
+
*
|
|
115
|
+
* When the ref points to a nested composition's virtual status ID
|
|
116
|
+
* (e.g., `innerService1.status.serviceUrl`), resolve it via the
|
|
117
|
+
* transitive resolver — substituting the inner composition's analyzed
|
|
118
|
+
* expression and producing valid KRO CEL (no raw markers, no virtual
|
|
119
|
+
* IDs).
|
|
120
|
+
*
|
|
121
|
+
* The nested-composition lookup delegates to {@link lookupNestedExpression}
|
|
122
|
+
* — the single source of truth for the match strategy. See that function
|
|
123
|
+
* for the full priority order.
|
|
59
124
|
*/
|
|
60
125
|
function generateCelExpression(ref, context) {
|
|
61
|
-
const
|
|
126
|
+
const isNestedComp = ref.__nestedComposition === true;
|
|
127
|
+
if (isNestedComp && context?.nestedStatusCel) {
|
|
128
|
+
const fieldName = ref.fieldPath.replace(/^status\./, '');
|
|
129
|
+
const innerExpr = context.resourceIds?.has(ref.resourceId)
|
|
130
|
+
? lookupNestedExpression(ref.resourceId, fieldName, context.nestedStatusCel, false)
|
|
131
|
+
: lookupNestedExpression(ref.resourceId, fieldName, context.nestedStatusCel);
|
|
132
|
+
if (innerExpr !== undefined) {
|
|
133
|
+
return finalizeCelForKro(innerExpr, context.nestedStatusCel, context);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
const expression = `${resolveResourceIdAlias(ref.resourceId, context)}.${ref.fieldPath}`;
|
|
62
137
|
const body = maybeWrapWithOmit(expression, false, context?.omitFields);
|
|
63
138
|
return `\${${body}}`;
|
|
64
139
|
}
|
|
65
140
|
// ---------------------------------------------------------------------------
|
|
66
|
-
// __KUBERNETES_REF__ marker
|
|
141
|
+
// __KUBERNETES_REF__ marker primitives
|
|
142
|
+
// ---------------------------------------------------------------------------
|
|
143
|
+
/**
|
|
144
|
+
* Single source of truth for __KUBERNETES_REF__ marker detection.
|
|
145
|
+
*
|
|
146
|
+
* Marker shape:
|
|
147
|
+
* - Resource ref: `__KUBERNETES_REF_<resourceId>_<fieldPath>__`
|
|
148
|
+
* - Schema ref: `__KUBERNETES_REF___schema___<fieldPath>__`
|
|
149
|
+
*
|
|
150
|
+
* `resourceId` is `__schema__` for schema refs, or a marker-safe resource id
|
|
151
|
+
* with optional single `_` segments. `fieldPath` is a dot-separated identifier
|
|
152
|
+
* sequence like "spec.name", "status.image_tag", or
|
|
153
|
+
* "status.workers.$item.name".
|
|
154
|
+
*
|
|
155
|
+
* Two flavors are needed:
|
|
156
|
+
* - `MARKER_PATTERN_SOURCE` is the non-global base pattern. Create a fresh
|
|
157
|
+
* `new RegExp(MARKER_PATTERN_SOURCE, 'g')` wherever global matching is
|
|
158
|
+
* needed — avoids the stateful-lastIndex footgun.
|
|
159
|
+
* - `MARKER_PATTERN_FULL` matches when the entire string is exactly one
|
|
160
|
+
* marker (for the single-ref fast path in
|
|
161
|
+
* {@link convertKubernetesRefMarkersTocel}).
|
|
162
|
+
*/
|
|
163
|
+
/**
|
|
164
|
+
* Non-global base pattern for __KUBERNETES_REF__ markers. Create a fresh
|
|
165
|
+
* `RegExp(MARKER_PATTERN_SOURCE, 'g')` wherever global matching is needed —
|
|
166
|
+
* avoids the stateful-lastIndex footgun of a module-level `/g` regex.
|
|
167
|
+
*/
|
|
168
|
+
const MARKER_PATTERN_SOURCE = KUBERNETES_REF_MARKER_SOURCE;
|
|
169
|
+
const MARKER_PATTERN_FULL = new RegExp(`^${MARKER_PATTERN_SOURCE}$`);
|
|
170
|
+
/**
|
|
171
|
+
* Convert a marker substring captured from {@link MARKER_PATTERN_G} (or
|
|
172
|
+
* {@link MARKER_PATTERN_FULL}) to its bare CEL path. Handles the
|
|
173
|
+
* `__schema__` sentinel by emitting `schema.<fieldPath>`; otherwise
|
|
174
|
+
* emits `<resourceId>.<fieldPath>`.
|
|
175
|
+
*/
|
|
176
|
+
function markerToCelPath(resourceId, fieldPath, context) {
|
|
177
|
+
return `${resolveResourceIdAlias(resourceId, context)}.${fieldPath}`;
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Normalize any `__KUBERNETES_REF__` markers in `str` to their bare CEL paths
|
|
181
|
+
* in-place — without wrapping them in `${…}`. Used by the static/dynamic
|
|
182
|
+
* classifier and the transitive resolver when we need to scan for non-
|
|
183
|
+
* schema references inside a marker-laden string.
|
|
184
|
+
*
|
|
185
|
+
* Unlike {@link convertKubernetesRefMarkersTocel}, this does NOT produce a
|
|
186
|
+
* KRO mixed-template string. It produces a raw CEL-path blend where
|
|
187
|
+
* markers have been replaced by their dotted CEL paths.
|
|
188
|
+
*
|
|
189
|
+
* Example:
|
|
190
|
+
* "http://__KUBERNETES_REF___schema___spec.name__:__KUBERNETES_REF___schema___spec.port__"
|
|
191
|
+
* →
|
|
192
|
+
* "http://schema.spec.name:schema.spec.port"
|
|
193
|
+
*
|
|
194
|
+
* The result isn't valid CEL on its own — it's literal text interleaved
|
|
195
|
+
* with CEL paths, suitable for pattern matching only.
|
|
196
|
+
*/
|
|
197
|
+
function normalizeMarkerString(str, context) {
|
|
198
|
+
return str.replace(new RegExp(MARKER_PATTERN_SOURCE, 'g'), (_match, id, path) => markerToCelPath(id, path, context));
|
|
199
|
+
}
|
|
200
|
+
/** Convert marker strings to bare CEL paths using the full serialization context. */
|
|
201
|
+
export function normalizeRefMarkersToCelPaths(str, context) {
|
|
202
|
+
const withNestedMarkers = resolveNestedRefMarkers(str, context?.nestedStatusCel, context?.resourceIds, context);
|
|
203
|
+
const withNestedStatus = resolveNestedCompositionRefs(withNestedMarkers, context?.nestedStatusCel, context?.resourceIds);
|
|
204
|
+
return normalizeMarkerString(withNestedStatus, context);
|
|
205
|
+
}
|
|
206
|
+
// ---------------------------------------------------------------------------
|
|
207
|
+
// CEL lambda variable handling
|
|
208
|
+
// ---------------------------------------------------------------------------
|
|
209
|
+
/**
|
|
210
|
+
* Pattern to extract lambda variable names from CEL macro calls.
|
|
211
|
+
*
|
|
212
|
+
* CEL macros (`.all`, `.exists`, `.exists_one`, `.map`, `.filter`)
|
|
213
|
+
* introduce a local variable in their first argument that should NOT
|
|
214
|
+
* be treated as a resource identifier when scanning for `<id>.status.X`
|
|
215
|
+
* patterns. The classification regex is shared with `cel-validator.ts`
|
|
216
|
+
* — keep them in sync if you change one.
|
|
217
|
+
*
|
|
218
|
+
* Note: `each` is also a special identifier — it's the implicit element
|
|
219
|
+
* variable used by KRO `forEach`/`readyWhen` callback bodies.
|
|
220
|
+
*/
|
|
221
|
+
const CEL_LAMBDA_MACRO_PATTERN = /\.(?:all|exists|exists_one|map|filter)\(\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*,/g;
|
|
222
|
+
/**
|
|
223
|
+
* Build the set of identifiers that should be treated as lambda-bound
|
|
224
|
+
* variables when scanning `expr` for resource references. Always includes
|
|
225
|
+
* `each` as a sentinel for `forEach.readyWhen` bodies.
|
|
226
|
+
*/
|
|
227
|
+
function collectLambdaVars(expr) {
|
|
228
|
+
const vars = new Set(['each']);
|
|
229
|
+
for (const m of expr.matchAll(CEL_LAMBDA_MACRO_PATTERN)) {
|
|
230
|
+
if (m[1])
|
|
231
|
+
vars.add(m[1]);
|
|
232
|
+
}
|
|
233
|
+
return vars;
|
|
234
|
+
}
|
|
235
|
+
// ---------------------------------------------------------------------------
|
|
236
|
+
// Static / dynamic classification
|
|
237
|
+
// ---------------------------------------------------------------------------
|
|
238
|
+
/**
|
|
239
|
+
* Check whether an already-resolved CEL-path string contains any
|
|
240
|
+
* non-schema resource references. "Non-schema resource reference" means
|
|
241
|
+
* any `<identifier>.(status|metadata|spec).<path>` where `<identifier>`
|
|
242
|
+
* is neither `schema` nor a CEL macro lambda variable.
|
|
243
|
+
*
|
|
244
|
+
* Lambda variables (the `c` in `.exists(c, c.status == "True")`) are
|
|
245
|
+
* detected via {@link collectLambdaVars} and excluded — otherwise a
|
|
246
|
+
* legitimate macro body would falsely classify the parent expression
|
|
247
|
+
* as dynamic for the wrong reason.
|
|
248
|
+
*/
|
|
249
|
+
function containsNoNonSchemaRefs(expr) {
|
|
250
|
+
const lambdaVars = collectLambdaVars(expr);
|
|
251
|
+
const pattern = /\b([a-zA-Z_$][\w$]*)\.(status|metadata|spec)\./g;
|
|
252
|
+
for (const m of expr.matchAll(pattern)) {
|
|
253
|
+
const id = m[1];
|
|
254
|
+
if (id === 'schema')
|
|
255
|
+
continue;
|
|
256
|
+
if (id && lambdaVars.has(id))
|
|
257
|
+
continue;
|
|
258
|
+
return false;
|
|
259
|
+
}
|
|
260
|
+
return true;
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Classify an expression as static (iff, after resolving all nested-
|
|
264
|
+
* composition references and schema markers, it contains no references
|
|
265
|
+
* to real-resource `status`/`metadata`/`spec` fields) or dynamic.
|
|
266
|
+
*
|
|
267
|
+
* This is the authoritative depth-agnostic static/dynamic classifier.
|
|
268
|
+
* It supersedes the local syntactic check in `validation/cel-validator.ts`
|
|
269
|
+
* for nested-composition references.
|
|
270
|
+
*
|
|
271
|
+
* Input shapes accepted:
|
|
272
|
+
* - CEL expression strings (e.g., `"app.status.readyReplicas >= 1"`)
|
|
273
|
+
* - Marker strings (e.g., `"http://__KUBERNETES_REF___schema___spec.name__"`)
|
|
274
|
+
* - Mixed: CEL paths + marker tokens + literals
|
|
275
|
+
*
|
|
276
|
+
* Returns `true` iff the fully-resolved expression is purely
|
|
277
|
+
* schema-and-literal.
|
|
278
|
+
*/
|
|
279
|
+
export function isStaticExpression(expr, nestedStatusCel) {
|
|
280
|
+
const afterNesting = resolveNestedCompositionRefs(expr, nestedStatusCel);
|
|
281
|
+
const afterMarkers = normalizeMarkerString(afterNesting);
|
|
282
|
+
return containsNoNonSchemaRefs(afterMarkers);
|
|
283
|
+
}
|
|
284
|
+
// ---------------------------------------------------------------------------
|
|
285
|
+
// Nested composition reference resolution
|
|
286
|
+
// ---------------------------------------------------------------------------
|
|
287
|
+
/**
|
|
288
|
+
* Maximum number of substitution passes when resolving nested composition
|
|
289
|
+
* references in {@link resolveNestedCompositionRefs}. The fixed-point loop
|
|
290
|
+
* normally converges in one or two passes (one per level of nesting), so
|
|
291
|
+
* 16 is a comfortable cap that handles pathologically deep compositions
|
|
292
|
+
* without giving runaway substitution loops a chance to wedge serialization.
|
|
293
|
+
*
|
|
294
|
+
* Hitting this limit indicates a real bug in the resolution table — most
|
|
295
|
+
* likely a cycle introduced by a faulty alias entry — not a legitimate
|
|
296
|
+
* composition shape.
|
|
297
|
+
*/
|
|
298
|
+
const NESTED_REF_RESOLUTION_DEPTH_LIMIT = 16;
|
|
299
|
+
/**
|
|
300
|
+
* Look up a nested composition's analyzed expression by `(resourceId, fieldName)`.
|
|
301
|
+
*
|
|
302
|
+
* **Single source of truth** for the nested-status match strategy. Used by
|
|
303
|
+
* every code path that needs to find an inner expression from a
|
|
304
|
+
* `nestedStatusCel` table — both the structured-ref paths
|
|
305
|
+
* (`generateCelExpression`, `serializeStatusMappingsToCel`) and the
|
|
306
|
+
* string-resolver path (`substituteNestedRefsOnce`,
|
|
307
|
+
* `resolveNestedRefMarkers`).
|
|
308
|
+
*
|
|
309
|
+
* Returns `undefined` when no match is found, leaving the caller to
|
|
310
|
+
* decide how to handle missing entries (typically: leave the reference
|
|
311
|
+
* in place and let downstream validation flag it).
|
|
312
|
+
*
|
|
313
|
+
* Match priority:
|
|
314
|
+
* 1. **Exact baseId match.** The reference uses the virtual nested
|
|
315
|
+
* composition baseId verbatim (e.g., from a template literal on a
|
|
316
|
+
* nested status proxy where `toString()` embeds the baseId).
|
|
317
|
+
* 2. **Base-name match (instance digits stripped).** Both the requested
|
|
318
|
+
* id and the candidate baseIds have their trailing instance digits
|
|
319
|
+
* stripped, then compared for equality. Handles the case where the
|
|
320
|
+
* reference uses `webAppWithProcessing2` but the table has
|
|
321
|
+
* `webAppWithProcessing1`.
|
|
322
|
+
* 3. **Unambiguous camelCase / case-insensitive prefix.** The
|
|
323
|
+
* reference uses a name that structurally relates to one (and only
|
|
324
|
+
* one) baseId stem (e.g., `inngest` matching `inngestBootstrap`).
|
|
325
|
+
* 4. **Field-name uniqueness.** When exactly one nested composition in
|
|
326
|
+
* the table provides the requested field, use it. Handles the case
|
|
327
|
+
* of arbitrary local variable names (e.g.,
|
|
328
|
+
* `const stack = webAppWithProcessing(...); stack.status.databaseUrl`).
|
|
329
|
+
*
|
|
330
|
+
* Ambiguous matches (multiple candidates from the prefix or field-name
|
|
331
|
+
* strategies) emit a warning log and return `undefined` so the caller
|
|
332
|
+
* can fall through to its own error handling.
|
|
333
|
+
*/
|
|
334
|
+
export function lookupNestedExpression(resourceId, fieldName, nestedStatusCel, allowFieldFallback = true) {
|
|
335
|
+
// Strategy 1: exact match.
|
|
336
|
+
const exactKey = `__nestedStatus:${resourceId}:${fieldName}`;
|
|
337
|
+
if (Object.hasOwn(nestedStatusCel, exactKey)) {
|
|
338
|
+
return nestedStatusCel[exactKey];
|
|
339
|
+
}
|
|
340
|
+
// Gather all entries for the requested field name once — strategies
|
|
341
|
+
// 2-4 all need this list.
|
|
342
|
+
const fieldSuffix = `:${fieldName}`;
|
|
343
|
+
const fieldMatches = [];
|
|
344
|
+
for (const key of Object.keys(nestedStatusCel)) {
|
|
345
|
+
if (!key.endsWith(fieldSuffix))
|
|
346
|
+
continue;
|
|
347
|
+
const parts = key.split(':');
|
|
348
|
+
if (parts.length === 3 && parts[1]) {
|
|
349
|
+
fieldMatches.push({ key, baseId: parts[1] });
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
if (fieldMatches.length === 0)
|
|
353
|
+
return undefined;
|
|
354
|
+
// Strategy 2: base-name match (instance digits stripped).
|
|
355
|
+
const refBase = resourceId.replace(/\d+$/, '');
|
|
356
|
+
const baseNameMatches = fieldMatches.filter((m) => m.baseId.replace(/\d+$/, '') === refBase);
|
|
357
|
+
if (baseNameMatches.length === 1) {
|
|
358
|
+
const match = baseNameMatches[0];
|
|
359
|
+
return match ? nestedStatusCel[match.key] : undefined;
|
|
360
|
+
}
|
|
361
|
+
// Strategy 3: unambiguous camelCase / case-insensitive prefix.
|
|
362
|
+
const prefixMatches = fieldMatches.filter((m) => {
|
|
363
|
+
const baseStem = m.baseId.replace(/\d+$/, '');
|
|
364
|
+
const resourceLooksLikeMergedChild = (resourceId.startsWith(baseStem) &&
|
|
365
|
+
resourceId.length > baseStem.length &&
|
|
366
|
+
(() => {
|
|
367
|
+
const boundaryChar = resourceId[baseStem.length];
|
|
368
|
+
return boundaryChar !== undefined && /[A-Z_-]/.test(boundaryChar);
|
|
369
|
+
})()) ||
|
|
370
|
+
new RegExp(`^${baseStem}\\d+[A-Z_-]`).test(resourceId);
|
|
371
|
+
if (resourceLooksLikeMergedChild) {
|
|
372
|
+
return false;
|
|
373
|
+
}
|
|
374
|
+
return (isCamelCasePrefix(resourceId, baseStem) ||
|
|
375
|
+
isCamelCasePrefix(baseStem, resourceId) ||
|
|
376
|
+
baseStem.toLowerCase() === resourceId.toLowerCase());
|
|
377
|
+
});
|
|
378
|
+
if (prefixMatches.length === 1) {
|
|
379
|
+
const match = prefixMatches[0];
|
|
380
|
+
return match ? nestedStatusCel[match.key] : undefined;
|
|
381
|
+
}
|
|
382
|
+
if (prefixMatches.length > 1) {
|
|
383
|
+
logger.warn('Ambiguous nested composition prefix match', {
|
|
384
|
+
resourceId,
|
|
385
|
+
fieldName,
|
|
386
|
+
candidates: prefixMatches.map((m) => m.baseId),
|
|
387
|
+
});
|
|
388
|
+
return undefined;
|
|
389
|
+
}
|
|
390
|
+
// Strategy 4: field-name uniqueness.
|
|
391
|
+
if (!allowFieldFallback)
|
|
392
|
+
return undefined;
|
|
393
|
+
if (fieldMatches.length === 1) {
|
|
394
|
+
const match = fieldMatches[0];
|
|
395
|
+
return match ? nestedStatusCel[match.key] : undefined;
|
|
396
|
+
}
|
|
397
|
+
// Field-only fallback is intentionally best-effort and fully silent on
|
|
398
|
+
// ambiguity. Prefix/base-name ambiguity still logs above, but this final
|
|
399
|
+
// branch should never emit low-signal warnings for common status fields
|
|
400
|
+
// like `ready` that appear across many unrelated nested compositions.
|
|
401
|
+
return undefined;
|
|
402
|
+
}
|
|
403
|
+
/**
|
|
404
|
+
* Transitively substitute nested-composition references inline from a
|
|
405
|
+
* lookup table.
|
|
406
|
+
*
|
|
407
|
+
* Searches `expr` for `<id>.status.<fieldPath>` patterns and, when `<id>`
|
|
408
|
+
* resolves to a nested composition entry via {@link lookupNestedExpression},
|
|
409
|
+
* replaces the match with the inner composition's analyzed expression
|
|
410
|
+
* wrapped in parentheses (to preserve operator precedence in compound
|
|
411
|
+
* expressions).
|
|
412
|
+
*
|
|
413
|
+
* **Does NOT touch `__KUBERNETES_REF__` markers.** Callers that need to
|
|
414
|
+
* emit final KRO CEL should run {@link convertKubernetesRefMarkersTocel}
|
|
415
|
+
* on the result. Callers that need to classify the result as
|
|
416
|
+
* static/dynamic should run {@link normalizeMarkerString} then
|
|
417
|
+
* {@link containsNoNonSchemaRefs} (or just call
|
|
418
|
+
* {@link isStaticExpression} which composes both steps).
|
|
419
|
+
*
|
|
420
|
+
* Iterates to a fixed point up to {@link NESTED_REF_RESOLUTION_DEPTH_LIMIT}
|
|
421
|
+
* — substituted expressions may themselves contain nested references that
|
|
422
|
+
* become resolvable once the outer reference is inlined (the three-level
|
|
423
|
+
* nesting case: L1 → L2 → L3).
|
|
424
|
+
*
|
|
425
|
+
* **Lambda variables are skipped.** When the resolved `<id>` is a CEL
|
|
426
|
+
* macro lambda variable like the `c` in `.exists(c, c.status == "Ready")`,
|
|
427
|
+
* the substitution does NOT fire — the variable refers to the macro's
|
|
428
|
+
* iteration element, not a nested composition.
|
|
429
|
+
*/
|
|
430
|
+
function resolveNestedCompositionRefs(expr, nestedStatusCel, resourceIds) {
|
|
431
|
+
if (!nestedStatusCel || Object.keys(nestedStatusCel).length === 0) {
|
|
432
|
+
return expr;
|
|
433
|
+
}
|
|
434
|
+
let current = expr;
|
|
435
|
+
for (let i = 0; i < NESTED_REF_RESOLUTION_DEPTH_LIMIT; i++) {
|
|
436
|
+
const next = substituteNestedRefsOnce(current, nestedStatusCel, resourceIds);
|
|
437
|
+
if (next === current)
|
|
438
|
+
return current;
|
|
439
|
+
current = next;
|
|
440
|
+
}
|
|
441
|
+
logger.warn('Nested composition resolution depth limit exceeded', {
|
|
442
|
+
depthLimit: NESTED_REF_RESOLUTION_DEPTH_LIMIT,
|
|
443
|
+
expressionPreview: expr.slice(0, 200),
|
|
444
|
+
});
|
|
445
|
+
return current;
|
|
446
|
+
}
|
|
447
|
+
export function inlineNestedStatusRefs(expr, nestedStatusCel, resourceIds) {
|
|
448
|
+
return resolveNestedCompositionRefs(expr, nestedStatusCel, resourceIds);
|
|
449
|
+
}
|
|
450
|
+
/**
|
|
451
|
+
* One pass of nested-reference substitution. See
|
|
452
|
+
* {@link resolveNestedCompositionRefs} for the full contract.
|
|
453
|
+
*/
|
|
454
|
+
function substituteNestedRefsOnce(expr, nestedStatusCel, resourceIds) {
|
|
455
|
+
const lambdaVars = collectLambdaVars(expr);
|
|
456
|
+
// Match `<id>.status.<fieldPath>`. The fieldPath capture is greedy on
|
|
457
|
+
// dots so paths like `components.app` are captured whole — that's the
|
|
458
|
+
// form `nestedStatusCel` keys use after recursive extraction.
|
|
459
|
+
const pattern = /\b([a-zA-Z_$][\w$]*)\.status\.([a-zA-Z_$][\w$.]*)/g;
|
|
460
|
+
return expr.replace(pattern, (match, id, field) => {
|
|
461
|
+
if (id === 'schema')
|
|
462
|
+
return match;
|
|
463
|
+
if (lambdaVars.has(id))
|
|
464
|
+
return match;
|
|
465
|
+
if (resourceIds?.has(id)) {
|
|
466
|
+
const strictInnerExpr = lookupNestedExpression(id, field, nestedStatusCel, false);
|
|
467
|
+
if (strictInnerExpr !== undefined)
|
|
468
|
+
return `(${strictInnerExpr})`;
|
|
469
|
+
return match;
|
|
470
|
+
}
|
|
471
|
+
const innerExpr = lookupNestedExpression(id, field, nestedStatusCel);
|
|
472
|
+
if (innerExpr !== undefined)
|
|
473
|
+
return `(${innerExpr})`;
|
|
474
|
+
return match;
|
|
475
|
+
});
|
|
476
|
+
}
|
|
477
|
+
/**
|
|
478
|
+
* Resolve `__KUBERNETES_REF__` markers whose resourceId is a virtual
|
|
479
|
+
* nested composition baseId (rather than a real resource). Each such
|
|
480
|
+
* marker is replaced inline with the inner composition's analyzed
|
|
481
|
+
* expression from `nestedStatusCel`.
|
|
482
|
+
*
|
|
483
|
+
* The substitution rewrites the marker into a KRO-formatted segment via
|
|
484
|
+
* {@link innerExprToYamlSegment}. Markers whose resourceId IS a real
|
|
485
|
+
* resource ID (or `__schema__`) are left in place — they're handled by
|
|
486
|
+
* downstream {@link convertKubernetesRefMarkersTocel}.
|
|
487
|
+
*
|
|
488
|
+
* Lookup uses the shared {@link lookupNestedExpression} so the match
|
|
489
|
+
* strategy stays consistent with the rest of the resolver.
|
|
490
|
+
*/
|
|
491
|
+
function resolveNestedRefMarkers(str, nestedStatusCel, resourceIds, context) {
|
|
492
|
+
if (!nestedStatusCel || Object.keys(nestedStatusCel).length === 0) {
|
|
493
|
+
return str;
|
|
494
|
+
}
|
|
495
|
+
return str.replace(new RegExp(MARKER_PATTERN_SOURCE, 'g'), (match, id, path) => {
|
|
496
|
+
if (id === '__schema__')
|
|
497
|
+
return match;
|
|
498
|
+
// Strip leading "status." since nestedStatusCel keys use the bare field path.
|
|
499
|
+
const fieldPath = path.replace(/^status\./, '');
|
|
500
|
+
if (resourceIds?.has(id)) {
|
|
501
|
+
const strictInnerExpr = lookupNestedExpression(id, fieldPath, nestedStatusCel, false);
|
|
502
|
+
if (strictInnerExpr !== undefined)
|
|
503
|
+
return innerExprToYamlSegment(strictInnerExpr, nestedStatusCel, context);
|
|
504
|
+
return match;
|
|
505
|
+
}
|
|
506
|
+
const innerExpr = lookupNestedExpression(id, fieldPath, nestedStatusCel);
|
|
507
|
+
if (innerExpr !== undefined)
|
|
508
|
+
return innerExprToYamlSegment(innerExpr, nestedStatusCel, context);
|
|
509
|
+
return match;
|
|
510
|
+
});
|
|
511
|
+
}
|
|
512
|
+
/**
|
|
513
|
+
* Matches a bare CEL literal — an integer, float, boolean, or null.
|
|
514
|
+
* Used by {@link innerExprToYamlSegment} to detect when a resolved
|
|
515
|
+
* nested-composition value should be wrapped in `string(...)` so
|
|
516
|
+
* template-literal-originated references produce string values instead
|
|
517
|
+
* of the literal's natural CEL type.
|
|
518
|
+
*/
|
|
519
|
+
const BARE_LITERAL_PATTERN = /^\s*(-?\d+(?:\.\d+)?|true|false|null)\s*$/;
|
|
520
|
+
/**
|
|
521
|
+
* Convert an inner composition's analyzed expression into a YAML-embeddable
|
|
522
|
+
* segment. Recursively resolves nested references within the inner expression
|
|
523
|
+
* and produces the appropriate KRO format.
|
|
524
|
+
*
|
|
525
|
+
* **Called from the marker-substitution path** (`resolveNestedRefMarkers`),
|
|
526
|
+
* which runs when the user wrote a template literal that coerced a nested
|
|
527
|
+
* composition proxy into a marker string (e.g., `` `${stack.status.cachePort}` ``).
|
|
528
|
+
* The template literal is the user's explicit signal that they want a
|
|
529
|
+
* STRING value — so when the resolved expression is a bare literal
|
|
530
|
+
* (number, boolean, null), we wrap it with `string(...)` so KRO's CEL
|
|
531
|
+
* evaluation produces a string matching the user's intent.
|
|
532
|
+
*
|
|
533
|
+
* The direct-ref path ({@link generateCelExpression} → {@link finalizeCelForKro})
|
|
534
|
+
* deliberately does NOT apply this wrapping — a direct assignment like
|
|
535
|
+
* `replicas: stack.status.someCount` should preserve the natural numeric
|
|
536
|
+
* type because the destination field expects a number.
|
|
537
|
+
*/
|
|
538
|
+
function innerExprToYamlSegment(innerExpr, nestedStatusCel, context) {
|
|
539
|
+
// Recursively resolve any further nested refs the inner expression itself
|
|
540
|
+
// contains (multi-level nesting).
|
|
541
|
+
const resolved = resolveNestedCompositionRefs(innerExpr, nestedStatusCel, context?.resourceIds);
|
|
542
|
+
if (resolved.includes('__KUBERNETES_REF_')) {
|
|
543
|
+
// Marker-laden — convert to mixed-template form.
|
|
544
|
+
return convertKubernetesRefMarkersTocel(resolved, context);
|
|
545
|
+
}
|
|
546
|
+
if (resolved.includes('${')) {
|
|
547
|
+
// Already a KRO template (from Cel.template) — pass through.
|
|
548
|
+
return resolved;
|
|
549
|
+
}
|
|
550
|
+
// Bare literal reached via a template-literal coercion — wrap with
|
|
551
|
+
// `string(...)` so KRO evaluates it to a string value. This matches
|
|
552
|
+
// direct-mode JS semantics (`${6379}` stringifies to `"6379"`) and
|
|
553
|
+
// prevents type-mismatch errors when the literal is used in a
|
|
554
|
+
// string-typed context like env var values.
|
|
555
|
+
if (BARE_LITERAL_PATTERN.test(resolved)) {
|
|
556
|
+
return `\${string(${resolved.trim()})}`;
|
|
557
|
+
}
|
|
558
|
+
// Plain CEL — wrap in ${...}.
|
|
559
|
+
return `\${${resolved}}`;
|
|
560
|
+
}
|
|
561
|
+
/**
|
|
562
|
+
* Produce the final KRO CEL form for a nested-composition value, with all
|
|
563
|
+
* nested references substituted in and all schema markers converted to
|
|
564
|
+
* mixed-template form.
|
|
565
|
+
*
|
|
566
|
+
* The return value is always a valid KRO expression string ready to be
|
|
567
|
+
* embedded directly into a YAML value position (resource template field,
|
|
568
|
+
* status CEL expression). Three cases are handled:
|
|
569
|
+
*
|
|
570
|
+
* - Marker-laden input (from a template literal) → converted to KRO
|
|
571
|
+
* mixed-template form via {@link convertKubernetesRefMarkersTocel}.
|
|
572
|
+
* - Input that already contains `${…}` segments (from a `Cel.template()`
|
|
573
|
+
* call, for example) → returned as-is; assumed to be in KRO form.
|
|
574
|
+
* - Plain-CEL input (raw CEL with no markers or `${…}`) → wrapped in
|
|
575
|
+
* `${…}` so KRO recognizes it as an expression to evaluate.
|
|
576
|
+
*/
|
|
577
|
+
export function finalizeCelForKro(expr, nestedStatusCel, context) {
|
|
578
|
+
const resolved = resolveNestedCompositionRefs(expr, nestedStatusCel, context?.resourceIds);
|
|
579
|
+
if (resolved.includes('__KUBERNETES_REF_')) {
|
|
580
|
+
return convertKubernetesRefMarkersTocel(resolved, context);
|
|
581
|
+
}
|
|
582
|
+
if (resolved.includes('${')) {
|
|
583
|
+
// Already contains KRO template placeholders — pass through.
|
|
584
|
+
return resolved;
|
|
585
|
+
}
|
|
586
|
+
return `\${${resolved}}`;
|
|
587
|
+
}
|
|
588
|
+
// ---------------------------------------------------------------------------
|
|
589
|
+
// __KUBERNETES_REF__ marker → KRO CEL conversion
|
|
67
590
|
// ---------------------------------------------------------------------------
|
|
68
591
|
/**
|
|
69
592
|
* Convert `__KUBERNETES_REF__` markers embedded in a string to CEL
|
|
70
|
-
* expressions.
|
|
593
|
+
* expressions in KRO mixed-template form.
|
|
71
594
|
*
|
|
72
595
|
* These markers are created when schema proxy values are used in
|
|
73
596
|
* template literals, e.g.:
|
|
@@ -75,19 +598,21 @@ function generateCelExpression(ref, context) {
|
|
|
75
598
|
* - `__KUBERNETES_REF___schema___spec.name__-policy`
|
|
76
599
|
* → `${string(schema.spec.name)}-policy`
|
|
77
600
|
*
|
|
78
|
-
*
|
|
79
|
-
*
|
|
601
|
+
* Single-reference inputs use a fast path that avoids the `string(…)`
|
|
602
|
+
* wrapper and supports `has()/omit()` for optional schema fields. Mixed
|
|
603
|
+
* inputs always wrap each reference in `${string(…)}` so KRO accepts
|
|
604
|
+
* non-string types in string contexts (ConfigMap data values, env var
|
|
605
|
+
* values).
|
|
80
606
|
*/
|
|
81
607
|
function convertKubernetesRefMarkersTocel(str, context) {
|
|
82
|
-
//
|
|
83
|
-
|
|
84
|
-
// like "spec.name", "status.readyReplicas", or "spec.workers.$item.name"
|
|
85
|
-
const refPattern = /__KUBERNETES_REF_(__schema__|[^_]+)_([a-zA-Z0-9.$]+)__/g;
|
|
86
|
-
// Fast-path: entire string is a single reference
|
|
87
|
-
const singleRefMatch = str.match(/^__KUBERNETES_REF_(__schema__|[^_]+)_([a-zA-Z0-9.$]+)__$/);
|
|
608
|
+
// Fast-path: entire string is a single reference.
|
|
609
|
+
const singleRefMatch = MARKER_PATTERN_FULL.exec(str);
|
|
88
610
|
if (singleRefMatch) {
|
|
89
611
|
const [, resourceId, fieldPath] = singleRefMatch;
|
|
90
|
-
|
|
612
|
+
if (!resourceId || !fieldPath) {
|
|
613
|
+
return str;
|
|
614
|
+
}
|
|
615
|
+
const celPath = markerToCelPath(resourceId, fieldPath, context);
|
|
91
616
|
// Single-ref: safe to wrap with has()/omit() for optional schema fields.
|
|
92
617
|
const body = maybeWrapWithOmit(celPath, false, context?.omitFields);
|
|
93
618
|
return `\${${body}}`;
|
|
@@ -102,18 +627,20 @@ function convertKubernetesRefMarkersTocel(str, context) {
|
|
|
102
627
|
// and mixing it into a concatenation would be a type error.
|
|
103
628
|
let result = '';
|
|
104
629
|
let lastIndex = 0;
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
result += str.slice(lastIndex, match.index);
|
|
630
|
+
// Fresh global instance per call — no shared lastIndex state.
|
|
631
|
+
for (const match of str.matchAll(new RegExp(MARKER_PATTERN_SOURCE, 'g'))) {
|
|
632
|
+
const idx = match.index ?? 0;
|
|
633
|
+
if (idx > lastIndex) {
|
|
634
|
+
result += str.slice(lastIndex, idx);
|
|
111
635
|
}
|
|
112
636
|
const [, resourceId, fieldPath] = match;
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
637
|
+
if (!resourceId || !fieldPath) {
|
|
638
|
+
result += match[0];
|
|
639
|
+
lastIndex = idx + match[0].length;
|
|
640
|
+
continue;
|
|
641
|
+
}
|
|
642
|
+
result += `\${string(${markerToCelPath(resourceId, fieldPath, context)})}`;
|
|
643
|
+
lastIndex = idx + match[0].length;
|
|
117
644
|
}
|
|
118
645
|
if (lastIndex < str.length) {
|
|
119
646
|
result += str.slice(lastIndex);
|
|
@@ -135,30 +662,37 @@ export function processResourceReferences(obj, context) {
|
|
|
135
662
|
}
|
|
136
663
|
if (isCelExpression(obj)) {
|
|
137
664
|
if (obj.__isTemplate) {
|
|
138
|
-
|
|
139
|
-
// format (e.g. "http://${schema.spec.name}.${service.metadata.namespace}").
|
|
140
|
-
// Pass them through as-is — do NOT convert to CEL concat or re-wrap.
|
|
141
|
-
return obj.expression;
|
|
665
|
+
return obj.expression.replace(/\$\{([^}]+)\}/g, (_match, innerExpr) => finalizeCelForKro(innerExpr, context?.nestedStatusCel, context));
|
|
142
666
|
}
|
|
143
667
|
// Bare CelExpression — may be a single schema.spec.X reference (possibly
|
|
144
|
-
// wrapped in `string(...)`).
|
|
145
|
-
//
|
|
146
|
-
//
|
|
147
|
-
const expr = obj.expression;
|
|
148
|
-
const
|
|
149
|
-
if (
|
|
150
|
-
return `\${${maybeWrapWithOmit(
|
|
668
|
+
// wrapped in `string(...)`). Delegate any single schema ref to
|
|
669
|
+
// maybeWrapWithOmit so nested optional ancestors get the same guard chain
|
|
670
|
+
// as KubernetesRef objects.
|
|
671
|
+
const expr = resolveNestedCompositionRefs(obj.expression, context?.nestedStatusCel, context?.resourceIds);
|
|
672
|
+
const bareRef = /^schema\.spec\.[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)*$/.exec(expr)?.[0];
|
|
673
|
+
if (bareRef) {
|
|
674
|
+
return `\${${maybeWrapWithOmit(bareRef, false, context?.omitFields)}}`;
|
|
151
675
|
}
|
|
152
|
-
const
|
|
153
|
-
if (
|
|
154
|
-
|
|
155
|
-
return `\${${maybeWrapWithOmit(innerPath, true, context.omitFields)}}`;
|
|
676
|
+
const stringRef = /^string\((schema\.spec\.[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)*)\)$/.exec(expr)?.[1];
|
|
677
|
+
if (stringRef) {
|
|
678
|
+
return `\${${maybeWrapWithOmit(stringRef, true, context?.omitFields)}}`;
|
|
156
679
|
}
|
|
157
|
-
return
|
|
680
|
+
return finalizeCelForKro(expr, context?.nestedStatusCel, context);
|
|
158
681
|
}
|
|
159
|
-
// Strings containing __KUBERNETES_REF__ markers from template literals
|
|
682
|
+
// Strings containing __KUBERNETES_REF__ markers from template literals.
|
|
683
|
+
// First resolve any nested-composition references — markers produced by
|
|
684
|
+
// `createKubernetesRefProxy` use a virtual baseId as the resourceId,
|
|
685
|
+
// and that baseId only resolves via `nestedStatusCel`. After that, any
|
|
686
|
+
// remaining markers (schema refs and direct resource refs) are converted
|
|
687
|
+
// to KRO mixed-template form.
|
|
160
688
|
if (typeof obj === 'string' && obj.includes('__KUBERNETES_REF_')) {
|
|
161
|
-
|
|
689
|
+
const resolved = resolveNestedRefMarkers(obj, context?.nestedStatusCel, context?.resourceIds, context);
|
|
690
|
+
if (resolved.includes('__KUBERNETES_REF_')) {
|
|
691
|
+
return convertKubernetesRefMarkersTocel(resolved, context);
|
|
692
|
+
}
|
|
693
|
+
// All markers were resolved through nestedStatusCel — the result is
|
|
694
|
+
// pure literal text or KRO-formatted CEL. Pass it through.
|
|
695
|
+
return resolved;
|
|
162
696
|
}
|
|
163
697
|
if (Array.isArray(obj)) {
|
|
164
698
|
return obj.map((item) => processResourceReferences(item, context));
|
|
@@ -186,121 +720,113 @@ export function processResourceReferences(obj, context) {
|
|
|
186
720
|
*
|
|
187
721
|
* Only processes dynamic fields that require Kro resolution.
|
|
188
722
|
*/
|
|
189
|
-
export function serializeStatusMappingsToCel(statusMappings, nestedStatusCel, resourceIds) {
|
|
723
|
+
export function serializeStatusMappingsToCel(statusMappings, nestedStatusCel, resourceIds, resourceAliases) {
|
|
190
724
|
logger.debug('Serializing status mappings to CEL', {
|
|
191
725
|
fieldCount: Object.keys(statusMappings).length,
|
|
192
726
|
hasNestedStatusCel: !!nestedStatusCel,
|
|
193
727
|
nestedStatusCelKeys: nestedStatusCel ? Object.keys(nestedStatusCel) : [],
|
|
194
728
|
});
|
|
195
729
|
const celExpressions = {};
|
|
730
|
+
const localResourceIds = resourceIds ? Array.from(resourceIds) : [];
|
|
731
|
+
const preserveVariables = new Set();
|
|
732
|
+
if (nestedStatusCel) {
|
|
733
|
+
for (const key of Object.keys(nestedStatusCel)) {
|
|
734
|
+
const match = key.match(/^__nestedStatus:([^:]+):/);
|
|
735
|
+
const id = match?.[1];
|
|
736
|
+
if (id && !resourceIds?.has(id)) {
|
|
737
|
+
preserveVariables.add(id);
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
function normalizeLocalResourceExpr(expr) {
|
|
742
|
+
let normalized = expr;
|
|
743
|
+
if (resourceAliases && resourceAliases.size > 0) {
|
|
744
|
+
const aliasEntries = Array.from(resourceAliases.entries())
|
|
745
|
+
.filter(([alias, resolvedId]) => alias !== resolvedId)
|
|
746
|
+
.sort((left, right) => right[0].length - left[0].length);
|
|
747
|
+
for (const [alias, resolvedId] of aliasEntries) {
|
|
748
|
+
normalized = normalized
|
|
749
|
+
.replace(new RegExp(`\\b${escapeRegExpLiteral(alias)}(?=\\.(?:status|spec|metadata)\\.)`, 'g'), resolvedId)
|
|
750
|
+
.replace(new RegExp(`__KUBERNETES_REF_${escapeRegExpLiteral(alias)}_`, 'g'), `__KUBERNETES_REF_${resolvedId}_`);
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
return localResourceIds.length > 0
|
|
754
|
+
? remapVariableNames(normalized, localResourceIds, preserveVariables)
|
|
755
|
+
: normalized;
|
|
756
|
+
}
|
|
757
|
+
/**
|
|
758
|
+
* Rewrite `schema.spec.*` references inside a resolved CEL expression so
|
|
759
|
+
* the result is valid KRO status CEL. KRO does not accept `schema.spec.*`
|
|
760
|
+
* in status CEL, so we apply two rewrites:
|
|
761
|
+
*
|
|
762
|
+
* - `schema.spec.X.Y.orValue(Z)` → `Z` (substitute the default)
|
|
763
|
+
* - `schema.spec.X.Y` → `X.spec.Y` iff `X` is a known resource ID
|
|
764
|
+
* (routes the reference to the deployed resource's spec field)
|
|
765
|
+
*
|
|
766
|
+
* Unknown schema references are left intact; the KRO factory's deploy-
|
|
767
|
+
* time hydration handles them.
|
|
768
|
+
*/
|
|
769
|
+
function rewriteSchemaRefsForKroStatus(expr) {
|
|
770
|
+
let out = expr.replace(/__schema__\.spec\.[a-zA-Z0-9_.]+\.orValue\(([^)]+)\)/g, '$1');
|
|
771
|
+
out = out.replace(/__schema__\.spec\.([a-zA-Z0-9_.]+)/g, 'spec.$1');
|
|
772
|
+
out = out.replace(/schema\.spec\.[a-zA-Z0-9_.]+\.orValue\(([^)]+)\)/g, '$1');
|
|
773
|
+
out = out.replace(/schema\.spec\.([a-zA-Z0-9]+)\.([a-zA-Z0-9.]+)/g, (_match, firstSegment, rest) => {
|
|
774
|
+
if (resourceIds?.has(firstSegment)) {
|
|
775
|
+
return `${firstSegment}.spec.${rest}`;
|
|
776
|
+
}
|
|
777
|
+
return _match;
|
|
778
|
+
});
|
|
779
|
+
out = out.replace(/schema\.spec\.([a-zA-Z_$][\w$]*)/g, 'spec.$1');
|
|
780
|
+
return out;
|
|
781
|
+
}
|
|
782
|
+
/**
|
|
783
|
+
* Compose the inner-expression resolution + KRO-status finalization
|
|
784
|
+
* pipeline used by both the KubernetesRef and CelExpression branches.
|
|
785
|
+
* Centralized here so the two branches don't drift on what "produce
|
|
786
|
+
* KRO status CEL from a resolved expression" means.
|
|
787
|
+
*/
|
|
788
|
+
function statusFieldFromExpression(expr) {
|
|
789
|
+
const resolved = normalizeLocalResourceExpr(resolveNestedCompositionRefs(normalizeLocalResourceExpr(expr), nestedStatusCel, resourceIds));
|
|
790
|
+
if (resolved.includes('__KUBERNETES_REF_')) {
|
|
791
|
+
// Marker-laden — use mixed-template form.
|
|
792
|
+
return rewriteSchemaRefsForKroStatus(convertKubernetesRefMarkersTocel(resolved));
|
|
793
|
+
}
|
|
794
|
+
if (resolved.includes('${')) {
|
|
795
|
+
// Already a KRO mixed-template value. Do not wrap it again as
|
|
796
|
+
// `${http://${...}}`, which is invalid CEL/YAML for status fields.
|
|
797
|
+
return rewriteSchemaRefsForKroStatus(resolved);
|
|
798
|
+
}
|
|
799
|
+
return `\${${rewriteSchemaRefsForKroStatus(resolved)}}`;
|
|
800
|
+
}
|
|
196
801
|
function serializeValue(value) {
|
|
197
802
|
if (isKubernetesRef(value)) {
|
|
198
803
|
const ref = value;
|
|
199
|
-
// For nested composition status references,
|
|
200
|
-
//
|
|
804
|
+
// For nested composition status references, look up the inner
|
|
805
|
+
// composition's analyzed CEL via the shared resolver and finalize
|
|
806
|
+
// it for KRO status emission.
|
|
201
807
|
if (ref.__nestedComposition && nestedStatusCel) {
|
|
202
808
|
const fieldName = ref.fieldPath.replace(/^status\./, '');
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
resourceId: ref.resourceId,
|
|
207
|
-
fieldPath: ref.fieldPath,
|
|
208
|
-
fieldName,
|
|
209
|
-
exactKey,
|
|
210
|
-
hasExactMatch: !!nestedStatusCel[exactKey],
|
|
211
|
-
});
|
|
212
|
-
if (nestedStatusCel[exactKey]) {
|
|
213
|
-
return `\${${nestedStatusCel[exactKey]}}`;
|
|
214
|
-
}
|
|
215
|
-
// Try base-name match: strip trailing digits and compare.
|
|
216
|
-
// The expression may use 'nestedService2' while the key has 'nestedService1'.
|
|
217
|
-
// Also try prefix match for variable name → composition baseId mapping.
|
|
218
|
-
const refBase = ref.resourceId.replace(/\d+$/, '');
|
|
219
|
-
for (const [key, cel] of Object.entries(nestedStatusCel)) {
|
|
220
|
-
const parts = key.split(':');
|
|
221
|
-
if (parts.length !== 3 || parts[2] !== fieldName)
|
|
222
|
-
continue;
|
|
223
|
-
const keyBase = parts[1].replace(/\d+$/, '');
|
|
224
|
-
if (refBase === keyBase) {
|
|
225
|
-
return `\${${cel}}`;
|
|
226
|
-
}
|
|
227
|
-
if (isCamelCasePrefix(refBase, keyBase) || isCamelCasePrefix(keyBase, refBase)) {
|
|
228
|
-
logger.warn('Nested status CEL resolved via prefix match (not exact)', {
|
|
229
|
-
refResourceId: ref.resourceId, matchedKey: key, fieldName,
|
|
230
|
-
});
|
|
231
|
-
return `\${${cel}}`;
|
|
232
|
-
}
|
|
809
|
+
const innerExpr = lookupNestedExpression(ref.resourceId, fieldName, nestedStatusCel);
|
|
810
|
+
if (innerExpr !== undefined) {
|
|
811
|
+
return statusFieldFromExpression(innerExpr);
|
|
233
812
|
}
|
|
234
813
|
}
|
|
235
|
-
//
|
|
236
|
-
//
|
|
237
|
-
//
|
|
238
|
-
//
|
|
239
|
-
|
|
240
|
-
return `\${${ref.resourceId}.${ref.fieldPath}}`;
|
|
814
|
+
// Unresolved ref — still run through the status-expression finalizer so
|
|
815
|
+
// direct schema refs (`__schema__.spec.x`) become KRO status refs (`spec.x`).
|
|
816
|
+
// Downstream validation will flag virtual ids that don't correspond to a
|
|
817
|
+
// real resource.
|
|
818
|
+
return statusFieldFromExpression(`${ref.resourceId}.${ref.fieldPath}`);
|
|
241
819
|
}
|
|
242
820
|
if (isCelExpression(value)) {
|
|
243
821
|
if (value.__isTemplate) {
|
|
244
|
-
return value.expression;
|
|
822
|
+
return value.expression.replace(/\$\{([^}]+)\}/g, (_match, innerExpr) => statusFieldFromExpression(innerExpr));
|
|
245
823
|
}
|
|
246
|
-
|
|
247
|
-
// actual CEL expression. Handles both standalone refs and refs embedded in
|
|
248
|
-
// larger expressions (e.g., `... && inngest.status.ready`).
|
|
249
|
-
//
|
|
250
|
-
// The fn.toString analysis uses variable names (e.g., `inngest`) but the
|
|
251
|
-
// nested status CEL keys use baseIds with instance numbers (e.g.,
|
|
252
|
-
// `inngestBootstrap1`). We try the exact match first, then scan for keys
|
|
253
|
-
// that start with the variable name.
|
|
254
|
-
let expr = value.expression;
|
|
255
|
-
if (nestedStatusCel) {
|
|
256
|
-
expr = expr.replace(/(\w+)\.status\.([\w.]+)/g, (_match, compId, field) => {
|
|
257
|
-
// Try exact match first
|
|
258
|
-
const exactKey = `__nestedStatus:${compId}:${field}`;
|
|
259
|
-
if (nestedStatusCel[exactKey]) {
|
|
260
|
-
return `(${nestedStatusCel[exactKey]})`;
|
|
261
|
-
}
|
|
262
|
-
// Try base-name match: strip trailing digits and compare.
|
|
263
|
-
// Also try prefix match: the fn.toString variable name (e.g., `inngest`)
|
|
264
|
-
// may be a prefix of the nested composition's baseId (e.g., `inngestBootstrap`).
|
|
265
|
-
const compIdBase = compId.replace(/\d+$/, '');
|
|
266
|
-
for (const [key, cel] of Object.entries(nestedStatusCel)) {
|
|
267
|
-
const parts = key.split(':');
|
|
268
|
-
if (parts.length !== 3 || parts[2] !== field)
|
|
269
|
-
continue;
|
|
270
|
-
const keyBase = parts[1].replace(/\d+$/, '');
|
|
271
|
-
if (compIdBase === keyBase) {
|
|
272
|
-
return `(${cel})`;
|
|
273
|
-
}
|
|
274
|
-
if (isCamelCasePrefix(compIdBase, keyBase) || isCamelCasePrefix(keyBase, compIdBase)) {
|
|
275
|
-
logger.warn('Nested status CEL expression resolved via prefix match', {
|
|
276
|
-
compId, matchedKey: key, field,
|
|
277
|
-
});
|
|
278
|
-
return `(${cel})`;
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
return `${compId}.status.${field}`;
|
|
282
|
-
});
|
|
283
|
-
}
|
|
284
|
-
// KRO status CEL cannot reference `schema.spec.*` — only resource IDs.
|
|
285
|
-
// Replace schema references with their .orValue() defaults, or with the
|
|
286
|
-
// resource's own spec field if a mapping is available.
|
|
287
|
-
// Pattern: `schema.spec.X.Y.orValue(Z)` → `Z` (use the default)
|
|
288
|
-
expr = expr.replace(/schema\.spec\.[a-zA-Z0-9_.]+\.orValue\(([^)]+)\)/g, '$1');
|
|
289
|
-
// For bare `schema.spec.X.Y` (no orValue): map to the resource's spec
|
|
290
|
-
// field ONLY if X is a known resource ID. E.g., `schema.spec.database.instances`
|
|
291
|
-
// → `database.spec.instances` when `database` is a resource in the RGD.
|
|
292
|
-
// Unknown segments are left as-is (they'll be resolved at deploy time).
|
|
293
|
-
expr = expr.replace(/schema\.spec\.([a-zA-Z0-9]+)\.([a-zA-Z0-9.]+)/g, (_match, firstSegment, rest) => {
|
|
294
|
-
if (resourceIds?.has(firstSegment)) {
|
|
295
|
-
return `${firstSegment}.spec.${rest}`;
|
|
296
|
-
}
|
|
297
|
-
// Not a known resource ID — keep the original schema ref.
|
|
298
|
-
// This will be resolved by the KRO factory at deploy time.
|
|
299
|
-
return _match;
|
|
300
|
-
});
|
|
301
|
-
return `\${${expr}}`;
|
|
824
|
+
return statusFieldFromExpression(value.expression);
|
|
302
825
|
}
|
|
303
|
-
if (
|
|
826
|
+
if (Array.isArray(value)) {
|
|
827
|
+
return value.map((item) => serializeValue(item));
|
|
828
|
+
}
|
|
829
|
+
if (value && typeof value === 'object') {
|
|
304
830
|
const nestedExpressions = {};
|
|
305
831
|
for (const [key, nestedValue] of Object.entries(value)) {
|
|
306
832
|
nestedExpressions[key] = serializeValue(nestedValue);
|
|
@@ -308,11 +834,13 @@ export function serializeStatusMappingsToCel(statusMappings, nestedStatusCel, re
|
|
|
308
834
|
return nestedExpressions;
|
|
309
835
|
}
|
|
310
836
|
if (typeof value === 'string') {
|
|
311
|
-
// Convert embedded __KUBERNETES_REF__ markers to CEL expressions
|
|
312
837
|
if (value.includes('__KUBERNETES_REF_')) {
|
|
313
|
-
|
|
838
|
+
// Resolve nested refs first (substitution is a no-op on pure marker
|
|
839
|
+
// strings but handles mixed forms), then convert markers to KRO CEL.
|
|
840
|
+
const resolved = resolveNestedRefMarkers(normalizeLocalResourceExpr(value), nestedStatusCel, resourceIds);
|
|
841
|
+
return rewriteSchemaRefsForKroStatus(convertKubernetesRefMarkersTocel(resolved));
|
|
314
842
|
}
|
|
315
|
-
return `\${"${value}"}`;
|
|
843
|
+
return `\${"${escapeCelString(value)}"}`;
|
|
316
844
|
}
|
|
317
845
|
if (typeof value === 'number') {
|
|
318
846
|
return `\${${value}}`;
|
|
@@ -320,9 +848,16 @@ export function serializeStatusMappingsToCel(statusMappings, nestedStatusCel, re
|
|
|
320
848
|
if (typeof value === 'boolean') {
|
|
321
849
|
return `\${${value}}`;
|
|
322
850
|
}
|
|
851
|
+
if (value === null) {
|
|
852
|
+
return `\${null}`;
|
|
853
|
+
}
|
|
323
854
|
return `\${""}`;
|
|
324
855
|
}
|
|
325
856
|
for (const [fieldName, fieldValue] of Object.entries(statusMappings)) {
|
|
857
|
+
// Skip internal metadata fields — these are consumed during
|
|
858
|
+
// serialization, not emitted as status fields.
|
|
859
|
+
if (fieldName.startsWith('__'))
|
|
860
|
+
continue;
|
|
326
861
|
celExpressions[fieldName] = serializeValue(fieldValue);
|
|
327
862
|
}
|
|
328
863
|
return celExpressions;
|
|
@@ -345,6 +880,7 @@ function isCamelCasePrefix(prefix, target) {
|
|
|
345
880
|
if (prefix.length === target.length)
|
|
346
881
|
return true;
|
|
347
882
|
// Character after the prefix must be uppercase (camelCase word boundary)
|
|
348
|
-
|
|
883
|
+
const boundaryChar = target[prefix.length];
|
|
884
|
+
return boundaryChar !== undefined && /[A-Z]/.test(boundaryChar);
|
|
349
885
|
}
|
|
350
886
|
//# sourceMappingURL=cel-references.js.map
|