typekro 0.2.2 → 0.3.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.
Files changed (197) hide show
  1. package/README.md +4 -3
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/core/composition/imperative.d.ts.map +1 -1
  4. package/dist/core/composition/imperative.js +15 -2
  5. package/dist/core/composition/imperative.js.map +1 -1
  6. package/dist/core/composition/typekro-runtime/typekro-runtime.d.ts.map +1 -1
  7. package/dist/core/composition/typekro-runtime/typekro-runtime.js +24 -25
  8. package/dist/core/composition/typekro-runtime/typekro-runtime.js.map +1 -1
  9. package/dist/core/dependencies/type-guards.d.ts.map +1 -1
  10. package/dist/core/dependencies/type-guards.js +7 -2
  11. package/dist/core/dependencies/type-guards.js.map +1 -1
  12. package/dist/core/deployment/engine.d.ts +0 -1
  13. package/dist/core/deployment/engine.d.ts.map +1 -1
  14. package/dist/core/deployment/engine.js +0 -1
  15. package/dist/core/deployment/engine.js.map +1 -1
  16. package/dist/core/errors.d.ts +85 -0
  17. package/dist/core/errors.d.ts.map +1 -1
  18. package/dist/core/errors.js +135 -0
  19. package/dist/core/errors.js.map +1 -1
  20. package/dist/core/expressions/analyzer.d.ts +584 -0
  21. package/dist/core/expressions/analyzer.d.ts.map +1 -0
  22. package/dist/core/expressions/analyzer.js +2956 -0
  23. package/dist/core/expressions/analyzer.js.map +1 -0
  24. package/dist/core/expressions/cache.d.ts +136 -0
  25. package/dist/core/expressions/cache.d.ts.map +1 -0
  26. package/dist/core/expressions/cache.js +347 -0
  27. package/dist/core/expressions/cache.js.map +1 -0
  28. package/dist/core/expressions/cel-conversion-engine.d.ts +126 -0
  29. package/dist/core/expressions/cel-conversion-engine.d.ts.map +1 -0
  30. package/dist/core/expressions/cel-conversion-engine.js +293 -0
  31. package/dist/core/expressions/cel-conversion-engine.js.map +1 -0
  32. package/dist/core/expressions/compile-time-validation.d.ts +270 -0
  33. package/dist/core/expressions/compile-time-validation.d.ts.map +1 -0
  34. package/dist/core/expressions/compile-time-validation.js +506 -0
  35. package/dist/core/expressions/compile-time-validation.js.map +1 -0
  36. package/dist/core/expressions/composition-integration.d.ts +315 -0
  37. package/dist/core/expressions/composition-integration.d.ts.map +1 -0
  38. package/dist/core/expressions/composition-integration.js +936 -0
  39. package/dist/core/expressions/composition-integration.js.map +1 -0
  40. package/dist/core/expressions/conditional-expression-processor.d.ts +154 -0
  41. package/dist/core/expressions/conditional-expression-processor.d.ts.map +1 -0
  42. package/dist/core/expressions/conditional-expression-processor.js +479 -0
  43. package/dist/core/expressions/conditional-expression-processor.js.map +1 -0
  44. package/dist/core/expressions/conditional-integration.d.ts +133 -0
  45. package/dist/core/expressions/conditional-integration.d.ts.map +1 -0
  46. package/dist/core/expressions/conditional-integration.js +293 -0
  47. package/dist/core/expressions/conditional-integration.js.map +1 -0
  48. package/dist/core/expressions/conditional-validation.d.ts +181 -0
  49. package/dist/core/expressions/conditional-validation.d.ts.map +1 -0
  50. package/dist/core/expressions/conditional-validation.js +460 -0
  51. package/dist/core/expressions/conditional-validation.js.map +1 -0
  52. package/dist/core/expressions/context-aware-generator.d.ts +127 -0
  53. package/dist/core/expressions/context-aware-generator.d.ts.map +1 -0
  54. package/dist/core/expressions/context-aware-generator.js +500 -0
  55. package/dist/core/expressions/context-aware-generator.js.map +1 -0
  56. package/dist/core/expressions/context-detector.d.ts +148 -0
  57. package/dist/core/expressions/context-detector.d.ts.map +1 -0
  58. package/dist/core/expressions/context-detector.js +546 -0
  59. package/dist/core/expressions/context-detector.js.map +1 -0
  60. package/dist/core/expressions/context-switcher.d.ts +185 -0
  61. package/dist/core/expressions/context-switcher.d.ts.map +1 -0
  62. package/dist/core/expressions/context-switcher.js +515 -0
  63. package/dist/core/expressions/context-switcher.js.map +1 -0
  64. package/dist/core/expressions/context-validator.d.ts +176 -0
  65. package/dist/core/expressions/context-validator.d.ts.map +1 -0
  66. package/dist/core/expressions/context-validator.js +452 -0
  67. package/dist/core/expressions/context-validator.js.map +1 -0
  68. package/dist/core/expressions/custom-context-manager.d.ts +194 -0
  69. package/dist/core/expressions/custom-context-manager.d.ts.map +1 -0
  70. package/dist/core/expressions/custom-context-manager.js +390 -0
  71. package/dist/core/expressions/custom-context-manager.js.map +1 -0
  72. package/dist/core/expressions/expression-proxy.d.ts +80 -0
  73. package/dist/core/expressions/expression-proxy.d.ts.map +1 -0
  74. package/dist/core/expressions/expression-proxy.js +227 -0
  75. package/dist/core/expressions/expression-proxy.js.map +1 -0
  76. package/dist/core/expressions/factory-integration.d.ts +132 -0
  77. package/dist/core/expressions/factory-integration.d.ts.map +1 -0
  78. package/dist/core/expressions/factory-integration.js +327 -0
  79. package/dist/core/expressions/factory-integration.js.map +1 -0
  80. package/dist/core/expressions/factory-pattern-handler.d.ts +88 -0
  81. package/dist/core/expressions/factory-pattern-handler.d.ts.map +1 -0
  82. package/dist/core/expressions/factory-pattern-handler.js +336 -0
  83. package/dist/core/expressions/factory-pattern-handler.js.map +1 -0
  84. package/dist/core/expressions/field-hydration-processor.d.ts +188 -0
  85. package/dist/core/expressions/field-hydration-processor.d.ts.map +1 -0
  86. package/dist/core/expressions/field-hydration-processor.js +562 -0
  87. package/dist/core/expressions/field-hydration-processor.js.map +1 -0
  88. package/dist/core/expressions/imperative-analyzer.d.ts +21 -0
  89. package/dist/core/expressions/imperative-analyzer.d.ts.map +1 -0
  90. package/dist/core/expressions/imperative-analyzer.js +343 -0
  91. package/dist/core/expressions/imperative-analyzer.js.map +1 -0
  92. package/dist/core/expressions/index.d.ts +54 -0
  93. package/dist/core/expressions/index.d.ts.map +1 -0
  94. package/dist/core/expressions/index.js +50 -0
  95. package/dist/core/expressions/index.js.map +1 -0
  96. package/dist/core/expressions/lazy-analysis.d.ts +1128 -0
  97. package/dist/core/expressions/lazy-analysis.d.ts.map +1 -0
  98. package/dist/core/expressions/lazy-analysis.js +2443 -0
  99. package/dist/core/expressions/lazy-analysis.js.map +1 -0
  100. package/dist/core/expressions/magic-assignable-analyzer.d.ts +123 -0
  101. package/dist/core/expressions/magic-assignable-analyzer.d.ts.map +1 -0
  102. package/dist/core/expressions/magic-assignable-analyzer.js +352 -0
  103. package/dist/core/expressions/magic-assignable-analyzer.js.map +1 -0
  104. package/dist/core/expressions/magic-proxy-analyzer.d.ts +206 -0
  105. package/dist/core/expressions/magic-proxy-analyzer.d.ts.map +1 -0
  106. package/dist/core/expressions/magic-proxy-analyzer.js +639 -0
  107. package/dist/core/expressions/magic-proxy-analyzer.js.map +1 -0
  108. package/dist/core/expressions/magic-proxy-detector.d.ts +154 -0
  109. package/dist/core/expressions/magic-proxy-detector.d.ts.map +1 -0
  110. package/dist/core/expressions/magic-proxy-detector.js +242 -0
  111. package/dist/core/expressions/magic-proxy-detector.js.map +1 -0
  112. package/dist/core/expressions/migration-helpers.d.ts +133 -0
  113. package/dist/core/expressions/migration-helpers.d.ts.map +1 -0
  114. package/dist/core/expressions/migration-helpers.js +443 -0
  115. package/dist/core/expressions/migration-helpers.js.map +1 -0
  116. package/dist/core/expressions/optionality-handler.d.ts +503 -0
  117. package/dist/core/expressions/optionality-handler.d.ts.map +1 -0
  118. package/dist/core/expressions/optionality-handler.js +1306 -0
  119. package/dist/core/expressions/optionality-handler.js.map +1 -0
  120. package/dist/core/expressions/readiness-integration.d.ts +119 -0
  121. package/dist/core/expressions/readiness-integration.d.ts.map +1 -0
  122. package/dist/core/expressions/readiness-integration.js +386 -0
  123. package/dist/core/expressions/readiness-integration.js.map +1 -0
  124. package/dist/core/expressions/resource-analyzer.d.ts +486 -0
  125. package/dist/core/expressions/resource-analyzer.d.ts.map +1 -0
  126. package/dist/core/expressions/resource-analyzer.js +1086 -0
  127. package/dist/core/expressions/resource-analyzer.js.map +1 -0
  128. package/dist/core/expressions/resource-validation.d.ts +187 -0
  129. package/dist/core/expressions/resource-validation.d.ts.map +1 -0
  130. package/dist/core/expressions/resource-validation.js +552 -0
  131. package/dist/core/expressions/resource-validation.js.map +1 -0
  132. package/dist/core/expressions/runtime-error-mapper.d.ts +138 -0
  133. package/dist/core/expressions/runtime-error-mapper.d.ts.map +1 -0
  134. package/dist/core/expressions/runtime-error-mapper.js +412 -0
  135. package/dist/core/expressions/runtime-error-mapper.js.map +1 -0
  136. package/dist/core/expressions/source-map.d.ts +168 -0
  137. package/dist/core/expressions/source-map.d.ts.map +1 -0
  138. package/dist/core/expressions/source-map.js +350 -0
  139. package/dist/core/expressions/source-map.js.map +1 -0
  140. package/dist/core/expressions/status-builder-analyzer.d.ts +353 -0
  141. package/dist/core/expressions/status-builder-analyzer.d.ts.map +1 -0
  142. package/dist/core/expressions/status-builder-analyzer.js +1301 -0
  143. package/dist/core/expressions/status-builder-analyzer.js.map +1 -0
  144. package/dist/core/expressions/type-inference.d.ts +184 -0
  145. package/dist/core/expressions/type-inference.d.ts.map +1 -0
  146. package/dist/core/expressions/type-inference.js +838 -0
  147. package/dist/core/expressions/type-inference.js.map +1 -0
  148. package/dist/core/expressions/type-safety.d.ts +203 -0
  149. package/dist/core/expressions/type-safety.d.ts.map +1 -0
  150. package/dist/core/expressions/type-safety.js +442 -0
  151. package/dist/core/expressions/type-safety.js.map +1 -0
  152. package/dist/core/expressions/types.d.ts +282 -0
  153. package/dist/core/expressions/types.d.ts.map +1 -0
  154. package/dist/core/expressions/types.js +8 -0
  155. package/dist/core/expressions/types.js.map +1 -0
  156. package/dist/core/kubernetes/client-provider.js +2 -2
  157. package/dist/core/kubernetes/client-provider.js.map +1 -1
  158. package/dist/core/serialization/core.d.ts.map +1 -1
  159. package/dist/core/serialization/core.js +573 -9
  160. package/dist/core/serialization/core.js.map +1 -1
  161. package/dist/core/types/deployment.d.ts +4 -0
  162. package/dist/core/types/deployment.d.ts.map +1 -1
  163. package/dist/core/types/deployment.js.map +1 -1
  164. package/dist/core/types/index.d.ts +1 -0
  165. package/dist/core/types/index.d.ts.map +1 -1
  166. package/dist/core/types/index.js.map +1 -1
  167. package/dist/core.d.ts +1 -1
  168. package/dist/core.d.ts.map +1 -1
  169. package/dist/core.js +1 -1
  170. package/dist/core.js.map +1 -1
  171. package/dist/factories/helm/helm-release.d.ts.map +1 -1
  172. package/dist/factories/helm/helm-release.js +0 -5
  173. package/dist/factories/helm/helm-release.js.map +1 -1
  174. package/dist/factories/helm/types.d.ts +1 -1
  175. package/dist/factories/helm/types.d.ts.map +1 -1
  176. package/dist/factories/shared.d.ts.map +1 -1
  177. package/dist/factories/shared.js +21 -1
  178. package/dist/factories/shared.js.map +1 -1
  179. package/dist/factories/simple/index.d.ts +2 -2
  180. package/dist/factories/simple/index.d.ts.map +1 -1
  181. package/dist/factories/simple/workloads/deployment.d.ts +3 -3
  182. package/dist/factories/simple/workloads/deployment.d.ts.map +1 -1
  183. package/dist/factories/simple/workloads/deployment.js +37 -11
  184. package/dist/factories/simple/workloads/deployment.js.map +1 -1
  185. package/dist/index.d.ts +1 -1
  186. package/dist/index.d.ts.map +1 -1
  187. package/dist/index.js +1 -1
  188. package/dist/index.js.map +1 -1
  189. package/dist/utils/index.d.ts +1 -1
  190. package/dist/utils/index.d.ts.map +1 -1
  191. package/dist/utils/index.js +1 -1
  192. package/dist/utils/index.js.map +1 -1
  193. package/dist/utils/type-guards.d.ts +6 -0
  194. package/dist/utils/type-guards.d.ts.map +1 -1
  195. package/dist/utils/type-guards.js +25 -2
  196. package/dist/utils/type-guards.js.map +1 -1
  197. package/package.json +6 -1
@@ -0,0 +1,1306 @@
1
+ /**
2
+ * Enhanced Type Optionality Handler for JavaScript to CEL Expression Conversion
3
+ *
4
+ * This module handles the mismatch between Enhanced type compile-time non-optionality
5
+ * and runtime optionality during field hydration. Enhanced types show fields as
6
+ * non-optional at compile time, but KubernetesRef objects might resolve to undefined
7
+ * during field hydration.
8
+ *
9
+ * Key Features:
10
+ * - Automatic null-safety detection for Enhanced type KubernetesRef objects
11
+ * - CEL expression generation with has() checks for potentially undefined fields
12
+ * - Support for optional chaining with Enhanced types that appear non-optional
13
+ * - Integration with field hydration timing to handle undefined-to-defined transitions
14
+ * - Context-aware optionality handling based on field hydration state
15
+ */
16
+ import { ConversionError } from '../errors.js';
17
+ import { getComponentLogger } from '../logging/index.js';
18
+ import { isKubernetesRef, } from '../../utils/type-guards.js';
19
+ import { CEL_EXPRESSION_BRAND, KUBERNETES_REF_BRAND } from '../constants/brands.js';
20
+ /**
21
+ * Default optionality handling options
22
+ */
23
+ const DEFAULT_OPTIONALITY_OPTIONS = {
24
+ deepAnalysis: true,
25
+ conservative: true,
26
+ useKroConditionals: true,
27
+ generateHasChecks: true,
28
+ maxDepth: 5,
29
+ includeReasoning: true
30
+ };
31
+ /**
32
+ * Enhanced Type Optionality Handler
33
+ *
34
+ * Handles the complexity of Enhanced types that appear non-optional at compile time
35
+ * but may be undefined at runtime during field hydration.
36
+ */
37
+ export class EnhancedTypeOptionalityHandler {
38
+ options;
39
+ logger = getComponentLogger('optionality-handler');
40
+ constructor(options) {
41
+ this.options = { ...DEFAULT_OPTIONALITY_OPTIONS, ...options };
42
+ }
43
+ /**
44
+ * Analyze KubernetesRef objects for optionality requirements
45
+ *
46
+ * This method determines whether KubernetesRef objects in expressions require
47
+ * null-safety handling based on Enhanced type behavior and field hydration timing.
48
+ */
49
+ analyzeOptionalityRequirements(expression, context) {
50
+ const results = [];
51
+ try {
52
+ // Extract all KubernetesRef objects from the expression
53
+ const kubernetesRefs = this.extractKubernetesRefs(expression);
54
+ this.logger.debug('Analyzing optionality requirements', {
55
+ expressionType: typeof expression,
56
+ kubernetesRefCount: kubernetesRefs.length,
57
+ contextType: context.type
58
+ });
59
+ for (const ref of kubernetesRefs) {
60
+ const analysis = this.analyzeKubernetesRefOptionality(ref, context);
61
+ results.push(analysis);
62
+ }
63
+ return results;
64
+ }
65
+ catch (error) {
66
+ this.logger.error('Failed to analyze optionality requirements', error);
67
+ return [];
68
+ }
69
+ }
70
+ /**
71
+ * Generate CEL expressions with appropriate null-safety checks
72
+ *
73
+ * This method takes the optionality analysis results and generates CEL expressions
74
+ * that include proper null-safety handling for potentially undefined fields.
75
+ */
76
+ generateNullSafeCelExpression(originalExpression, optionalityResults, context) {
77
+ try {
78
+ // Determine if any KubernetesRef objects require null-safety
79
+ const requiresNullSafety = optionalityResults.some(result => result.requiresNullSafety);
80
+ if (!requiresNullSafety) {
81
+ // No null-safety required, return as-is
82
+ return {
83
+ valid: true,
84
+ celExpression: this.convertToBasicCel(originalExpression, context),
85
+ dependencies: optionalityResults.map(r => r.kubernetesRef),
86
+ sourceMap: [],
87
+ errors: [],
88
+ warnings: [],
89
+ requiresConversion: optionalityResults.length > 0
90
+ };
91
+ }
92
+ // Generate null-safe CEL expression
93
+ const nullSafeCel = this.generateNullSafeExpression(originalExpression, optionalityResults, context);
94
+ return {
95
+ valid: true,
96
+ celExpression: nullSafeCel,
97
+ dependencies: optionalityResults.map(r => r.kubernetesRef),
98
+ sourceMap: this.generateSourceMapping(originalExpression, nullSafeCel, context),
99
+ errors: [],
100
+ warnings: [],
101
+ requiresConversion: true
102
+ };
103
+ }
104
+ catch (error) {
105
+ const conversionError = new ConversionError(`Failed to generate null-safe CEL expression: ${error instanceof Error ? error.message : String(error)}`, String(originalExpression), 'unknown');
106
+ return {
107
+ valid: false,
108
+ celExpression: null,
109
+ dependencies: optionalityResults.map(r => r.kubernetesRef),
110
+ sourceMap: [],
111
+ errors: [conversionError],
112
+ warnings: [],
113
+ requiresConversion: true
114
+ };
115
+ }
116
+ }
117
+ /**
118
+ * Handle optional chaining with Enhanced types
119
+ *
120
+ * This method specifically handles cases where optional chaining is used with
121
+ * Enhanced types that appear non-optional at compile time.
122
+ */
123
+ handleOptionalChainingWithEnhancedTypes(expression, context) {
124
+ try {
125
+ // Detect optional chaining patterns in the expression
126
+ const optionalChainingAnalysis = this.analyzeOptionalChainingPatterns(expression, context);
127
+ if (optionalChainingAnalysis.patterns.length === 0) {
128
+ // No optional chaining detected - analyze for regular optionality
129
+ const optionalityResults = this.analyzeOptionalityRequirements(expression, context);
130
+ return this.generateNullSafeCelExpression(expression, optionalityResults, context);
131
+ }
132
+ // Generate appropriate CEL expressions for optional chaining with Enhanced types
133
+ const celResult = this.generateOptionalChainingCelExpression(expression, optionalChainingAnalysis, context);
134
+ return celResult;
135
+ }
136
+ catch (error) {
137
+ const conversionError = new ConversionError(`Failed to handle optional chaining: ${error instanceof Error ? error.message : String(error)}`, String(expression), 'optional-chaining');
138
+ return {
139
+ valid: false,
140
+ celExpression: null,
141
+ dependencies: this.extractKubernetesRefs(expression),
142
+ sourceMap: [],
143
+ errors: [conversionError],
144
+ warnings: [],
145
+ requiresConversion: true
146
+ };
147
+ }
148
+ }
149
+ /**
150
+ * Analyze optional chaining patterns in expressions with Enhanced types
151
+ */
152
+ analyzeOptionalChainingPatterns(expression, context) {
153
+ const patterns = [];
154
+ const enhancedTypeFields = [];
155
+ // Extract KubernetesRef objects that might be involved in optional chaining
156
+ const kubernetesRefs = this.extractKubernetesRefs(expression);
157
+ for (const ref of kubernetesRefs) {
158
+ // Check if this KubernetesRef represents an Enhanced type field
159
+ const enhancedFieldInfo = this.analyzeEnhancedTypeField(ref, context);
160
+ if (enhancedFieldInfo.isEnhancedType) {
161
+ enhancedTypeFields.push(enhancedFieldInfo);
162
+ // Create optional chaining pattern for this Enhanced type field
163
+ const pattern = {
164
+ kubernetesRef: ref,
165
+ fieldPath: ref.fieldPath || '',
166
+ isEnhancedType: true,
167
+ appearsNonOptional: enhancedFieldInfo.appearsNonOptional,
168
+ actuallyOptional: enhancedFieldInfo.actuallyOptional,
169
+ chainingDepth: this.calculateChainingDepth(ref.fieldPath || ''),
170
+ suggestedCelPattern: this.generateOptionalChainingCelPattern(ref, context)
171
+ };
172
+ patterns.push(pattern);
173
+ }
174
+ }
175
+ const requiresSpecialHandling = enhancedTypeFields.some(field => field.appearsNonOptional && field.actuallyOptional);
176
+ return { patterns, enhancedTypeFields, requiresSpecialHandling };
177
+ }
178
+ /**
179
+ * Analyze Enhanced type field information
180
+ */
181
+ analyzeEnhancedTypeField(kubernetesRef, context) {
182
+ const fieldPath = kubernetesRef.fieldPath || '';
183
+ const isStatusField = fieldPath.startsWith('status.');
184
+ // Enhanced types in status fields appear non-optional but are actually optional
185
+ const appearsNonOptional = !fieldPath.includes('?') && !fieldPath.includes('|');
186
+ const actuallyOptional = isStatusField || this.isPotentiallyUndefinedAtRuntime(kubernetesRef, context);
187
+ return {
188
+ kubernetesRef,
189
+ fieldPath,
190
+ isEnhancedType: true,
191
+ appearsNonOptional,
192
+ actuallyOptional,
193
+ isStatusField,
194
+ requiresOptionalChaining: appearsNonOptional && actuallyOptional,
195
+ confidence: this.calculateOptionalityConfidence(kubernetesRef, context)
196
+ };
197
+ }
198
+ /**
199
+ * Generate CEL expression for optional chaining with Enhanced types
200
+ */
201
+ generateOptionalChainingCelExpression(expression, optionalChainingAnalysis, context) {
202
+ try {
203
+ if (!optionalChainingAnalysis.requiresSpecialHandling) {
204
+ // No special handling needed - use regular conversion
205
+ const optionalityResults = this.analyzeOptionalityRequirements(expression, context);
206
+ return this.generateNullSafeCelExpression(expression, optionalityResults, context);
207
+ }
208
+ // Generate CEL expression with proper optional chaining support
209
+ let celExpression;
210
+ if (context.useKroConditionals) {
211
+ // Use Kro's conditional operators for optional chaining
212
+ celExpression = this.generateKroOptionalChainingExpression(optionalChainingAnalysis.patterns, context);
213
+ }
214
+ else {
215
+ // Use has() checks for optional chaining
216
+ celExpression = this.generateHasCheckOptionalChainingExpression(optionalChainingAnalysis.patterns, context);
217
+ }
218
+ const dependencies = optionalChainingAnalysis.patterns.map(p => p.kubernetesRef);
219
+ return {
220
+ valid: true,
221
+ celExpression: {
222
+ [CEL_EXPRESSION_BRAND]: true,
223
+ expression: celExpression,
224
+ type: this.inferExpressionType(expression, context)
225
+ },
226
+ dependencies,
227
+ sourceMap: this.generateSourceMapping(expression, { expression: celExpression }, context),
228
+ errors: [],
229
+ warnings: [],
230
+ requiresConversion: true
231
+ };
232
+ }
233
+ catch (error) {
234
+ const conversionError = new ConversionError(`Failed to generate optional chaining CEL: ${error instanceof Error ? error.message : String(error)}`, String(expression), 'optional-chaining');
235
+ return {
236
+ valid: false,
237
+ celExpression: null,
238
+ dependencies: optionalChainingAnalysis.patterns.map(p => p.kubernetesRef),
239
+ sourceMap: [],
240
+ errors: [conversionError],
241
+ warnings: [],
242
+ requiresConversion: true
243
+ };
244
+ }
245
+ }
246
+ /**
247
+ * Generate Kro CEL expression with ? prefix operator for optional chaining
248
+ *
249
+ * Kro uses the ? operator as a prefix before field names for optional access
250
+ */
251
+ generateKroOptionalChainingExpression(patterns, _context) {
252
+ if (patterns.length === 0) {
253
+ return 'null';
254
+ }
255
+ // For Kro, use ? prefix operator for optional field access
256
+ const expressions = patterns.map(pattern => {
257
+ const resourcePath = pattern.kubernetesRef.resourceId === '__schema__'
258
+ ? `schema.${pattern.fieldPath}`
259
+ : `resources.${pattern.kubernetesRef.resourceId}.${pattern.fieldPath}`;
260
+ // Convert field.path.to.value to field.?path.?to.?value (Kro ? prefix syntax)
261
+ const optionalPath = this.convertToKroOptionalSyntax(resourcePath);
262
+ return optionalPath;
263
+ });
264
+ // Combine multiple patterns if needed
265
+ if (expressions.length === 1) {
266
+ return expressions[0] || 'null';
267
+ }
268
+ // For multiple patterns, use logical AND
269
+ return expressions.join(' && ');
270
+ }
271
+ /**
272
+ * Convert a field path to Kro's ? prefix optional syntax
273
+ * Example: resources.service.status.loadBalancer.ingress[0].ip
274
+ * Becomes: resources.service.status.?loadBalancer.?ingress[0].?ip
275
+ *
276
+ * The ? operator should be placed before fields that might not exist
277
+ */
278
+ convertToKroOptionalSyntax(resourcePath) {
279
+ // Split the path into parts, handling array access
280
+ const parts = resourcePath.split('.');
281
+ const result = [];
282
+ for (let i = 0; i < parts.length; i++) {
283
+ const part = parts[i];
284
+ // Ensure part is defined
285
+ if (!part)
286
+ continue;
287
+ // Don't add ? to root parts (resources, schema) or the resource ID
288
+ if (i < 3) {
289
+ result.push(part);
290
+ }
291
+ else {
292
+ // Add ? prefix for optional access to nested fields that might not exist
293
+ if (part.includes('[')) {
294
+ // Handle array access: field[0] becomes ?field[0]
295
+ result.push(`?${part}`);
296
+ }
297
+ else {
298
+ result.push(`?${part}`);
299
+ }
300
+ }
301
+ }
302
+ return result.join('.');
303
+ }
304
+ /**
305
+ * Generate has() check expression for optional chaining
306
+ */
307
+ generateHasCheckOptionalChainingExpression(patterns, _context) {
308
+ if (patterns.length === 0) {
309
+ return 'null';
310
+ }
311
+ const expressions = [];
312
+ for (const pattern of patterns) {
313
+ const resourcePath = pattern.kubernetesRef.resourceId === '__schema__'
314
+ ? `schema.${pattern.fieldPath}`
315
+ : `resources.${pattern.kubernetesRef.resourceId}.${pattern.fieldPath}`;
316
+ // Generate nested has() checks for the field path
317
+ const hasChecks = this.generateNestedHasChecksForPath(resourcePath);
318
+ const finalExpression = `${hasChecks.join(' && ')} && ${resourcePath}`;
319
+ expressions.push(finalExpression);
320
+ }
321
+ return expressions.join(' && ');
322
+ }
323
+ /**
324
+ * Generate nested has() checks for a field path
325
+ */
326
+ generateNestedHasChecksForPath(resourcePath) {
327
+ const checks = [];
328
+ const parts = resourcePath.split('.');
329
+ for (let i = 0; i < parts.length; i++) {
330
+ const partialPath = parts.slice(0, i + 1).join('.');
331
+ checks.push(`has(${partialPath})`);
332
+ }
333
+ return checks;
334
+ }
335
+ /**
336
+ * Calculate chaining depth for a field path
337
+ */
338
+ calculateChainingDepth(fieldPath) {
339
+ return fieldPath.split('.').length;
340
+ }
341
+ /**
342
+ * Generate optional chaining CEL pattern for a KubernetesRef
343
+ */
344
+ generateOptionalChainingCelPattern(kubernetesRef, context) {
345
+ const resourcePath = kubernetesRef.resourceId === '__schema__'
346
+ ? `schema.${kubernetesRef.fieldPath}`
347
+ : `resources.${kubernetesRef.resourceId}.${kubernetesRef.fieldPath}`;
348
+ if (context.useKroConditionals) {
349
+ // Use Kro's ? prefix operator for optional access
350
+ return this.convertToKroOptionalSyntax(resourcePath);
351
+ }
352
+ // Fallback to has() checks for better null safety
353
+ const hasChecks = this.generateNestedHasChecksForPath(resourcePath);
354
+ return `${hasChecks.join(' && ')} ? ${resourcePath} : null`;
355
+ }
356
+ /**
357
+ * Automatically detect null-safety requirements for Enhanced type KubernetesRef objects
358
+ *
359
+ * This method analyzes Enhanced types and their KubernetesRef objects to determine
360
+ * which fields require null-safety checks despite appearing non-optional at compile time.
361
+ */
362
+ detectNullSafetyRequirements(enhancedResources, context) {
363
+ const nullSafetyMap = new Map();
364
+ try {
365
+ this.logger.debug('Detecting null-safety requirements for Enhanced types', {
366
+ resourceCount: Object.keys(enhancedResources).length,
367
+ contextType: context.type
368
+ });
369
+ for (const [resourceId, enhancedResource] of Object.entries(enhancedResources)) {
370
+ const resourceAnalysis = [];
371
+ // Analyze the Enhanced resource for potential KubernetesRef objects
372
+ const potentialRefs = this.extractPotentialKubernetesRefsFromEnhanced(enhancedResource, resourceId);
373
+ for (const ref of potentialRefs) {
374
+ const analysis = this.analyzeKubernetesRefOptionality(ref, context);
375
+ // Enhanced types require special handling
376
+ if (analysis.potentiallyUndefined) {
377
+ analysis.reason = `Enhanced type field '${analysis.fieldPath}' appears non-optional at compile time but may be undefined at runtime during field hydration`;
378
+ analysis.requiresNullSafety = true;
379
+ analysis.suggestedCelPattern = this.generateEnhancedTypeNullSafetyPattern(ref, context);
380
+ }
381
+ resourceAnalysis.push(analysis);
382
+ }
383
+ if (resourceAnalysis.length > 0) {
384
+ nullSafetyMap.set(resourceId, resourceAnalysis);
385
+ }
386
+ }
387
+ this.logger.debug('Null-safety detection complete', {
388
+ resourcesWithNullSafety: nullSafetyMap.size,
389
+ totalAnalysisResults: Array.from(nullSafetyMap.values()).reduce((sum, arr) => sum + arr.length, 0)
390
+ });
391
+ return nullSafetyMap;
392
+ }
393
+ catch (error) {
394
+ this.logger.error('Failed to detect null-safety requirements', error);
395
+ return new Map();
396
+ }
397
+ }
398
+ /**
399
+ * Generate Enhanced type-specific null-safety patterns
400
+ */
401
+ generateEnhancedTypeNullSafetyPattern(kubernetesRef, context) {
402
+ const resourcePath = kubernetesRef.resourceId === '__schema__'
403
+ ? `schema.${kubernetesRef.fieldPath}`
404
+ : `resources.${kubernetesRef.resourceId}.${kubernetesRef.fieldPath}`;
405
+ // For Enhanced types, we need to be extra careful about null-safety
406
+ if (context.generateHasChecks) {
407
+ // Use has() checks for potentially undefined Enhanced type fields
408
+ if (kubernetesRef.fieldPath?.includes('.')) {
409
+ // For nested fields, check each level
410
+ const pathParts = kubernetesRef.fieldPath.split('.');
411
+ const checks = [];
412
+ for (let i = 0; i < pathParts.length; i++) {
413
+ const partialPath = pathParts.slice(0, i + 1).join('.');
414
+ const fullPath = kubernetesRef.resourceId === '__schema__'
415
+ ? `schema.${partialPath}`
416
+ : `resources.${kubernetesRef.resourceId}.${partialPath}`;
417
+ checks.push(`has(${fullPath})`);
418
+ }
419
+ return `${checks.join(' && ')} && ${resourcePath}`;
420
+ }
421
+ else {
422
+ return `has(${resourcePath}) && ${resourcePath}`;
423
+ }
424
+ }
425
+ if (context.useKroConditionals) {
426
+ // Use Kro's ? prefix operator for Enhanced types
427
+ return this.convertToKroOptionalSyntax(resourcePath);
428
+ }
429
+ // Fallback to basic null check
430
+ return `${resourcePath} != null && ${resourcePath}`;
431
+ }
432
+ /**
433
+ * Extract potential KubernetesRef objects from Enhanced resources
434
+ */
435
+ extractPotentialKubernetesRefsFromEnhanced(_enhancedResource, resourceId) {
436
+ const refs = [];
437
+ // Common field paths that might contain KubernetesRef objects in Enhanced types
438
+ const commonFieldPaths = [
439
+ 'status.readyReplicas',
440
+ 'status.availableReplicas',
441
+ 'status.conditions',
442
+ 'status.phase',
443
+ 'status.podIP',
444
+ 'status.hostIP',
445
+ 'status.loadBalancer.ingress',
446
+ 'spec.replicas',
447
+ 'spec.selector',
448
+ 'metadata.name',
449
+ 'metadata.namespace',
450
+ 'metadata.labels',
451
+ 'metadata.annotations'
452
+ ];
453
+ for (const fieldPath of commonFieldPaths) {
454
+ // Create a potential KubernetesRef for analysis
455
+ const potentialRef = {
456
+ [KUBERNETES_REF_BRAND]: true,
457
+ resourceId,
458
+ fieldPath,
459
+ type: 'unknown'
460
+ };
461
+ refs.push(potentialRef);
462
+ }
463
+ return refs;
464
+ }
465
+ /**
466
+ * Integrate with field hydration timing
467
+ *
468
+ * This method provides integration with TypeKro's field hydration system to
469
+ * handle the transition from undefined to defined values during hydration.
470
+ */
471
+ integrateWithFieldHydrationTiming(expression, hydrationStates, context) {
472
+ try {
473
+ const kubernetesRefs = this.extractKubernetesRefs(expression);
474
+ // Analyze hydration states for all references
475
+ const hydrationAnalysis = this.analyzeHydrationStates(kubernetesRefs, hydrationStates);
476
+ // Generate expressions for different hydration phases
477
+ const preHydrationExpression = this.generatePreHydrationExpression(expression, hydrationAnalysis.unhydratedRefs, context);
478
+ const postHydrationExpression = this.generatePostHydrationExpression(expression, hydrationAnalysis.hydratedRefs, context);
479
+ const hydrationDependentExpression = this.generateHydrationDependentExpression(expression, hydrationAnalysis.hydratingRefs, context);
480
+ // Generate transition handlers for undefined-to-defined transitions
481
+ const transitionHandlers = this.generateHydrationTransitionHandlers(expression, hydrationAnalysis, context);
482
+ return {
483
+ preHydrationExpression,
484
+ postHydrationExpression,
485
+ hydrationDependentExpression,
486
+ transitionHandlers
487
+ };
488
+ }
489
+ catch (error) {
490
+ this.logger.error('Failed to integrate with field hydration timing', error);
491
+ return {
492
+ preHydrationExpression: null,
493
+ postHydrationExpression: null,
494
+ hydrationDependentExpression: null,
495
+ transitionHandlers: []
496
+ };
497
+ }
498
+ }
499
+ /**
500
+ * Handle undefined-to-defined transitions during field hydration
501
+ *
502
+ * This method creates handlers for the transition from undefined to defined
503
+ * values as fields are hydrated over time.
504
+ */
505
+ handleUndefinedToDefinedTransitions(expression, hydrationStates, context) {
506
+ try {
507
+ const kubernetesRefs = this.extractKubernetesRefs(expression);
508
+ const transitionPlan = this.createTransitionPlan(kubernetesRefs, hydrationStates, context);
509
+ return {
510
+ transitionPlan,
511
+ phaseExpressions: this.generatePhaseExpressions(expression, transitionPlan, context),
512
+ watchExpressions: this.generateWatchExpressions(transitionPlan, context),
513
+ fallbackExpressions: this.generateFallbackExpressions(expression, transitionPlan, context),
514
+ valid: true,
515
+ errors: []
516
+ };
517
+ }
518
+ catch (error) {
519
+ const transitionError = new ConversionError(`Failed to handle undefined-to-defined transitions: ${error instanceof Error ? error.message : String(error)}`, String(expression), 'unknown');
520
+ return {
521
+ transitionPlan: { phases: [], totalDuration: 0, criticalFields: [] },
522
+ phaseExpressions: new Map(),
523
+ watchExpressions: [],
524
+ fallbackExpressions: new Map(),
525
+ valid: false,
526
+ errors: [transitionError]
527
+ };
528
+ }
529
+ }
530
+ /**
531
+ * Analyze hydration states for KubernetesRef objects
532
+ */
533
+ analyzeHydrationStates(kubernetesRefs, hydrationStates) {
534
+ const unhydratedRefs = [];
535
+ const hydratedRefs = [];
536
+ const hydratingRefs = [];
537
+ const failedRefs = [];
538
+ for (const ref of kubernetesRefs) {
539
+ const stateKey = `${ref.resourceId}:${ref.fieldPath}`;
540
+ const state = hydrationStates.get(stateKey);
541
+ if (!state) {
542
+ unhydratedRefs.push(ref);
543
+ }
544
+ else if (state.hydrationFailed) {
545
+ failedRefs.push(ref);
546
+ }
547
+ else if (state.isHydrated) {
548
+ hydratedRefs.push(ref);
549
+ }
550
+ else if (state.isHydrating) {
551
+ hydratingRefs.push(ref);
552
+ }
553
+ else {
554
+ unhydratedRefs.push(ref);
555
+ }
556
+ }
557
+ return {
558
+ unhydratedRefs,
559
+ hydratedRefs,
560
+ hydratingRefs,
561
+ failedRefs,
562
+ totalRefs: kubernetesRefs.length,
563
+ hydrationProgress: hydratedRefs.length / kubernetesRefs.length
564
+ };
565
+ }
566
+ /**
567
+ * Create transition plan for hydration phases
568
+ */
569
+ createTransitionPlan(kubernetesRefs, _hydrationStates, _context) {
570
+ const phases = [];
571
+ const criticalFields = [];
572
+ // Group fields by expected hydration timing
573
+ const immediateFields = [];
574
+ const earlyFields = [];
575
+ const lateFields = [];
576
+ for (const ref of kubernetesRefs) {
577
+ const fieldPath = ref.fieldPath || '';
578
+ if (ref.resourceId === '__schema__' || fieldPath.startsWith('metadata.') || fieldPath.startsWith('spec.')) {
579
+ immediateFields.push(ref);
580
+ }
581
+ else if (fieldPath.includes('ready') || fieldPath.includes('available') || fieldPath.includes('replicas')) {
582
+ earlyFields.push(ref);
583
+ if (fieldPath.includes('ready') || fieldPath.includes('available')) {
584
+ criticalFields.push(`${ref.resourceId}.${fieldPath}`);
585
+ }
586
+ }
587
+ else {
588
+ lateFields.push(ref);
589
+ }
590
+ }
591
+ // Create phases
592
+ if (immediateFields.length > 0) {
593
+ phases.push({
594
+ name: 'immediate',
595
+ fields: immediateFields,
596
+ expectedDuration: 0,
597
+ dependencies: [],
598
+ isCritical: false
599
+ });
600
+ }
601
+ if (earlyFields.length > 0) {
602
+ phases.push({
603
+ name: 'early',
604
+ fields: earlyFields,
605
+ expectedDuration: 5000, // 5 seconds
606
+ dependencies: immediateFields.map(ref => `${ref.resourceId}.${ref.fieldPath}`),
607
+ isCritical: true
608
+ });
609
+ }
610
+ if (lateFields.length > 0) {
611
+ phases.push({
612
+ name: 'late',
613
+ fields: lateFields,
614
+ expectedDuration: 30000, // 30 seconds
615
+ dependencies: [...immediateFields, ...earlyFields].map(ref => `${ref.resourceId}.${ref.fieldPath}`),
616
+ isCritical: false
617
+ });
618
+ }
619
+ const totalDuration = phases.reduce((sum, phase) => sum + phase.expectedDuration, 0);
620
+ return { phases, totalDuration, criticalFields };
621
+ }
622
+ /**
623
+ * Generate hydration transition handlers
624
+ */
625
+ generateHydrationTransitionHandlers(expression, hydrationAnalysis, context) {
626
+ const handlers = [];
627
+ // Handler for unhydrated -> hydrating transition
628
+ if (hydrationAnalysis.unhydratedRefs.length > 0) {
629
+ handlers.push({
630
+ fromState: 'unhydrated',
631
+ toState: 'hydrating',
632
+ triggerCondition: this.generateHydrationStartCondition(hydrationAnalysis.unhydratedRefs),
633
+ transitionExpression: this.generateHydrationStartExpression(expression, hydrationAnalysis.unhydratedRefs, context),
634
+ priority: 1
635
+ });
636
+ }
637
+ // Handler for hydrating -> hydrated transition
638
+ if (hydrationAnalysis.hydratingRefs.length > 0) {
639
+ handlers.push({
640
+ fromState: 'hydrating',
641
+ toState: 'hydrated',
642
+ triggerCondition: this.generateHydrationCompleteCondition(hydrationAnalysis.hydratingRefs),
643
+ transitionExpression: this.generateHydrationCompleteExpression(expression, hydrationAnalysis.hydratingRefs, context),
644
+ priority: 2
645
+ });
646
+ }
647
+ // Handler for hydration failure
648
+ if (hydrationAnalysis.failedRefs.length > 0) {
649
+ handlers.push({
650
+ fromState: 'hydrating',
651
+ toState: 'failed',
652
+ triggerCondition: this.generateHydrationFailureCondition(hydrationAnalysis.failedRefs),
653
+ transitionExpression: this.generateHydrationFailureExpression(expression, hydrationAnalysis.failedRefs, context),
654
+ priority: 3
655
+ });
656
+ }
657
+ return handlers;
658
+ }
659
+ /**
660
+ * Generate phase expressions for different hydration phases
661
+ */
662
+ generatePhaseExpressions(expression, transitionPlan, context) {
663
+ const phaseExpressions = new Map();
664
+ for (const phase of transitionPlan.phases) {
665
+ try {
666
+ const phaseExpression = this.generatePhaseSpecificExpression(expression, phase, context);
667
+ phaseExpressions.set(phase.name, phaseExpression);
668
+ }
669
+ catch (error) {
670
+ this.logger.warn(`Failed to generate expression for phase ${phase.name}`, error);
671
+ }
672
+ }
673
+ return phaseExpressions;
674
+ }
675
+ /**
676
+ * Generate watch expressions for monitoring hydration progress
677
+ */
678
+ generateWatchExpressions(transitionPlan, _context) {
679
+ const watchExpressions = [];
680
+ for (const phase of transitionPlan.phases) {
681
+ for (const field of phase.fields) {
682
+ const resourcePath = field.resourceId === '__schema__'
683
+ ? `schema.${field.fieldPath}`
684
+ : `resources.${field.resourceId}.${field.fieldPath}`;
685
+ const watchExpression = {
686
+ [CEL_EXPRESSION_BRAND]: true,
687
+ expression: `has(${resourcePath})`,
688
+ type: 'boolean'
689
+ };
690
+ watchExpressions.push(watchExpression);
691
+ }
692
+ }
693
+ return watchExpressions;
694
+ }
695
+ /**
696
+ * Generate fallback expressions for hydration failures
697
+ */
698
+ generateFallbackExpressions(_expression, transitionPlan, _context) {
699
+ const fallbackExpressions = new Map();
700
+ for (const phase of transitionPlan.phases) {
701
+ const fallbackExpression = {
702
+ [CEL_EXPRESSION_BRAND]: true,
703
+ expression: phase.isCritical ? 'false' : 'null',
704
+ type: phase.isCritical ? 'boolean' : 'null'
705
+ };
706
+ fallbackExpressions.set(phase.name, fallbackExpression);
707
+ }
708
+ return fallbackExpressions;
709
+ }
710
+ /**
711
+ * Generate phase-specific expression
712
+ */
713
+ generatePhaseSpecificExpression(_expression, phase, _context) {
714
+ // Generate expression that only uses fields available in this phase
715
+ const availableFields = phase.fields.map(field => {
716
+ const resourcePath = field.resourceId === '__schema__'
717
+ ? `schema.${field.fieldPath}`
718
+ : `resources.${field.resourceId}.${field.fieldPath}`;
719
+ return resourcePath;
720
+ });
721
+ // Create a simplified expression using only available fields
722
+ const phaseExpression = availableFields.length > 0
723
+ ? availableFields.join(' && ')
724
+ : 'true';
725
+ return {
726
+ [CEL_EXPRESSION_BRAND]: true,
727
+ expression: phaseExpression,
728
+ type: 'boolean'
729
+ };
730
+ }
731
+ /**
732
+ * Generate condition for hydration start
733
+ */
734
+ generateHydrationStartCondition(refs) {
735
+ const conditions = refs.map(ref => {
736
+ const resourcePath = ref.resourceId === '__schema__'
737
+ ? `schema.${ref.fieldPath}`
738
+ : `resources.${ref.resourceId}.${ref.fieldPath}`;
739
+ return `!has(${resourcePath})`;
740
+ });
741
+ return conditions.join(' && ');
742
+ }
743
+ /**
744
+ * Generate expression for hydration start
745
+ */
746
+ generateHydrationStartExpression(_expression, _refs, _context) {
747
+ return {
748
+ [CEL_EXPRESSION_BRAND]: true,
749
+ expression: 'null', // Return null while hydrating
750
+ type: 'null'
751
+ };
752
+ }
753
+ /**
754
+ * Generate condition for hydration complete
755
+ */
756
+ generateHydrationCompleteCondition(refs) {
757
+ const conditions = refs.map(ref => {
758
+ const resourcePath = ref.resourceId === '__schema__'
759
+ ? `schema.${ref.fieldPath}`
760
+ : `resources.${ref.resourceId}.${ref.fieldPath}`;
761
+ return `has(${resourcePath})`;
762
+ });
763
+ return conditions.join(' && ');
764
+ }
765
+ /**
766
+ * Generate expression for hydration complete
767
+ */
768
+ generateHydrationCompleteExpression(expression, _refs, context) {
769
+ // Use the original expression since all fields are now available
770
+ return this.convertToBasicCel(expression, context);
771
+ }
772
+ /**
773
+ * Generate condition for hydration failure
774
+ */
775
+ generateHydrationFailureCondition(_refs) {
776
+ // This would typically check for timeout or error conditions
777
+ return 'false'; // Placeholder
778
+ }
779
+ /**
780
+ * Generate expression for hydration failure
781
+ */
782
+ generateHydrationFailureExpression(_expression, _refs, _context) {
783
+ return {
784
+ [CEL_EXPRESSION_BRAND]: true,
785
+ expression: 'false', // Return false on failure
786
+ type: 'boolean'
787
+ };
788
+ }
789
+ /**
790
+ * Analyze a single KubernetesRef for optionality requirements
791
+ */
792
+ analyzeKubernetesRefOptionality(kubernetesRef, context) {
793
+ const isSchemaReference = kubernetesRef.resourceId === '__schema__';
794
+ const fieldPath = kubernetesRef.fieldPath || '';
795
+ // Enhanced types appear non-optional at compile time but may be undefined at runtime
796
+ const potentiallyUndefined = this.isPotentiallyUndefinedAtRuntime(kubernetesRef, context);
797
+ const requiresNullSafety = potentiallyUndefined && (context.conservativeNullSafety ?? true);
798
+ // Check if optional chaining was used in the original expression
799
+ const hasOptionalChaining = this.hasOptionalChainingInExpression(kubernetesRef, context);
800
+ const confidence = this.calculateOptionalityConfidence(kubernetesRef, context);
801
+ const reason = this.determineOptionalityReason(kubernetesRef, context);
802
+ const suggestedCelPattern = requiresNullSafety
803
+ ? this.generateSuggestedCelPattern(kubernetesRef, context)
804
+ : undefined;
805
+ return {
806
+ kubernetesRef,
807
+ potentiallyUndefined,
808
+ requiresNullSafety,
809
+ hasOptionalChaining,
810
+ fieldPath,
811
+ resourceId: kubernetesRef.resourceId,
812
+ isSchemaReference,
813
+ confidence,
814
+ reason,
815
+ suggestedCelPattern
816
+ };
817
+ }
818
+ /**
819
+ * Determine if a KubernetesRef is potentially undefined at runtime
820
+ */
821
+ isPotentiallyUndefinedAtRuntime(kubernetesRef, context) {
822
+ // Schema references are generally available, but some schema fields might be optional
823
+ if (kubernetesRef.resourceId === '__schema__') {
824
+ return this.isSchemaFieldPotentiallyUndefined(kubernetesRef, context);
825
+ }
826
+ // Resource status fields are potentially undefined during field hydration
827
+ if (kubernetesRef.fieldPath?.startsWith('status.')) {
828
+ return this.isStatusFieldPotentiallyUndefined(kubernetesRef, context);
829
+ }
830
+ // Resource spec fields might be optional
831
+ if (kubernetesRef.fieldPath?.startsWith('spec.')) {
832
+ return this.isSpecFieldPotentiallyUndefined(kubernetesRef, context);
833
+ }
834
+ // Resource metadata fields are generally available but some might be optional
835
+ if (kubernetesRef.fieldPath?.startsWith('metadata.')) {
836
+ return this.isMetadataFieldPotentiallyUndefined(kubernetesRef, context);
837
+ }
838
+ // Check hydration state if available
839
+ if (context.hydrationStates) {
840
+ const stateKey = `${kubernetesRef.resourceId}:${kubernetesRef.fieldPath}`;
841
+ const state = context.hydrationStates.get(stateKey);
842
+ if (state) {
843
+ return !state.isHydrated || state.hydrationFailed;
844
+ }
845
+ }
846
+ // Conservative approach: assume potentially undefined for Enhanced types
847
+ return true;
848
+ }
849
+ /**
850
+ * Check if a schema field is potentially undefined
851
+ */
852
+ isSchemaFieldPotentiallyUndefined(kubernetesRef, context) {
853
+ const fieldPath = kubernetesRef.fieldPath || '';
854
+ // Common optional schema fields
855
+ const commonOptionalFields = [
856
+ 'metadata.labels',
857
+ 'metadata.annotations',
858
+ 'metadata.namespace',
859
+ 'spec.replicas',
860
+ 'spec.resources',
861
+ 'spec.nodeSelector',
862
+ 'spec.tolerations',
863
+ 'spec.affinity'
864
+ ];
865
+ // Check if this is a commonly optional field
866
+ if (commonOptionalFields.some(optional => fieldPath.startsWith(optional))) {
867
+ return true;
868
+ }
869
+ // Check for array access which might be undefined
870
+ if (fieldPath.includes('[') || fieldPath.includes('.length')) {
871
+ return true;
872
+ }
873
+ // Schema fields are generally available, but be conservative
874
+ return context.conservativeNullSafety ?? true;
875
+ }
876
+ /**
877
+ * Check if a status field is potentially undefined
878
+ */
879
+ isStatusFieldPotentiallyUndefined(kubernetesRef, _context) {
880
+ const fieldPath = kubernetesRef.fieldPath || '';
881
+ // Status fields are almost always potentially undefined during hydration
882
+ const alwaysUndefinedStatusFields = [
883
+ 'status.conditions',
884
+ 'status.loadBalancer',
885
+ 'status.ingress',
886
+ 'status.podIP',
887
+ 'status.hostIP',
888
+ 'status.phase',
889
+ 'status.readyReplicas',
890
+ 'status.availableReplicas',
891
+ 'status.observedGeneration'
892
+ ];
893
+ // Check if this is a field that's commonly undefined
894
+ if (alwaysUndefinedStatusFields.some(field => fieldPath.startsWith(field))) {
895
+ return true;
896
+ }
897
+ // All status fields are potentially undefined during field hydration
898
+ return true;
899
+ }
900
+ /**
901
+ * Check if a spec field is potentially undefined
902
+ */
903
+ isSpecFieldPotentiallyUndefined(kubernetesRef, context) {
904
+ const fieldPath = kubernetesRef.fieldPath || '';
905
+ // Common optional spec fields
906
+ const commonOptionalSpecFields = [
907
+ 'spec.replicas',
908
+ 'spec.resources',
909
+ 'spec.nodeSelector',
910
+ 'spec.tolerations',
911
+ 'spec.affinity',
912
+ 'spec.volumes',
913
+ 'spec.volumeMounts',
914
+ 'spec.env',
915
+ 'spec.ports',
916
+ 'spec.selector'
917
+ ];
918
+ // Check if this is a commonly optional spec field
919
+ if (commonOptionalSpecFields.some(optional => fieldPath.startsWith(optional))) {
920
+ return true;
921
+ }
922
+ // Check for array access
923
+ if (fieldPath.includes('[') || fieldPath.includes('.length')) {
924
+ return true;
925
+ }
926
+ // Most spec fields are required, but be conservative for Enhanced types
927
+ return context.conservativeNullSafety ?? false;
928
+ }
929
+ /**
930
+ * Check if a metadata field is potentially undefined
931
+ */
932
+ isMetadataFieldPotentiallyUndefined(kubernetesRef, context) {
933
+ const fieldPath = kubernetesRef.fieldPath || '';
934
+ // Common optional metadata fields
935
+ const commonOptionalMetadataFields = [
936
+ 'metadata.labels',
937
+ 'metadata.annotations',
938
+ 'metadata.namespace',
939
+ 'metadata.ownerReferences',
940
+ 'metadata.finalizers'
941
+ ];
942
+ // Check if this is a commonly optional metadata field
943
+ if (commonOptionalMetadataFields.some(optional => fieldPath.startsWith(optional))) {
944
+ return true;
945
+ }
946
+ // Core metadata fields like name and uid are generally available
947
+ const coreMetadataFields = [
948
+ 'metadata.name',
949
+ 'metadata.uid',
950
+ 'metadata.creationTimestamp',
951
+ 'metadata.generation'
952
+ ];
953
+ if (coreMetadataFields.some(core => fieldPath.startsWith(core))) {
954
+ return false;
955
+ }
956
+ // Be conservative for other metadata fields
957
+ return context.conservativeNullSafety ?? true;
958
+ }
959
+ /**
960
+ * Check if optional chaining was used in the original expression
961
+ */
962
+ hasOptionalChainingInExpression(_kubernetesRef, _context) {
963
+ // This would need to be determined from the original expression AST
964
+ // For now, return false as a placeholder
965
+ return false;
966
+ }
967
+ /**
968
+ * Calculate confidence level for optionality analysis
969
+ */
970
+ calculateOptionalityConfidence(kubernetesRef, context) {
971
+ let confidence = 0.8; // Base confidence
972
+ // Higher confidence for schema references
973
+ if (kubernetesRef.resourceId === '__schema__') {
974
+ confidence += 0.1;
975
+ }
976
+ // Lower confidence for status fields (more likely to be undefined)
977
+ if (kubernetesRef.fieldPath?.startsWith('status.')) {
978
+ confidence -= 0.2;
979
+ }
980
+ // Higher confidence if we have hydration state information
981
+ if (context.hydrationStates) {
982
+ confidence += 0.1;
983
+ }
984
+ return Math.max(0, Math.min(1, confidence));
985
+ }
986
+ /**
987
+ * Determine the reason for optionality determination
988
+ */
989
+ determineOptionalityReason(kubernetesRef, _context) {
990
+ if (kubernetesRef.resourceId === '__schema__') {
991
+ return 'Schema reference - generally available';
992
+ }
993
+ if (kubernetesRef.fieldPath?.startsWith('status.')) {
994
+ return 'Status field - potentially undefined during field hydration';
995
+ }
996
+ return 'Enhanced type - appears non-optional at compile time but may be undefined at runtime';
997
+ }
998
+ /**
999
+ * Generate suggested CEL pattern for null-safety
1000
+ */
1001
+ generateSuggestedCelPattern(kubernetesRef, context) {
1002
+ const resourcePath = kubernetesRef.resourceId === '__schema__'
1003
+ ? `schema.${kubernetesRef.fieldPath}`
1004
+ : `resources.${kubernetesRef.resourceId}.${kubernetesRef.fieldPath}`;
1005
+ if (context.generateHasChecks) {
1006
+ return `has(${resourcePath}) && ${resourcePath}`;
1007
+ }
1008
+ if (context.useKroConditionals) {
1009
+ return `${resourcePath}?`;
1010
+ }
1011
+ return resourcePath;
1012
+ }
1013
+ /**
1014
+ * Extract KubernetesRef objects from an expression
1015
+ */
1016
+ extractKubernetesRefs(expression) {
1017
+ const refs = [];
1018
+ if (isKubernetesRef(expression)) {
1019
+ refs.push(expression);
1020
+ }
1021
+ else if (Array.isArray(expression)) {
1022
+ for (const item of expression) {
1023
+ refs.push(...this.extractKubernetesRefs(item));
1024
+ }
1025
+ }
1026
+ else if (expression && typeof expression === 'object') {
1027
+ for (const value of Object.values(expression)) {
1028
+ refs.push(...this.extractKubernetesRefs(value));
1029
+ }
1030
+ }
1031
+ return refs;
1032
+ }
1033
+ /**
1034
+ * Convert expression to basic CEL without null-safety
1035
+ */
1036
+ convertToBasicCel(expression, _context) {
1037
+ // This is a placeholder - would need to integrate with the main analyzer
1038
+ return {
1039
+ [CEL_EXPRESSION_BRAND]: true,
1040
+ expression: String(expression),
1041
+ type: 'unknown'
1042
+ };
1043
+ }
1044
+ /**
1045
+ * Generate CEL expressions with has() checks for potentially undefined fields
1046
+ *
1047
+ * This method creates comprehensive CEL expressions that include has() checks
1048
+ * for all potentially undefined fields in the expression.
1049
+ */
1050
+ generateCelWithHasChecks(expression, optionalityResults, context) {
1051
+ try {
1052
+ const fieldsRequiringChecks = optionalityResults.filter(result => result.requiresNullSafety);
1053
+ if (fieldsRequiringChecks.length === 0) {
1054
+ return this.convertToBasicCel(expression, context);
1055
+ }
1056
+ // Generate has() checks for each field
1057
+ const hasChecks = this.generateHasChecksForFields(fieldsRequiringChecks, context);
1058
+ // Generate the main expression
1059
+ const mainExpression = this.convertExpressionWithKubernetesRefs(expression, optionalityResults, context);
1060
+ // Combine has() checks with the main expression
1061
+ const combinedExpression = this.combineHasChecksWithExpression(hasChecks, mainExpression, context);
1062
+ return {
1063
+ [CEL_EXPRESSION_BRAND]: true,
1064
+ expression: combinedExpression,
1065
+ type: this.inferExpressionType(expression, context)
1066
+ };
1067
+ }
1068
+ catch (error) {
1069
+ this.logger.error('Failed to generate CEL with has() checks', error);
1070
+ return this.convertToBasicCel(expression, context);
1071
+ }
1072
+ }
1073
+ /**
1074
+ * Generate has() checks for fields that require null-safety
1075
+ */
1076
+ generateHasChecksForFields(fieldsRequiringChecks, context) {
1077
+ const hasChecks = [];
1078
+ const processedPaths = new Set();
1079
+ for (const field of fieldsRequiringChecks) {
1080
+ const resourcePath = field.isSchemaReference
1081
+ ? `schema.${field.fieldPath}`
1082
+ : `resources.${field.resourceId}.${field.fieldPath}`;
1083
+ // Avoid duplicate checks for the same path
1084
+ if (processedPaths.has(resourcePath)) {
1085
+ continue;
1086
+ }
1087
+ processedPaths.add(resourcePath);
1088
+ // Generate nested has() checks for complex field paths
1089
+ const nestedChecks = this.generateNestedHasChecks(field, context);
1090
+ hasChecks.push(...nestedChecks);
1091
+ }
1092
+ return hasChecks;
1093
+ }
1094
+ /**
1095
+ * Generate nested has() checks for complex field paths
1096
+ */
1097
+ generateNestedHasChecks(field, _context) {
1098
+ const checks = [];
1099
+ const fieldPath = field.fieldPath;
1100
+ if (!fieldPath || !fieldPath.includes('.')) {
1101
+ // Simple field path
1102
+ const resourcePath = field.isSchemaReference
1103
+ ? `schema.${fieldPath}`
1104
+ : `resources.${field.resourceId}.${fieldPath}`;
1105
+ checks.push(`has(${resourcePath})`);
1106
+ return checks;
1107
+ }
1108
+ // Complex field path - check each level
1109
+ const pathParts = fieldPath.split('.');
1110
+ const basePrefix = field.isSchemaReference ? 'schema' : `resources.${field.resourceId}`;
1111
+ for (let i = 0; i < pathParts.length; i++) {
1112
+ const partialPath = pathParts.slice(0, i + 1).join('.');
1113
+ const fullPath = `${basePrefix}.${partialPath}`;
1114
+ // Skip checks for array indices
1115
+ if (!partialPath.includes('[') && !partialPath.includes(']')) {
1116
+ checks.push(`has(${fullPath})`);
1117
+ }
1118
+ }
1119
+ return checks;
1120
+ }
1121
+ /**
1122
+ * Convert expression with KubernetesRef objects to CEL
1123
+ */
1124
+ convertExpressionWithKubernetesRefs(expression, optionalityResults, context) {
1125
+ // This is a simplified conversion - in a real implementation,
1126
+ // this would integrate with the main expression analyzer
1127
+ if (isKubernetesRef(expression)) {
1128
+ const result = optionalityResults.find(r => r.kubernetesRef === expression);
1129
+ if (result) {
1130
+ return result.isSchemaReference
1131
+ ? `schema.${result.fieldPath}`
1132
+ : `resources.${result.resourceId}.${result.fieldPath}`;
1133
+ }
1134
+ }
1135
+ // Handle different expression types
1136
+ if (typeof expression === 'string') {
1137
+ return `"${expression}"`;
1138
+ }
1139
+ if (typeof expression === 'number') {
1140
+ return String(expression);
1141
+ }
1142
+ if (typeof expression === 'boolean') {
1143
+ return String(expression);
1144
+ }
1145
+ if (Array.isArray(expression)) {
1146
+ const elements = expression.map(item => this.convertExpressionWithKubernetesRefs(item, optionalityResults, context));
1147
+ return `[${elements.join(', ')}]`;
1148
+ }
1149
+ if (expression && typeof expression === 'object') {
1150
+ // Handle object expressions
1151
+ const properties = Object.entries(expression).map(([key, value]) => {
1152
+ const convertedValue = this.convertExpressionWithKubernetesRefs(value, optionalityResults, context);
1153
+ return `"${key}": ${convertedValue}`;
1154
+ });
1155
+ return `{${properties.join(', ')}}`;
1156
+ }
1157
+ return String(expression);
1158
+ }
1159
+ /**
1160
+ * Combine has() checks with the main expression
1161
+ */
1162
+ combineHasChecksWithExpression(hasChecks, mainExpression, _context) {
1163
+ if (hasChecks.length === 0) {
1164
+ return mainExpression;
1165
+ }
1166
+ // Remove duplicate checks
1167
+ const uniqueChecks = Array.from(new Set(hasChecks));
1168
+ // Combine all checks with AND operator
1169
+ const allChecks = uniqueChecks.join(' && ');
1170
+ // Combine checks with the main expression
1171
+ return `${allChecks} && ${mainExpression}`;
1172
+ }
1173
+ /**
1174
+ * Infer the type of the expression result
1175
+ */
1176
+ inferExpressionType(expression, _context) {
1177
+ if (typeof expression === 'string') {
1178
+ return 'string';
1179
+ }
1180
+ if (typeof expression === 'number') {
1181
+ return 'number';
1182
+ }
1183
+ if (typeof expression === 'boolean') {
1184
+ return 'boolean';
1185
+ }
1186
+ if (Array.isArray(expression)) {
1187
+ return 'array';
1188
+ }
1189
+ if (expression && typeof expression === 'object') {
1190
+ return 'object';
1191
+ }
1192
+ return 'unknown';
1193
+ }
1194
+ /**
1195
+ * Generate null-safe CEL expression
1196
+ */
1197
+ generateNullSafeExpression(expression, optionalityResults, context) {
1198
+ // Use the enhanced has() check generation
1199
+ return this.generateCelWithHasChecks(expression, optionalityResults, context);
1200
+ }
1201
+ /**
1202
+ * Generate source mapping for debugging
1203
+ */
1204
+ generateSourceMapping(originalExpression, celExpression, context) {
1205
+ if (!context.sourceMap) {
1206
+ return [];
1207
+ }
1208
+ return [{
1209
+ originalExpression: String(originalExpression),
1210
+ celExpression: celExpression.expression,
1211
+ sourceLocation: {
1212
+ line: 0,
1213
+ column: 0,
1214
+ length: String(originalExpression).length
1215
+ },
1216
+ context: 'status',
1217
+ id: `optionality-${Date.now()}`,
1218
+ timestamp: Date.now()
1219
+ }];
1220
+ }
1221
+ /**
1222
+ * Generate pre-hydration expression (for unhydrated fields)
1223
+ */
1224
+ generatePreHydrationExpression(_expression, _unhydratedRefs, _context) {
1225
+ // For pre-hydration, return a safe default or null check
1226
+ return {
1227
+ [CEL_EXPRESSION_BRAND]: true,
1228
+ expression: 'false', // Safe default before hydration
1229
+ type: 'boolean'
1230
+ };
1231
+ }
1232
+ /**
1233
+ * Generate post-hydration expression (for hydrated fields)
1234
+ */
1235
+ generatePostHydrationExpression(expression, _hydratedRefs, context) {
1236
+ // For post-hydration, can use the fields directly
1237
+ return this.convertToBasicCel(expression, context);
1238
+ }
1239
+ /**
1240
+ * Generate hydration-dependent expression (for fields being hydrated)
1241
+ */
1242
+ generateHydrationDependentExpression(_expression, hydratingRefs, _context) {
1243
+ // For fields being hydrated, use conditional checks
1244
+ const conditionalChecks = hydratingRefs.map(ref => {
1245
+ const resourcePath = ref.resourceId === '__schema__'
1246
+ ? `schema.${ref.fieldPath}`
1247
+ : `resources.${ref.resourceId}.${ref.fieldPath}`;
1248
+ return `has(${resourcePath})`;
1249
+ }).join(' && ');
1250
+ return {
1251
+ [CEL_EXPRESSION_BRAND]: true,
1252
+ expression: conditionalChecks,
1253
+ type: 'boolean'
1254
+ };
1255
+ }
1256
+ }
1257
+ /**
1258
+ * Convenience function to analyze optionality requirements
1259
+ */
1260
+ export function analyzeOptionalityRequirements(expression, context, options) {
1261
+ const handler = new EnhancedTypeOptionalityHandler(options);
1262
+ return handler.analyzeOptionalityRequirements(expression, context);
1263
+ }
1264
+ /**
1265
+ * Convenience function to generate null-safe CEL expressions
1266
+ */
1267
+ export function generateNullSafeCelExpression(expression, optionalityResults, context, options) {
1268
+ const handler = new EnhancedTypeOptionalityHandler(options);
1269
+ return handler.generateNullSafeCelExpression(expression, optionalityResults, context);
1270
+ }
1271
+ /**
1272
+ * Convenience function to handle optional chaining with Enhanced types
1273
+ */
1274
+ export function handleOptionalChainingWithEnhancedTypes(expression, context, options) {
1275
+ const handler = new EnhancedTypeOptionalityHandler(options);
1276
+ return handler.handleOptionalChainingWithEnhancedTypes(expression, context);
1277
+ }
1278
+ /**
1279
+ * Convenience function to generate CEL expressions with has() checks
1280
+ */
1281
+ export function generateCelWithHasChecks(expression, optionalityResults, context, options) {
1282
+ const handler = new EnhancedTypeOptionalityHandler(options);
1283
+ return handler.generateCelWithHasChecks(expression, optionalityResults, context);
1284
+ }
1285
+ /**
1286
+ * Convenience function to detect null-safety requirements for Enhanced types
1287
+ */
1288
+ export function detectNullSafetyRequirements(enhancedResources, context, options) {
1289
+ const handler = new EnhancedTypeOptionalityHandler(options);
1290
+ return handler.detectNullSafetyRequirements(enhancedResources, context);
1291
+ }
1292
+ /**
1293
+ * Convenience function to integrate with field hydration timing
1294
+ */
1295
+ export function integrateWithFieldHydrationTiming(expression, hydrationStates, context, options) {
1296
+ const handler = new EnhancedTypeOptionalityHandler(options);
1297
+ return handler.integrateWithFieldHydrationTiming(expression, hydrationStates, context);
1298
+ }
1299
+ /**
1300
+ * Convenience function to handle undefined-to-defined transitions
1301
+ */
1302
+ export function handleUndefinedToDefinedTransitions(expression, hydrationStates, context, options) {
1303
+ const handler = new EnhancedTypeOptionalityHandler(options);
1304
+ return handler.handleUndefinedToDefinedTransitions(expression, hydrationStates, context);
1305
+ }
1306
+ //# sourceMappingURL=optionality-handler.js.map