typekro 0.8.0 → 0.10.0

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