typekro 0.2.1 → 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,1086 @@
1
+ /**
2
+ * Resource Builder Integration for JavaScript to CEL Expression Conversion
3
+ *
4
+ * This module provides integration with TypeKro's existing KubernetesRef and magic proxy systems
5
+ * for analyzing resource configurations that contain JavaScript expressions with KubernetesRef objects.
6
+ *
7
+ * The analyzer detects when resource builders use expressions that depend on other resources
8
+ * and converts them to appropriate CEL expressions based on the factory pattern.
9
+ */
10
+ import { containsKubernetesRefs, isKubernetesRef } from '../../utils/type-guards.js';
11
+ import { ConversionError } from '../errors.js';
12
+ import { JavaScriptToCelAnalyzer, } from './analyzer.js';
13
+ import { SourceMapBuilder } from './source-map.js';
14
+ /**
15
+ * Analyzes resource configurations for JavaScript expressions containing KubernetesRef objects
16
+ */
17
+ export class ResourceAnalyzer {
18
+ analyzer;
19
+ dependencyTracker;
20
+ typeValidator;
21
+ constructor() {
22
+ this.analyzer = new JavaScriptToCelAnalyzer();
23
+ this.dependencyTracker = new DependencyTracker();
24
+ this.typeValidator = new ResourceTypeValidator();
25
+ }
26
+ /**
27
+ * Analyze a resource configuration for KubernetesRef-dependent expressions
28
+ * This is the main entry point for resource builder integration
29
+ */
30
+ analyzeResourceConfig(resourceId, config, context) {
31
+ const result = {
32
+ originalConfig: config,
33
+ processedConfig: {},
34
+ dependencies: [],
35
+ convertedFields: [],
36
+ errors: [],
37
+ requiresConversion: false,
38
+ circularDependencies: [],
39
+ typeValidationResults: []
40
+ };
41
+ try {
42
+ // Deep analyze the configuration object
43
+ result.processedConfig = this.analyzeConfigObject(config, '', context, result);
44
+ // Track dependencies automatically
45
+ const _dependencyInfos = this.dependencyTracker.trackDependencies(resourceId, result.dependencies, result.convertedFields, {
46
+ trackSchemaDependencies: true,
47
+ trackResourceDependencies: true,
48
+ trackExternalDependencies: true,
49
+ detectCircularDependencies: true,
50
+ computeDeploymentOrder: true
51
+ });
52
+ // Get circular dependencies from tracker
53
+ result.circularDependencies = this.dependencyTracker.getDependencyGraph().circularChains;
54
+ // Validate resource types if requested
55
+ if (context.validateResourceTypes) {
56
+ result.typeValidationResults = this.validateResourceTypes(result.dependencies, context);
57
+ }
58
+ // Set overall conversion flag
59
+ result.requiresConversion = result.convertedFields.length > 0;
60
+ }
61
+ catch (error) {
62
+ result.errors.push(new ConversionError(`Failed to analyze resource config: ${error instanceof Error ? error.message : String(error)}`, JSON.stringify(config), 'unknown'));
63
+ }
64
+ return result;
65
+ }
66
+ /**
67
+ * Recursively analyze a configuration object for KubernetesRef objects
68
+ */
69
+ analyzeConfigObject(obj, fieldPath, context, result) {
70
+ if (obj === null || obj === undefined) {
71
+ return obj;
72
+ }
73
+ // Check if this value contains KubernetesRef objects
74
+ if (!containsKubernetesRefs(obj)) {
75
+ // No KubernetesRef objects - return as-is for performance
76
+ return obj;
77
+ }
78
+ // Handle direct KubernetesRef objects
79
+ if (isKubernetesRef(obj)) {
80
+ return this.convertKubernetesRefInConfig(obj, fieldPath, context, result);
81
+ }
82
+ // Handle arrays
83
+ if (Array.isArray(obj)) {
84
+ return obj.map((item, index) => this.analyzeConfigObject(item, `${fieldPath}[${index}]`, context, result));
85
+ }
86
+ // Handle objects
87
+ if (typeof obj === 'object') {
88
+ const processedObj = {};
89
+ for (const [key, value] of Object.entries(obj)) {
90
+ const currentFieldPath = fieldPath ? `${fieldPath}.${key}` : key;
91
+ processedObj[key] = this.analyzeConfigObject(value, currentFieldPath, context, result);
92
+ }
93
+ return processedObj;
94
+ }
95
+ // Handle primitive values that might be expressions
96
+ if (typeof obj === 'string' && this.looksLikeExpression(obj)) {
97
+ return this.analyzeStringExpression(obj, fieldPath, context, result);
98
+ }
99
+ return obj;
100
+ }
101
+ /**
102
+ * Convert a KubernetesRef object in a resource configuration
103
+ */
104
+ convertKubernetesRefInConfig(ref, fieldPath, context, result) {
105
+ try {
106
+ // Track this dependency
107
+ result.dependencies.push(ref);
108
+ result.convertedFields.push(fieldPath);
109
+ // Create analysis context for this KubernetesRef
110
+ const analysisContext = {
111
+ type: 'resource',
112
+ availableReferences: context.availableResources || {},
113
+ factoryType: context.factoryType,
114
+ sourceMap: new SourceMapBuilder(),
115
+ dependencies: [],
116
+ ...(context.schemaProxy && { schemaProxy: context.schemaProxy })
117
+ };
118
+ // Convert the KubernetesRef to CEL
119
+ const celExpression = this.analyzer.convertKubernetesRefToCel(ref, analysisContext);
120
+ // Add any additional dependencies found during conversion
121
+ result.dependencies.push(...(analysisContext.dependencies || []));
122
+ return celExpression;
123
+ }
124
+ catch (error) {
125
+ const conversionError = new ConversionError(`Failed to convert KubernetesRef at ${fieldPath}: ${error instanceof Error ? error.message : String(error)}`, `${ref.resourceId}.${ref.fieldPath}`, 'member-access');
126
+ result.errors.push(conversionError);
127
+ // Return the original ref on error
128
+ return ref;
129
+ }
130
+ }
131
+ /**
132
+ * Analyze string expressions that might contain KubernetesRef objects
133
+ */
134
+ analyzeStringExpression(expression, fieldPath, context, result) {
135
+ try {
136
+ // Create analysis context
137
+ const analysisContext = {
138
+ type: 'resource',
139
+ availableReferences: context.availableResources || {},
140
+ factoryType: context.factoryType,
141
+ sourceMap: new SourceMapBuilder(),
142
+ dependencies: [],
143
+ sourceText: expression,
144
+ ...(context.schemaProxy && { schemaProxy: context.schemaProxy })
145
+ };
146
+ // Analyze the expression
147
+ const conversionResult = this.analyzer.analyzeExpression(expression, analysisContext);
148
+ if (conversionResult.valid && conversionResult.requiresConversion) {
149
+ // Track dependencies and converted fields
150
+ result.dependencies.push(...conversionResult.dependencies);
151
+ result.convertedFields.push(fieldPath);
152
+ result.errors.push(...conversionResult.errors);
153
+ return conversionResult.celExpression;
154
+ }
155
+ // No conversion needed or failed - return original
156
+ result.errors.push(...conversionResult.errors);
157
+ return expression;
158
+ }
159
+ catch (error) {
160
+ const conversionError = new ConversionError(`Failed to analyze expression at ${fieldPath}: ${error instanceof Error ? error.message : String(error)}`, expression, 'javascript');
161
+ result.errors.push(conversionError);
162
+ return expression;
163
+ }
164
+ }
165
+ /**
166
+ * Check if a string looks like a JavaScript expression
167
+ */
168
+ looksLikeExpression(str) {
169
+ // Look for common expression patterns
170
+ return (str.includes('${') || // Template literals
171
+ str.includes('?.') || // Optional chaining
172
+ str.includes('||') || // Logical OR
173
+ str.includes('&&') || // Logical AND
174
+ str.includes('??') || // Nullish coalescing
175
+ /\w+\.\w+/.test(str) // Property access
176
+ );
177
+ }
178
+ /**
179
+ * Validate resource types for KubernetesRef objects
180
+ * This implements requirement 4.5: resource type validation
181
+ */
182
+ validateResourceTypes(dependencies, context) {
183
+ const validationContext = {
184
+ strictTypeChecking: context.validateResourceTypes || false,
185
+ ...(context.availableResources && { availableResources: context.availableResources }),
186
+ ...(context.schemaProxy && { schemaProxy: context.schemaProxy })
187
+ };
188
+ return dependencies.map(dep => this.typeValidator.validateKubernetesRef(dep, validationContext));
189
+ }
190
+ /**
191
+ * Get dependency information for a resource
192
+ */
193
+ getDependencyInfo(resourceId) {
194
+ return this.dependencyTracker.getDependencies(resourceId);
195
+ }
196
+ /**
197
+ * Get the full dependency graph
198
+ */
199
+ getDependencyGraph() {
200
+ return this.dependencyTracker.getDependencyGraph();
201
+ }
202
+ /**
203
+ * Get resources that depend on a specific resource
204
+ */
205
+ getDependents(resourceId) {
206
+ return this.dependencyTracker.getDependents(resourceId);
207
+ }
208
+ /**
209
+ * Check if there are circular dependencies
210
+ */
211
+ hasCircularDependencies() {
212
+ return this.dependencyTracker.hasCircularDependencies();
213
+ }
214
+ /**
215
+ * Get the recommended deployment order
216
+ */
217
+ getDeploymentOrder() {
218
+ return this.dependencyTracker.getDeploymentOrder();
219
+ }
220
+ /**
221
+ * Perform advanced circular dependency analysis
222
+ * This provides detailed analysis and recommendations for resolving circular dependencies
223
+ */
224
+ analyzeCircularDependencies() {
225
+ return this.dependencyTracker.detectCircularDependencyChains();
226
+ }
227
+ /**
228
+ * Get the resource type validator
229
+ */
230
+ getTypeValidator() {
231
+ return this.typeValidator;
232
+ }
233
+ /**
234
+ * Register a custom schema validator
235
+ */
236
+ registerSchemaValidator(schemaType, validator) {
237
+ this.typeValidator.registerSchemaValidator(schemaType, validator);
238
+ }
239
+ /**
240
+ * Register a custom resource type
241
+ */
242
+ registerResourceType(name, typeInfo) {
243
+ this.typeValidator.registerResourceType(name, typeInfo);
244
+ }
245
+ /**
246
+ * Validate a single KubernetesRef for type correctness
247
+ */
248
+ validateKubernetesRef(ref, context) {
249
+ return this.typeValidator.validateKubernetesRef(ref, context);
250
+ }
251
+ }
252
+ /**
253
+ * Convenience function for analyzing resource configurations
254
+ * This is the main entry point for resource builder integration
255
+ */
256
+ export function analyzeResourceConfig(resourceId, config, context) {
257
+ const analyzer = new ResourceAnalyzer();
258
+ const fullContext = {
259
+ ...context,
260
+ resourceId,
261
+ resourceConfig: config
262
+ };
263
+ return analyzer.analyzeResourceConfig(resourceId, config, fullContext);
264
+ }
265
+ /**
266
+ * Analyze resource configurations for factory function integration with KubernetesRef detection
267
+ * This function integrates with existing factory functions to detect and convert
268
+ * JavaScript expressions that contain KubernetesRef objects from the magic proxy system
269
+ */
270
+ export function analyzeFactoryResourceConfig(resourceId, config, availableResources, schemaProxy, factoryType = 'kro') {
271
+ const context = {
272
+ type: 'resource',
273
+ resourceId,
274
+ resourceConfig: config,
275
+ availableReferences: availableResources,
276
+ availableResources,
277
+ factoryType,
278
+ validateResourceTypes: true,
279
+ sourceMap: new SourceMapBuilder(),
280
+ ...(schemaProxy && { schemaProxy })
281
+ };
282
+ return analyzeResourceConfig(resourceId, config, context);
283
+ }
284
+ /**
285
+ * Automatic dependency tracker for KubernetesRef objects in expressions
286
+ * This implements requirement 4.2: automatic dependency tracking
287
+ */
288
+ export class DependencyTracker {
289
+ dependencyGraph;
290
+ constructor() {
291
+ this.dependencyGraph = {
292
+ dependencies: new Map(),
293
+ dependents: new Map(),
294
+ circularChains: [],
295
+ deploymentOrder: []
296
+ };
297
+ }
298
+ /**
299
+ * Track dependencies for a resource configuration
300
+ */
301
+ trackDependencies(resourceId, dependencies, fieldPaths, options = {}) {
302
+ const dependencyInfos = [];
303
+ for (let i = 0; i < dependencies.length; i++) {
304
+ const dep = dependencies[i];
305
+ if (!dep)
306
+ continue;
307
+ const fieldPath = fieldPaths[i] || `unknown[${i}]`;
308
+ const dependencyInfo = this.createDependencyInfo(dep, fieldPath, options);
309
+ dependencyInfos.push(dependencyInfo);
310
+ // Add to dependency graph
311
+ this.addToDependencyGraph(resourceId, dependencyInfo);
312
+ }
313
+ // Update dependency graph computations
314
+ if (options.detectCircularDependencies) {
315
+ this.detectCircularDependencies();
316
+ }
317
+ if (options.computeDeploymentOrder) {
318
+ this.computeDeploymentOrder();
319
+ }
320
+ return dependencyInfos;
321
+ }
322
+ /**
323
+ * Create dependency information for a KubernetesRef
324
+ */
325
+ createDependencyInfo(ref, fieldPath, options) {
326
+ const dependencyType = this.determineDependencyType(ref);
327
+ // Skip tracking based on options
328
+ if (dependencyType === 'schema' && options.trackSchemaDependencies === false) {
329
+ return this.createSkippedDependencyInfo(ref, fieldPath, dependencyType);
330
+ }
331
+ if (dependencyType === 'resource' && options.trackResourceDependencies === false) {
332
+ return this.createSkippedDependencyInfo(ref, fieldPath, dependencyType);
333
+ }
334
+ if (dependencyType === 'external' && options.trackExternalDependencies === false) {
335
+ return this.createSkippedDependencyInfo(ref, fieldPath, dependencyType);
336
+ }
337
+ return {
338
+ reference: ref,
339
+ fieldPath,
340
+ dependencyType,
341
+ required: this.isDependencyRequired(ref, fieldPath),
342
+ expectedType: ref._type ? String(ref._type) : 'unknown',
343
+ metadata: {
344
+ affectsReadiness: this.affectsReadiness(ref, fieldPath),
345
+ conditional: this.isConditional(fieldPath),
346
+ expressionContext: this.getExpressionContext(fieldPath)
347
+ }
348
+ };
349
+ }
350
+ /**
351
+ * Create a skipped dependency info (for disabled tracking)
352
+ */
353
+ createSkippedDependencyInfo(ref, fieldPath, dependencyType) {
354
+ return {
355
+ reference: ref,
356
+ fieldPath,
357
+ dependencyType,
358
+ required: false,
359
+ expectedType: 'skipped',
360
+ metadata: {
361
+ affectsReadiness: false,
362
+ conditional: false,
363
+ expressionContext: 'skipped'
364
+ }
365
+ };
366
+ }
367
+ /**
368
+ * Determine the type of dependency
369
+ */
370
+ determineDependencyType(ref) {
371
+ if (ref.resourceId === '__schema__') {
372
+ return 'schema';
373
+ }
374
+ // Check if it's a known resource type
375
+ if (ref.resourceId.match(/^[a-z][a-z0-9-]*$/)) {
376
+ return 'resource';
377
+ }
378
+ return 'external';
379
+ }
380
+ /**
381
+ * Determine if a dependency is required
382
+ */
383
+ isDependencyRequired(ref, fieldPath) {
384
+ // Schema dependencies are generally required
385
+ if (ref.resourceId === '__schema__') {
386
+ return true;
387
+ }
388
+ // Dependencies in required fields are required
389
+ if (this.isRequiredField(fieldPath)) {
390
+ return true;
391
+ }
392
+ // Dependencies in conditional expressions may not be required
393
+ if (this.isConditional(fieldPath)) {
394
+ return false;
395
+ }
396
+ // Default to required for safety
397
+ return true;
398
+ }
399
+ /**
400
+ * Check if a field path represents a required field
401
+ */
402
+ isRequiredField(fieldPath) {
403
+ // Common required fields
404
+ const requiredFields = ['name', 'image', 'namespace'];
405
+ return requiredFields.some(field => fieldPath.includes(field));
406
+ }
407
+ /**
408
+ * Check if a dependency affects resource readiness
409
+ */
410
+ affectsReadiness(ref, fieldPath) {
411
+ // Status field dependencies typically affect readiness
412
+ if (ref.fieldPath.startsWith('status.')) {
413
+ return true;
414
+ }
415
+ // Dependencies in readiness-related fields
416
+ const readinessFields = ['ready', 'available', 'replicas', 'conditions'];
417
+ return readinessFields.some(field => fieldPath.includes(field) || ref.fieldPath.includes(field));
418
+ }
419
+ /**
420
+ * Check if a field path is in a conditional context
421
+ */
422
+ isConditional(fieldPath) {
423
+ // Look for conditional patterns in field path
424
+ return fieldPath.includes('?') ||
425
+ fieldPath.includes('||') ||
426
+ fieldPath.includes('&&') ||
427
+ fieldPath.includes('??');
428
+ }
429
+ /**
430
+ * Get expression context for a field path
431
+ */
432
+ getExpressionContext(fieldPath) {
433
+ // Extract the top-level field context
434
+ const parts = fieldPath.split('.');
435
+ if (parts.length > 0 && parts[0]) {
436
+ return parts[0];
437
+ }
438
+ return 'unknown';
439
+ }
440
+ /**
441
+ * Add dependency info to the dependency graph
442
+ */
443
+ addToDependencyGraph(resourceId, dependencyInfo) {
444
+ // Add to dependencies map
445
+ if (!this.dependencyGraph.dependencies.has(resourceId)) {
446
+ this.dependencyGraph.dependencies.set(resourceId, []);
447
+ }
448
+ this.dependencyGraph.dependencies.get(resourceId)?.push(dependencyInfo);
449
+ // Add to dependents map (reverse mapping)
450
+ const dependentResourceId = dependencyInfo.reference.resourceId;
451
+ if (dependentResourceId !== '__schema__') {
452
+ if (!this.dependencyGraph.dependents.has(dependentResourceId)) {
453
+ this.dependencyGraph.dependents.set(dependentResourceId, []);
454
+ }
455
+ const dependents = this.dependencyGraph.dependents.get(dependentResourceId);
456
+ if (!dependents.includes(resourceId)) {
457
+ dependents.push(resourceId);
458
+ }
459
+ }
460
+ }
461
+ /**
462
+ * Detect circular dependencies in the dependency graph
463
+ */
464
+ detectCircularDependencies() {
465
+ this.dependencyGraph.circularChains = [];
466
+ const visited = new Set();
467
+ const recursionStack = new Set();
468
+ // Check each resource for cycles
469
+ for (const resourceId of this.dependencyGraph.dependencies.keys()) {
470
+ if (!visited.has(resourceId)) {
471
+ this.detectCyclesFromResource(resourceId, [], visited, recursionStack);
472
+ }
473
+ }
474
+ }
475
+ /**
476
+ * Detect cycles starting from a specific resource
477
+ */
478
+ detectCyclesFromResource(resourceId, path, visited, recursionStack) {
479
+ if (recursionStack.has(resourceId)) {
480
+ // Found a cycle
481
+ const cycleStart = path.indexOf(resourceId);
482
+ if (cycleStart >= 0) {
483
+ const cycle = path.slice(cycleStart).concat([resourceId]);
484
+ this.dependencyGraph.circularChains.push(cycle);
485
+ }
486
+ return;
487
+ }
488
+ if (visited.has(resourceId)) {
489
+ return;
490
+ }
491
+ visited.add(resourceId);
492
+ recursionStack.add(resourceId);
493
+ // Follow dependencies
494
+ const dependencies = this.dependencyGraph.dependencies.get(resourceId) || [];
495
+ for (const dep of dependencies) {
496
+ if (dep.reference.resourceId !== '__schema__') {
497
+ this.detectCyclesFromResource(dep.reference.resourceId, [...path, resourceId], visited, recursionStack);
498
+ }
499
+ }
500
+ recursionStack.delete(resourceId);
501
+ }
502
+ /**
503
+ * Compute deployment order using topological sort
504
+ */
505
+ computeDeploymentOrder() {
506
+ const inDegree = new Map();
507
+ const adjList = new Map();
508
+ // Initialize in-degree and adjacency list
509
+ for (const [resourceId, dependencies] of this.dependencyGraph.dependencies) {
510
+ if (!inDegree.has(resourceId)) {
511
+ inDegree.set(resourceId, 0);
512
+ }
513
+ if (!adjList.has(resourceId)) {
514
+ adjList.set(resourceId, []);
515
+ }
516
+ for (const dep of dependencies) {
517
+ if (dep.reference.resourceId !== '__schema__') {
518
+ const depResourceId = dep.reference.resourceId;
519
+ if (!inDegree.has(depResourceId)) {
520
+ inDegree.set(depResourceId, 0);
521
+ }
522
+ if (!adjList.has(depResourceId)) {
523
+ adjList.set(depResourceId, []);
524
+ }
525
+ // Add edge from dependency to dependent
526
+ adjList.get(depResourceId)?.push(resourceId);
527
+ inDegree.set(resourceId, inDegree.get(resourceId) + 1);
528
+ }
529
+ }
530
+ }
531
+ // Kahn's algorithm for topological sort
532
+ const queue = [];
533
+ const result = [];
534
+ // Find all resources with no dependencies
535
+ for (const [resourceId, degree] of inDegree) {
536
+ if (degree === 0) {
537
+ queue.push(resourceId);
538
+ }
539
+ }
540
+ while (queue.length > 0) {
541
+ const current = queue.shift();
542
+ result.push(current);
543
+ // Process all dependents
544
+ const dependents = adjList.get(current) || [];
545
+ for (const dependent of dependents) {
546
+ const newDegree = inDegree.get(dependent) - 1;
547
+ inDegree.set(dependent, newDegree);
548
+ if (newDegree === 0) {
549
+ queue.push(dependent);
550
+ }
551
+ }
552
+ }
553
+ this.dependencyGraph.deploymentOrder = result;
554
+ }
555
+ /**
556
+ * Get the current dependency graph
557
+ */
558
+ getDependencyGraph() {
559
+ return { ...this.dependencyGraph };
560
+ }
561
+ /**
562
+ * Get dependencies for a specific resource
563
+ */
564
+ getDependencies(resourceId) {
565
+ return this.dependencyGraph.dependencies.get(resourceId) || [];
566
+ }
567
+ /**
568
+ * Get resources that depend on a specific resource
569
+ */
570
+ getDependents(resourceId) {
571
+ return this.dependencyGraph.dependents.get(resourceId) || [];
572
+ }
573
+ /**
574
+ * Check if there are circular dependencies
575
+ */
576
+ hasCircularDependencies() {
577
+ return this.dependencyGraph.circularChains.length > 0;
578
+ }
579
+ /**
580
+ * Get the deployment order
581
+ */
582
+ getDeploymentOrder() {
583
+ return [...this.dependencyGraph.deploymentOrder];
584
+ }
585
+ /**
586
+ * Reset the dependency graph
587
+ */
588
+ reset() {
589
+ this.dependencyGraph = {
590
+ dependencies: new Map(),
591
+ dependents: new Map(),
592
+ circularChains: [],
593
+ deploymentOrder: []
594
+ };
595
+ }
596
+ /**
597
+ * Advanced circular dependency detection with detailed chain analysis
598
+ * This provides more sophisticated analysis of KubernetesRef chains
599
+ */
600
+ detectCircularDependencyChains() {
601
+ const analysis = {
602
+ hasCircularDependencies: false,
603
+ circularChains: [],
604
+ chainAnalysis: [],
605
+ recommendations: []
606
+ };
607
+ // Use Tarjan's strongly connected components algorithm for better cycle detection
608
+ const tarjanResult = this.findStronglyConnectedComponents();
609
+ for (const component of tarjanResult.components) {
610
+ if (component.length > 1) {
611
+ // This is a circular dependency
612
+ analysis.hasCircularDependencies = true;
613
+ analysis.circularChains.push(component);
614
+ // Analyze the chain
615
+ const chainAnalysis = this.analyzeCircularChain(component);
616
+ analysis.chainAnalysis.push(chainAnalysis);
617
+ // Generate recommendations
618
+ const recommendations = this.generateCircularDependencyRecommendations(chainAnalysis);
619
+ analysis.recommendations.push(...recommendations);
620
+ }
621
+ }
622
+ return analysis;
623
+ }
624
+ /**
625
+ * Find strongly connected components using Tarjan's algorithm
626
+ */
627
+ findStronglyConnectedComponents() {
628
+ const index = new Map();
629
+ const lowLink = new Map();
630
+ const onStack = new Set();
631
+ const stack = [];
632
+ const components = [];
633
+ let currentIndex = 0;
634
+ const strongConnect = (resourceId) => {
635
+ index.set(resourceId, currentIndex);
636
+ lowLink.set(resourceId, currentIndex);
637
+ currentIndex++;
638
+ stack.push(resourceId);
639
+ onStack.add(resourceId);
640
+ // Get dependencies for this resource
641
+ const dependencies = this.dependencyGraph.dependencies.get(resourceId) || [];
642
+ for (const dep of dependencies) {
643
+ const depResourceId = dep.reference.resourceId;
644
+ if (depResourceId === '__schema__')
645
+ continue; // Skip schema references
646
+ if (!index.has(depResourceId)) {
647
+ strongConnect(depResourceId);
648
+ lowLink.set(resourceId, Math.min(lowLink.get(resourceId), lowLink.get(depResourceId)));
649
+ }
650
+ else if (onStack.has(depResourceId)) {
651
+ lowLink.set(resourceId, Math.min(lowLink.get(resourceId), index.get(depResourceId)));
652
+ }
653
+ }
654
+ // If resourceId is a root node, pop the stack and create a component
655
+ if (lowLink.get(resourceId) === index.get(resourceId)) {
656
+ const component = [];
657
+ let w;
658
+ do {
659
+ w = stack.pop();
660
+ onStack.delete(w);
661
+ component.push(w);
662
+ } while (w !== resourceId);
663
+ components.push(component);
664
+ }
665
+ };
666
+ // Run algorithm on all unvisited nodes
667
+ for (const resourceId of this.dependencyGraph.dependencies.keys()) {
668
+ if (!index.has(resourceId)) {
669
+ strongConnect(resourceId);
670
+ }
671
+ }
672
+ return { components };
673
+ }
674
+ /**
675
+ * Analyze a circular dependency chain
676
+ */
677
+ analyzeCircularChain(chain) {
678
+ const analysis = {
679
+ chain,
680
+ chainLength: chain.length,
681
+ severity: this.calculateChainSeverity(chain),
682
+ breakPoints: this.findPotentialBreakPoints(chain),
683
+ affectedFields: this.getAffectedFields(chain),
684
+ riskLevel: 'medium'
685
+ };
686
+ // Determine risk level
687
+ if (analysis.severity > 0.8 || analysis.chainLength > 5) {
688
+ analysis.riskLevel = 'high';
689
+ }
690
+ else if (analysis.severity < 0.3 && analysis.chainLength <= 2) {
691
+ analysis.riskLevel = 'low';
692
+ }
693
+ return analysis;
694
+ }
695
+ /**
696
+ * Calculate the severity of a circular dependency chain
697
+ */
698
+ calculateChainSeverity(chain) {
699
+ let severity = 0;
700
+ let totalDependencies = 0;
701
+ for (const resourceId of chain) {
702
+ const dependencies = this.dependencyGraph.dependencies.get(resourceId) || [];
703
+ totalDependencies += dependencies.length;
704
+ // Increase severity for required dependencies
705
+ const requiredDeps = dependencies.filter(dep => dep.required);
706
+ severity += requiredDeps.length * 0.3;
707
+ // Increase severity for readiness-affecting dependencies
708
+ const readinessDeps = dependencies.filter(dep => dep.metadata?.affectsReadiness);
709
+ severity += readinessDeps.length * 0.2;
710
+ }
711
+ // Normalize by chain length and total dependencies
712
+ return Math.min(severity / (chain.length * Math.max(totalDependencies, 1)), 1);
713
+ }
714
+ /**
715
+ * Find potential break points in a circular chain
716
+ */
717
+ findPotentialBreakPoints(chain) {
718
+ const breakPoints = [];
719
+ for (const resourceId of chain) {
720
+ const dependencies = this.dependencyGraph.dependencies.get(resourceId) || [];
721
+ // Look for optional dependencies that could be break points
722
+ const optionalDeps = dependencies.filter(dep => !dep.required);
723
+ if (optionalDeps.length > 0) {
724
+ breakPoints.push(resourceId);
725
+ }
726
+ // Look for conditional dependencies
727
+ const conditionalDeps = dependencies.filter(dep => dep.metadata?.conditional);
728
+ if (conditionalDeps.length > 0) {
729
+ breakPoints.push(resourceId);
730
+ }
731
+ }
732
+ return [...new Set(breakPoints)]; // Remove duplicates
733
+ }
734
+ /**
735
+ * Get affected fields for a circular chain
736
+ */
737
+ getAffectedFields(chain) {
738
+ const affectedFields = [];
739
+ for (const resourceId of chain) {
740
+ const dependencies = this.dependencyGraph.dependencies.get(resourceId) || [];
741
+ for (const dep of dependencies) {
742
+ if (chain.includes(dep.reference.resourceId)) {
743
+ affectedFields.push(`${resourceId}.${dep.fieldPath}`);
744
+ }
745
+ }
746
+ }
747
+ return affectedFields;
748
+ }
749
+ /**
750
+ * Generate recommendations for resolving circular dependencies
751
+ */
752
+ generateCircularDependencyRecommendations(chainAnalysis) {
753
+ const recommendations = [];
754
+ // Recommend breaking at optional dependencies
755
+ if (chainAnalysis.breakPoints.length > 0) {
756
+ recommendations.push({
757
+ type: 'break-optional-dependency',
758
+ description: `Consider making dependencies optional at: ${chainAnalysis.breakPoints.join(', ')}`,
759
+ severity: 'medium',
760
+ affectedResources: chainAnalysis.breakPoints,
761
+ implementation: 'Use conditional expressions or default values for these dependencies'
762
+ });
763
+ }
764
+ // Recommend refactoring for high-severity chains
765
+ if (chainAnalysis.severity > 0.7) {
766
+ recommendations.push({
767
+ type: 'refactor-architecture',
768
+ description: 'Consider refactoring the resource architecture to eliminate circular dependencies',
769
+ severity: 'high',
770
+ affectedResources: chainAnalysis.chain,
771
+ implementation: 'Extract shared dependencies into separate resources or use event-driven patterns'
772
+ });
773
+ }
774
+ // Recommend using external configuration for long chains
775
+ if (chainAnalysis.chainLength > 4) {
776
+ recommendations.push({
777
+ type: 'external-configuration',
778
+ description: 'Consider using external configuration (ConfigMaps, Secrets) to break the dependency chain',
779
+ severity: 'medium',
780
+ affectedResources: chainAnalysis.chain,
781
+ implementation: 'Move configuration values to ConfigMaps and reference them instead of cross-resource dependencies'
782
+ });
783
+ }
784
+ return recommendations;
785
+ }
786
+ }
787
+ /**
788
+ * Comprehensive resource type validator for KubernetesRef objects
789
+ * This implements requirement 4.5: resource type validation
790
+ */
791
+ export class ResourceTypeValidator {
792
+ knownResourceTypes;
793
+ schemaValidators;
794
+ constructor() {
795
+ this.knownResourceTypes = new Map();
796
+ this.schemaValidators = new Map();
797
+ this.initializeKnownTypes();
798
+ }
799
+ /**
800
+ * Validate a KubernetesRef for type correctness
801
+ */
802
+ validateKubernetesRef(ref, context) {
803
+ const result = {
804
+ fieldPath: `${ref.resourceId}.${ref.fieldPath}`,
805
+ reference: ref,
806
+ valid: true,
807
+ expectedType: ref._type ? String(ref._type) : 'unknown'
808
+ };
809
+ try {
810
+ if (ref.resourceId === '__schema__') {
811
+ return this.validateSchemaRef(ref, context, result);
812
+ }
813
+ else {
814
+ return this.validateResourceRef(ref, context, result);
815
+ }
816
+ }
817
+ catch (error) {
818
+ result.valid = false;
819
+ result.error = error instanceof Error ? error.message : String(error);
820
+ return result;
821
+ }
822
+ }
823
+ /**
824
+ * Validate a schema reference
825
+ */
826
+ validateSchemaRef(ref, context, result) {
827
+ if (!context.schemaProxy) {
828
+ result.valid = false;
829
+ result.error = 'Schema proxy not available for validation';
830
+ return result;
831
+ }
832
+ // Validate field path structure
833
+ const pathValidation = this.validateSchemaFieldPath(ref.fieldPath);
834
+ if (!pathValidation.valid) {
835
+ result.valid = false;
836
+ result.error = pathValidation.error || 'Validation failed';
837
+ return result;
838
+ }
839
+ // Validate against schema if available
840
+ const schemaValidator = this.schemaValidators.get(context.schemaType || 'default');
841
+ if (schemaValidator) {
842
+ const schemaValidation = schemaValidator.validateField(ref.fieldPath, ref._type);
843
+ if (!schemaValidation.valid) {
844
+ result.valid = false;
845
+ result.error = schemaValidation.error || 'Schema validation failed';
846
+ result.actualType = schemaValidation.actualType || 'unknown';
847
+ }
848
+ }
849
+ return result;
850
+ }
851
+ /**
852
+ * Validate a resource reference
853
+ */
854
+ validateResourceRef(ref, context, result) {
855
+ // Check if resource exists
856
+ const resource = context.availableResources?.[ref.resourceId];
857
+ if (!resource) {
858
+ result.valid = false;
859
+ result.error = `Resource '${ref.resourceId}' not found`;
860
+ return result;
861
+ }
862
+ // Validate field path structure
863
+ const pathValidation = this.validateResourceFieldPath(ref.fieldPath, resource);
864
+ if (!pathValidation.valid) {
865
+ result.valid = false;
866
+ result.error = pathValidation.error || 'Path validation failed';
867
+ result.actualType = pathValidation.actualType || 'unknown';
868
+ return result;
869
+ }
870
+ // Validate type compatibility
871
+ const typeValidation = this.validateTypeCompatibility(ref, resource);
872
+ if (!typeValidation.valid) {
873
+ result.valid = false;
874
+ result.error = typeValidation.error || 'Type validation failed';
875
+ result.actualType = typeValidation.actualType || 'unknown';
876
+ }
877
+ return result;
878
+ }
879
+ /**
880
+ * Validate schema field path structure
881
+ */
882
+ validateSchemaFieldPath(fieldPath) {
883
+ const parts = fieldPath.split('.');
884
+ if (parts.length < 2) {
885
+ return {
886
+ valid: false,
887
+ error: 'Schema field path must have at least 2 parts (e.g., spec.name)'
888
+ };
889
+ }
890
+ const rootField = parts[0];
891
+ if (rootField !== 'spec' && rootField !== 'status') {
892
+ return {
893
+ valid: false,
894
+ error: `Schema field path must start with 'spec' or 'status', got '${rootField}'`
895
+ };
896
+ }
897
+ return { valid: true };
898
+ }
899
+ /**
900
+ * Validate resource field path structure
901
+ */
902
+ validateResourceFieldPath(fieldPath, resource) {
903
+ const parts = fieldPath.split('.');
904
+ if (parts.length === 0) {
905
+ return {
906
+ valid: false,
907
+ error: 'Field path cannot be empty'
908
+ };
909
+ }
910
+ const rootField = parts[0];
911
+ const validRootFields = ['metadata', 'spec', 'status'];
912
+ if (!rootField || !validRootFields.includes(rootField)) {
913
+ return {
914
+ valid: false,
915
+ error: `Invalid root field '${rootField || 'undefined'}'. Must be one of: ${validRootFields.join(', ')}`
916
+ };
917
+ }
918
+ // Validate specific field patterns
919
+ const patternValidation = this.validateFieldPattern(fieldPath);
920
+ if (!patternValidation.valid) {
921
+ return patternValidation;
922
+ }
923
+ // Try to infer actual type from resource
924
+ const actualType = this.inferFieldType(fieldPath, resource);
925
+ return {
926
+ valid: true,
927
+ actualType: actualType || 'unknown'
928
+ };
929
+ }
930
+ /**
931
+ * Validate field patterns against known Kubernetes patterns
932
+ */
933
+ validateFieldPattern(fieldPath) {
934
+ // Known valid patterns
935
+ const validPatterns = [
936
+ // Metadata fields
937
+ { pattern: /^metadata\.(name|namespace|uid|resourceVersion|generation)$/, type: 'string' },
938
+ { pattern: /^metadata\.labels\..+$/, type: 'string' },
939
+ { pattern: /^metadata\.annotations\..+$/, type: 'string' },
940
+ // Common spec fields
941
+ { pattern: /^spec\.replicas$/, type: 'number' },
942
+ { pattern: /^spec\.selector\.matchLabels\..+$/, type: 'string' },
943
+ // Common status fields
944
+ { pattern: /^status\.ready$/, type: 'boolean' },
945
+ { pattern: /^status\.(replicas|readyReplicas|availableReplicas|unavailableReplicas)$/, type: 'number' },
946
+ { pattern: /^status\.conditions\[\d+\]\.(type|status|reason|message)$/, type: 'string' },
947
+ { pattern: /^status\.conditions\[\d+\]\.lastTransitionTime$/, type: 'string' },
948
+ { pattern: /^status\.loadBalancer\.ingress\[\d+\]\.(ip|hostname)$/, type: 'string' },
949
+ { pattern: /^status\.(podIP|hostIP)$/, type: 'string' },
950
+ { pattern: /^status\.phase$/, type: 'string' },
951
+ // Generic patterns
952
+ { pattern: /^spec\./, type: 'unknown' },
953
+ { pattern: /^status\./, type: 'unknown' }
954
+ ];
955
+ const matchingPattern = validPatterns.find(p => p.pattern.test(fieldPath));
956
+ if (!matchingPattern) {
957
+ return {
958
+ valid: false,
959
+ error: `Field path '${fieldPath}' does not match any known Kubernetes field patterns`
960
+ };
961
+ }
962
+ return {
963
+ valid: true,
964
+ actualType: matchingPattern.type
965
+ };
966
+ }
967
+ /**
968
+ * Validate type compatibility between expected and actual types
969
+ */
970
+ validateTypeCompatibility(ref, resource) {
971
+ if (!ref._type) {
972
+ // No expected type specified, assume compatible
973
+ return { valid: true };
974
+ }
975
+ const expectedType = String(ref._type);
976
+ const actualType = this.inferFieldType(ref.fieldPath, resource);
977
+ if (!actualType || actualType === 'unknown') {
978
+ // Cannot determine actual type, assume compatible
979
+ return { valid: true };
980
+ }
981
+ // Check type compatibility
982
+ const compatible = this.areTypesCompatible(expectedType, actualType);
983
+ if (!compatible) {
984
+ return {
985
+ valid: false,
986
+ error: `Type mismatch: expected '${expectedType}' but field has type '${actualType}'`,
987
+ actualType
988
+ };
989
+ }
990
+ return { valid: true, actualType };
991
+ }
992
+ /**
993
+ * Check if two types are compatible
994
+ */
995
+ areTypesCompatible(expectedType, actualType) {
996
+ // Exact match
997
+ if (expectedType === actualType) {
998
+ return true;
999
+ }
1000
+ // Compatible type mappings
1001
+ const compatibleTypes = {
1002
+ 'string': ['string', 'unknown'],
1003
+ 'number': ['number', 'integer', 'float', 'unknown'],
1004
+ 'boolean': ['boolean', 'unknown'],
1005
+ 'object': ['object', 'unknown'],
1006
+ 'array': ['array', 'unknown'],
1007
+ 'unknown': ['string', 'number', 'boolean', 'object', 'array', 'unknown']
1008
+ };
1009
+ const compatibleWithExpected = compatibleTypes[expectedType] || [];
1010
+ return compatibleWithExpected.includes(actualType);
1011
+ }
1012
+ /**
1013
+ * Infer the type of a field from a resource
1014
+ */
1015
+ inferFieldType(fieldPath, _resource) {
1016
+ // Try to infer type from field path patterns
1017
+ if (fieldPath.includes('replicas') || fieldPath.includes('generation') || fieldPath.includes('port')) {
1018
+ return 'number';
1019
+ }
1020
+ if (fieldPath.includes('ready') || fieldPath.includes('enabled')) {
1021
+ return 'boolean';
1022
+ }
1023
+ if (fieldPath.includes('name') || fieldPath.includes('namespace') || fieldPath.includes('ip') || fieldPath.includes('phase')) {
1024
+ return 'string';
1025
+ }
1026
+ if (fieldPath.includes('labels') || fieldPath.includes('annotations') || fieldPath.includes('selector')) {
1027
+ return 'object';
1028
+ }
1029
+ if (fieldPath.includes('conditions') || fieldPath.includes('ingress')) {
1030
+ return 'array';
1031
+ }
1032
+ return 'unknown';
1033
+ }
1034
+ /**
1035
+ * Initialize known resource types
1036
+ */
1037
+ initializeKnownTypes() {
1038
+ // Common Kubernetes resource types
1039
+ this.knownResourceTypes.set('Deployment', {
1040
+ apiVersion: 'apps/v1',
1041
+ kind: 'Deployment',
1042
+ commonFields: {
1043
+ 'metadata.name': 'string',
1044
+ 'metadata.namespace': 'string',
1045
+ 'spec.replicas': 'number',
1046
+ 'status.readyReplicas': 'number',
1047
+ 'status.availableReplicas': 'number'
1048
+ }
1049
+ });
1050
+ this.knownResourceTypes.set('Service', {
1051
+ apiVersion: 'v1',
1052
+ kind: 'Service',
1053
+ commonFields: {
1054
+ 'metadata.name': 'string',
1055
+ 'metadata.namespace': 'string',
1056
+ 'spec.type': 'string',
1057
+ 'spec.ports': 'array',
1058
+ 'status.loadBalancer.ingress': 'array'
1059
+ }
1060
+ });
1061
+ this.knownResourceTypes.set('Pod', {
1062
+ apiVersion: 'v1',
1063
+ kind: 'Pod',
1064
+ commonFields: {
1065
+ 'metadata.name': 'string',
1066
+ 'metadata.namespace': 'string',
1067
+ 'status.phase': 'string',
1068
+ 'status.podIP': 'string',
1069
+ 'status.hostIP': 'string'
1070
+ }
1071
+ });
1072
+ }
1073
+ /**
1074
+ * Register a custom schema validator
1075
+ */
1076
+ registerSchemaValidator(schemaType, validator) {
1077
+ this.schemaValidators.set(schemaType, validator);
1078
+ }
1079
+ /**
1080
+ * Register a custom resource type
1081
+ */
1082
+ registerResourceType(name, typeInfo) {
1083
+ this.knownResourceTypes.set(name, typeInfo);
1084
+ }
1085
+ }
1086
+ //# sourceMappingURL=resource-analyzer.js.map