typekro 0.7.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (532) 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/compositions/typekro-runtime/typekro-runtime.d.ts +1 -1
  21. package/dist/compositions/typekro-runtime/typekro-runtime.d.ts.map +1 -1
  22. package/dist/compositions/typekro-runtime/typekro-runtime.js +39 -7
  23. package/dist/compositions/typekro-runtime/typekro-runtime.js.map +1 -1
  24. package/dist/core/composition/context.d.ts +58 -2
  25. package/dist/core/composition/context.d.ts.map +1 -1
  26. package/dist/core/composition/context.js +4 -0
  27. package/dist/core/composition/context.js.map +1 -1
  28. package/dist/core/composition/imperative.d.ts +9 -0
  29. package/dist/core/composition/imperative.d.ts.map +1 -1
  30. package/dist/core/composition/imperative.js +538 -54
  31. package/dist/core/composition/imperative.js.map +1 -1
  32. package/dist/core/composition/nested-status-cel.d.ts +34 -1
  33. package/dist/core/composition/nested-status-cel.d.ts.map +1 -1
  34. package/dist/core/composition/nested-status-cel.js +379 -41
  35. package/dist/core/composition/nested-status-cel.js.map +1 -1
  36. package/dist/core/composition-debugger.d.ts +1 -1
  37. package/dist/core/composition-debugger.d.ts.map +1 -1
  38. package/dist/core/composition-debugger.js.map +1 -1
  39. package/dist/core/constants/brands.d.ts +1 -1
  40. package/dist/core/constants/brands.d.ts.map +1 -1
  41. package/dist/core/constants/brands.js +1 -1
  42. package/dist/core/constants/brands.js.map +1 -1
  43. package/dist/core/containers/build.d.ts +4 -4
  44. package/dist/core/containers/build.d.ts.map +1 -1
  45. package/dist/core/containers/build.js +44 -16
  46. package/dist/core/containers/build.js.map +1 -1
  47. package/dist/core/containers/registries/types.d.ts +1 -1
  48. package/dist/core/containers/registries/types.d.ts.map +1 -1
  49. package/dist/core/dependencies/resolver.d.ts +19 -0
  50. package/dist/core/dependencies/resolver.d.ts.map +1 -1
  51. package/dist/core/dependencies/resolver.js +261 -1
  52. package/dist/core/dependencies/resolver.js.map +1 -1
  53. package/dist/core/deployment/client-provider-manager.d.ts +9 -3
  54. package/dist/core/deployment/client-provider-manager.d.ts.map +1 -1
  55. package/dist/core/deployment/client-provider-manager.js +12 -0
  56. package/dist/core/deployment/client-provider-manager.js.map +1 -1
  57. package/dist/core/deployment/crd-manager.d.ts +24 -0
  58. package/dist/core/deployment/crd-manager.d.ts.map +1 -1
  59. package/dist/core/deployment/crd-manager.js +79 -1
  60. package/dist/core/deployment/crd-manager.js.map +1 -1
  61. package/dist/core/deployment/deployment-state-discovery.d.ts +116 -0
  62. package/dist/core/deployment/deployment-state-discovery.d.ts.map +1 -0
  63. package/dist/core/deployment/deployment-state-discovery.js +400 -0
  64. package/dist/core/deployment/deployment-state-discovery.js.map +1 -0
  65. package/dist/core/deployment/direct-factory.d.ts +65 -6
  66. package/dist/core/deployment/direct-factory.d.ts.map +1 -1
  67. package/dist/core/deployment/direct-factory.js +551 -56
  68. package/dist/core/deployment/direct-factory.js.map +1 -1
  69. package/dist/core/deployment/engine.d.ts +64 -3
  70. package/dist/core/deployment/engine.d.ts.map +1 -1
  71. package/dist/core/deployment/engine.js +194 -27
  72. package/dist/core/deployment/engine.js.map +1 -1
  73. package/dist/core/deployment/event-filter.js +1 -1
  74. package/dist/core/deployment/event-filter.js.map +1 -1
  75. package/dist/core/deployment/event-monitor.d.ts +11 -0
  76. package/dist/core/deployment/event-monitor.d.ts.map +1 -1
  77. package/dist/core/deployment/event-monitor.js +14 -0
  78. package/dist/core/deployment/event-monitor.js.map +1 -1
  79. package/dist/core/deployment/handle-tracing.d.ts +14 -0
  80. package/dist/core/deployment/handle-tracing.d.ts.map +1 -0
  81. package/dist/core/deployment/handle-tracing.js +38 -0
  82. package/dist/core/deployment/handle-tracing.js.map +1 -0
  83. package/dist/core/deployment/kro-factory.d.ts +136 -3
  84. package/dist/core/deployment/kro-factory.d.ts.map +1 -1
  85. package/dist/core/deployment/kro-factory.js +945 -268
  86. package/dist/core/deployment/kro-factory.js.map +1 -1
  87. package/dist/core/deployment/kro-readiness.d.ts.map +1 -1
  88. package/dist/core/deployment/kro-readiness.js +21 -12
  89. package/dist/core/deployment/kro-readiness.js.map +1 -1
  90. package/dist/core/deployment/nested-composition-status.d.ts +1 -1
  91. package/dist/core/deployment/nested-composition-status.d.ts.map +1 -1
  92. package/dist/core/deployment/nested-composition-status.js +96 -53
  93. package/dist/core/deployment/nested-composition-status.js.map +1 -1
  94. package/dist/core/deployment/resource-applier.d.ts +15 -2
  95. package/dist/core/deployment/resource-applier.d.ts.map +1 -1
  96. package/dist/core/deployment/resource-applier.js +75 -25
  97. package/dist/core/deployment/resource-applier.js.map +1 -1
  98. package/dist/core/deployment/resource-tagging.d.ts +220 -0
  99. package/dist/core/deployment/resource-tagging.d.ts.map +1 -0
  100. package/dist/core/deployment/resource-tagging.js +292 -0
  101. package/dist/core/deployment/resource-tagging.js.map +1 -0
  102. package/dist/core/deployment/rollback-manager.d.ts +25 -4
  103. package/dist/core/deployment/rollback-manager.d.ts.map +1 -1
  104. package/dist/core/deployment/rollback-manager.js +70 -57
  105. package/dist/core/deployment/rollback-manager.js.map +1 -1
  106. package/dist/core/deployment/shared-utilities.d.ts +6 -0
  107. package/dist/core/deployment/shared-utilities.d.ts.map +1 -1
  108. package/dist/core/deployment/shared-utilities.js +32 -2
  109. package/dist/core/deployment/shared-utilities.js.map +1 -1
  110. package/dist/core/deployment/singleton-owner-drift.d.ts +16 -0
  111. package/dist/core/deployment/singleton-owner-drift.d.ts.map +1 -0
  112. package/dist/core/deployment/singleton-owner-drift.js +54 -0
  113. package/dist/core/deployment/singleton-owner-drift.js.map +1 -0
  114. package/dist/core/deployment/strategies/alchemy-strategy.d.ts +3 -1
  115. package/dist/core/deployment/strategies/alchemy-strategy.d.ts.map +1 -1
  116. package/dist/core/deployment/strategies/alchemy-strategy.js +121 -18
  117. package/dist/core/deployment/strategies/alchemy-strategy.js.map +1 -1
  118. package/dist/core/deployment/strategies/base-strategy.d.ts +9 -3
  119. package/dist/core/deployment/strategies/base-strategy.d.ts.map +1 -1
  120. package/dist/core/deployment/strategies/base-strategy.js +32 -4
  121. package/dist/core/deployment/strategies/base-strategy.js.map +1 -1
  122. package/dist/core/deployment/strategies/direct-strategy.d.ts +12 -4
  123. package/dist/core/deployment/strategies/direct-strategy.d.ts.map +1 -1
  124. package/dist/core/deployment/strategies/direct-strategy.js +112 -8
  125. package/dist/core/deployment/strategies/direct-strategy.js.map +1 -1
  126. package/dist/core/errors.d.ts +2 -2
  127. package/dist/core/errors.d.ts.map +1 -1
  128. package/dist/core/errors.js.map +1 -1
  129. package/dist/core/expressions/analysis/cache.d.ts +2 -2
  130. package/dist/core/expressions/analysis/cache.d.ts.map +1 -1
  131. package/dist/core/expressions/analysis/cache.js +4 -0
  132. package/dist/core/expressions/analysis/cache.js.map +1 -1
  133. package/dist/core/expressions/analysis/fn-toString-self-test.d.ts.map +1 -1
  134. package/dist/core/expressions/analysis/fn-toString-self-test.js +0 -1
  135. package/dist/core/expressions/analysis/fn-toString-self-test.js.map +1 -1
  136. package/dist/core/expressions/analysis/shared-types.d.ts +2 -2
  137. package/dist/core/expressions/analysis/shared-types.d.ts.map +1 -1
  138. package/dist/core/expressions/analysis/source-map.d.ts.map +1 -1
  139. package/dist/core/expressions/analysis/source-map.js +1 -0
  140. package/dist/core/expressions/analysis/source-map.js.map +1 -1
  141. package/dist/core/expressions/analysis/types.d.ts +7 -7
  142. package/dist/core/expressions/analysis/types.d.ts.map +1 -1
  143. package/dist/core/expressions/composition/composition-analyzer-helpers.d.ts +37 -3
  144. package/dist/core/expressions/composition/composition-analyzer-helpers.d.ts.map +1 -1
  145. package/dist/core/expressions/composition/composition-analyzer-helpers.js +370 -7
  146. package/dist/core/expressions/composition/composition-analyzer-helpers.js.map +1 -1
  147. package/dist/core/expressions/composition/composition-analyzer-ternary.d.ts +2 -2
  148. package/dist/core/expressions/composition/composition-analyzer-ternary.d.ts.map +1 -1
  149. package/dist/core/expressions/composition/composition-analyzer-ternary.js +264 -29
  150. package/dist/core/expressions/composition/composition-analyzer-ternary.js.map +1 -1
  151. package/dist/core/expressions/composition/composition-analyzer-traversal.d.ts.map +1 -1
  152. package/dist/core/expressions/composition/composition-analyzer-traversal.js +78 -6
  153. package/dist/core/expressions/composition/composition-analyzer-traversal.js.map +1 -1
  154. package/dist/core/expressions/composition/composition-analyzer-types.d.ts +60 -0
  155. package/dist/core/expressions/composition/composition-analyzer-types.d.ts.map +1 -1
  156. package/dist/core/expressions/composition/composition-analyzer.d.ts +1 -1
  157. package/dist/core/expressions/composition/composition-analyzer.d.ts.map +1 -1
  158. package/dist/core/expressions/composition/composition-analyzer.js +158 -8
  159. package/dist/core/expressions/composition/composition-analyzer.js.map +1 -1
  160. package/dist/core/expressions/composition/expression-analyzer.d.ts.map +1 -1
  161. package/dist/core/expressions/composition/expression-analyzer.js +21 -4
  162. package/dist/core/expressions/composition/expression-analyzer.js.map +1 -1
  163. package/dist/core/expressions/composition/imperative-analyzer.d.ts +1 -1
  164. package/dist/core/expressions/composition/imperative-analyzer.d.ts.map +1 -1
  165. package/dist/core/expressions/composition/imperative-analyzer.js +31 -7
  166. package/dist/core/expressions/composition/imperative-analyzer.js.map +1 -1
  167. package/dist/core/expressions/composition/scope-manager.d.ts +2 -2
  168. package/dist/core/expressions/composition/scope-manager.d.ts.map +1 -1
  169. package/dist/core/expressions/composition/scope-manager.js.map +1 -1
  170. package/dist/core/expressions/conditional/conditional-expression-processor.d.ts +3 -3
  171. package/dist/core/expressions/conditional/conditional-expression-processor.d.ts.map +1 -1
  172. package/dist/core/expressions/conditional/conditional-expression-processor.js.map +1 -1
  173. package/dist/core/expressions/conditional/conditional-integration.d.ts.map +1 -1
  174. package/dist/core/expressions/conditional/conditional-integration.js.map +1 -1
  175. package/dist/core/expressions/context/context-aware-generator.d.ts +5 -5
  176. package/dist/core/expressions/context/context-aware-generator.d.ts.map +1 -1
  177. package/dist/core/expressions/context/context-aware-generator.js.map +1 -1
  178. package/dist/core/expressions/context/context-detector.d.ts +3 -3
  179. package/dist/core/expressions/context/context-detector.d.ts.map +1 -1
  180. package/dist/core/expressions/context/context-detector.js.map +1 -1
  181. package/dist/core/expressions/context/context-validator.d.ts +6 -6
  182. package/dist/core/expressions/context/context-validator.d.ts.map +1 -1
  183. package/dist/core/expressions/context/context-validator.js +2 -2
  184. package/dist/core/expressions/context/context-validator.js.map +1 -1
  185. package/dist/core/expressions/factory/cel-conversion-engine.d.ts +4 -4
  186. package/dist/core/expressions/factory/cel-conversion-engine.d.ts.map +1 -1
  187. package/dist/core/expressions/factory/cel-conversion-engine.js.map +1 -1
  188. package/dist/core/expressions/factory/dependency-tracker.d.ts +2 -2
  189. package/dist/core/expressions/factory/dependency-tracker.d.ts.map +1 -1
  190. package/dist/core/expressions/factory/dependency-tracker.js +21 -5
  191. package/dist/core/expressions/factory/dependency-tracker.js.map +1 -1
  192. package/dist/core/expressions/factory/factory-integration.d.ts +2 -4
  193. package/dist/core/expressions/factory/factory-integration.d.ts.map +1 -1
  194. package/dist/core/expressions/factory/factory-integration.js +0 -6
  195. package/dist/core/expressions/factory/factory-integration.js.map +1 -1
  196. package/dist/core/expressions/factory/factory-pattern-handler.d.ts +3 -3
  197. package/dist/core/expressions/factory/factory-pattern-handler.d.ts.map +1 -1
  198. package/dist/core/expressions/factory/factory-pattern-handler.js +1 -0
  199. package/dist/core/expressions/factory/factory-pattern-handler.js.map +1 -1
  200. package/dist/core/expressions/factory/migration-helpers.js.map +1 -1
  201. package/dist/core/expressions/factory/resource-analyzer.d.ts +4 -4
  202. package/dist/core/expressions/factory/resource-analyzer.d.ts.map +1 -1
  203. package/dist/core/expressions/factory/resource-analyzer.js.map +1 -1
  204. package/dist/core/expressions/factory/resource-type-validator.d.ts +5 -5
  205. package/dist/core/expressions/factory/resource-type-validator.d.ts.map +1 -1
  206. package/dist/core/expressions/factory/resource-type-validator.js.map +1 -1
  207. package/dist/core/expressions/factory/status-builder-analyzer.d.ts +6 -7
  208. package/dist/core/expressions/factory/status-builder-analyzer.d.ts.map +1 -1
  209. package/dist/core/expressions/factory/status-builder-analyzer.js +0 -3
  210. package/dist/core/expressions/factory/status-builder-analyzer.js.map +1 -1
  211. package/dist/core/expressions/factory/status-builder-types.d.ts +1 -1
  212. package/dist/core/expressions/factory/status-builder-types.d.ts.map +1 -1
  213. package/dist/core/expressions/factory/status-cel-generation.d.ts +1 -1
  214. package/dist/core/expressions/factory/status-cel-generation.d.ts.map +1 -1
  215. package/dist/core/expressions/factory/status-cel-generation.js +1 -1
  216. package/dist/core/expressions/factory/status-cel-generation.js.map +1 -1
  217. package/dist/core/expressions/factory/status-field-analysis.d.ts +3 -3
  218. package/dist/core/expressions/factory/status-field-analysis.d.ts.map +1 -1
  219. package/dist/core/expressions/factory/status-field-analysis.js.map +1 -1
  220. package/dist/core/expressions/magic-proxy/magic-assignable-analyzer.d.ts +5 -5
  221. package/dist/core/expressions/magic-proxy/magic-assignable-analyzer.d.ts.map +1 -1
  222. package/dist/core/expressions/magic-proxy/magic-assignable-analyzer.js +1 -1
  223. package/dist/core/expressions/magic-proxy/magic-assignable-analyzer.js.map +1 -1
  224. package/dist/core/expressions/magic-proxy/magic-proxy-analyzer.d.ts +10 -10
  225. package/dist/core/expressions/magic-proxy/magic-proxy-analyzer.d.ts.map +1 -1
  226. package/dist/core/expressions/magic-proxy/magic-proxy-analyzer.js +5 -1
  227. package/dist/core/expressions/magic-proxy/magic-proxy-analyzer.js.map +1 -1
  228. package/dist/core/expressions/magic-proxy/magic-proxy-ast.d.ts +2 -2
  229. package/dist/core/expressions/magic-proxy/magic-proxy-ast.d.ts.map +1 -1
  230. package/dist/core/expressions/magic-proxy/magic-proxy-ast.js.map +1 -1
  231. package/dist/core/expressions/magic-proxy/magic-proxy-detector.d.ts +5 -5
  232. package/dist/core/expressions/magic-proxy/magic-proxy-detector.d.ts.map +1 -1
  233. package/dist/core/expressions/magic-proxy/magic-proxy-detector.js.map +1 -1
  234. package/dist/core/expressions/magic-proxy/magic-proxy-types.d.ts +2 -2
  235. package/dist/core/expressions/magic-proxy/magic-proxy-types.d.ts.map +1 -1
  236. package/dist/core/expressions/magic-proxy/optionality-handler.d.ts +1 -2
  237. package/dist/core/expressions/magic-proxy/optionality-handler.d.ts.map +1 -1
  238. package/dist/core/expressions/magic-proxy/optionality-handler.js +2 -15
  239. package/dist/core/expressions/magic-proxy/optionality-handler.js.map +1 -1
  240. package/dist/core/expressions/validation/compile-time-checker.d.ts +1 -1
  241. package/dist/core/expressions/validation/compile-time-checker.d.ts.map +1 -1
  242. package/dist/core/expressions/validation/compile-time-checker.js.map +1 -1
  243. package/dist/core/expressions/validation/compile-time-types.d.ts +2 -2
  244. package/dist/core/expressions/validation/compile-time-types.d.ts.map +1 -1
  245. package/dist/core/expressions/validation/kubernetes-field-types.d.ts +4 -4
  246. package/dist/core/expressions/validation/kubernetes-field-types.d.ts.map +1 -1
  247. package/dist/core/expressions/validation/kubernetes-field-types.js.map +1 -1
  248. package/dist/core/expressions/validation/resource-field-utils.d.ts +10 -10
  249. package/dist/core/expressions/validation/resource-field-utils.d.ts.map +1 -1
  250. package/dist/core/expressions/validation/resource-field-utils.js.map +1 -1
  251. package/dist/core/expressions/validation/resource-validation.d.ts +3 -3
  252. package/dist/core/expressions/validation/resource-validation.d.ts.map +1 -1
  253. package/dist/core/expressions/validation/resource-validation.js.map +1 -1
  254. package/dist/core/expressions/validation/type-inference-types.d.ts +2 -2
  255. package/dist/core/expressions/validation/type-inference-types.d.ts.map +1 -1
  256. package/dist/core/expressions/validation/type-safety.d.ts +2 -2
  257. package/dist/core/expressions/validation/type-safety.d.ts.map +1 -1
  258. package/dist/core/expressions/validation/type-safety.js +1 -0
  259. package/dist/core/expressions/validation/type-safety.js.map +1 -1
  260. package/dist/core/kubernetes/bun-api-client.js.map +1 -1
  261. package/dist/core/kubernetes/bun-http-library.d.ts.map +1 -1
  262. package/dist/core/kubernetes/bun-http-library.js +29 -3
  263. package/dist/core/kubernetes/bun-http-library.js.map +1 -1
  264. package/dist/core/kubernetes/client-provider.d.ts +12 -0
  265. package/dist/core/kubernetes/client-provider.d.ts.map +1 -1
  266. package/dist/core/kubernetes/client-provider.js +35 -0
  267. package/dist/core/kubernetes/client-provider.js.map +1 -1
  268. package/dist/core/metadata/resource-metadata.d.ts +46 -1
  269. package/dist/core/metadata/resource-metadata.d.ts.map +1 -1
  270. package/dist/core/metadata/resource-metadata.js.map +1 -1
  271. package/dist/core/proxy/create-resource.d.ts +15 -0
  272. package/dist/core/proxy/create-resource.d.ts.map +1 -1
  273. package/dist/core/proxy/create-resource.js +56 -2
  274. package/dist/core/proxy/create-resource.js.map +1 -1
  275. package/dist/core/readiness/registry.js +2 -2
  276. package/dist/core/readiness/registry.js.map +1 -1
  277. package/dist/core/references/cel-evaluator.d.ts +1 -4
  278. package/dist/core/references/cel-evaluator.d.ts.map +1 -1
  279. package/dist/core/references/cel-evaluator.js +3 -7
  280. package/dist/core/references/cel-evaluator.js.map +1 -1
  281. package/dist/core/references/cel.d.ts +70 -0
  282. package/dist/core/references/cel.d.ts.map +1 -1
  283. package/dist/core/references/cel.js +188 -8
  284. package/dist/core/references/cel.js.map +1 -1
  285. package/dist/core/references/external-refs.d.ts.map +1 -1
  286. package/dist/core/references/external-refs.js +3 -0
  287. package/dist/core/references/external-refs.js.map +1 -1
  288. package/dist/core/references/resolver.d.ts.map +1 -1
  289. package/dist/core/references/resolver.js +28 -17
  290. package/dist/core/references/resolver.js.map +1 -1
  291. package/dist/core/references/schema-proxy.d.ts +18 -10
  292. package/dist/core/references/schema-proxy.d.ts.map +1 -1
  293. package/dist/core/references/schema-proxy.js +174 -23
  294. package/dist/core/references/schema-proxy.js.map +1 -1
  295. package/dist/core/runtime-patches/crd-schema-fix.d.ts.map +1 -1
  296. package/dist/core/runtime-patches/crd-schema-fix.js +4 -1
  297. package/dist/core/runtime-patches/crd-schema-fix.js.map +1 -1
  298. package/dist/core/serialization/cel-optimizer.d.ts.map +1 -1
  299. package/dist/core/serialization/cel-optimizer.js +2 -0
  300. package/dist/core/serialization/cel-optimizer.js.map +1 -1
  301. package/dist/core/serialization/cel-references.d.ts +75 -1
  302. package/dist/core/serialization/cel-references.d.ts.map +1 -1
  303. package/dist/core/serialization/cel-references.js +723 -145
  304. package/dist/core/serialization/cel-references.js.map +1 -1
  305. package/dist/core/serialization/core.d.ts +13 -8
  306. package/dist/core/serialization/core.d.ts.map +1 -1
  307. package/dist/core/serialization/core.js +973 -12
  308. package/dist/core/serialization/core.js.map +1 -1
  309. package/dist/core/serialization/kro-post-processing.d.ts +46 -0
  310. package/dist/core/serialization/kro-post-processing.d.ts.map +1 -0
  311. package/dist/core/serialization/kro-post-processing.js +150 -0
  312. package/dist/core/serialization/kro-post-processing.js.map +1 -0
  313. package/dist/core/serialization/schema.d.ts +62 -3
  314. package/dist/core/serialization/schema.d.ts.map +1 -1
  315. package/dist/core/serialization/schema.js +819 -6
  316. package/dist/core/serialization/schema.js.map +1 -1
  317. package/dist/core/serialization/status-analysis-pipeline.d.ts +1 -1
  318. package/dist/core/serialization/status-analysis-pipeline.d.ts.map +1 -1
  319. package/dist/core/serialization/status-analysis-pipeline.js.map +1 -1
  320. package/dist/core/serialization/yaml.d.ts +3 -2
  321. package/dist/core/serialization/yaml.d.ts.map +1 -1
  322. package/dist/core/serialization/yaml.js +404 -56
  323. package/dist/core/serialization/yaml.js.map +1 -1
  324. package/dist/core/singleton/singleton.d.ts +16 -0
  325. package/dist/core/singleton/singleton.d.ts.map +1 -0
  326. package/dist/core/singleton/singleton.js +135 -0
  327. package/dist/core/singleton/singleton.js.map +1 -0
  328. package/dist/core/types/common.d.ts +2 -2
  329. package/dist/core/types/common.d.ts.map +1 -1
  330. package/dist/core/types/composable.d.ts +1 -1
  331. package/dist/core/types/composable.d.ts.map +1 -1
  332. package/dist/core/types/deployment.d.ts +126 -6
  333. package/dist/core/types/deployment.d.ts.map +1 -1
  334. package/dist/core/types/deployment.js +1 -1
  335. package/dist/core/types/deployment.js.map +1 -1
  336. package/dist/core/types/kubernetes.d.ts +25 -17
  337. package/dist/core/types/kubernetes.d.ts.map +1 -1
  338. package/dist/core/types/references.d.ts +1 -1
  339. package/dist/core/types/references.d.ts.map +1 -1
  340. package/dist/core/types/references.js.map +1 -1
  341. package/dist/core/types/resource-graph.d.ts +1 -1
  342. package/dist/core/types/resource-graph.d.ts.map +1 -1
  343. package/dist/core/types/schema.d.ts +1 -1
  344. package/dist/core/types/schema.d.ts.map +1 -1
  345. package/dist/core/types/serialization.d.ts +62 -6
  346. package/dist/core/types/serialization.d.ts.map +1 -1
  347. package/dist/core/validation/cel-validator.d.ts +15 -2
  348. package/dist/core/validation/cel-validator.d.ts.map +1 -1
  349. package/dist/core/validation/cel-validator.js +144 -63
  350. package/dist/core/validation/cel-validator.js.map +1 -1
  351. package/dist/factories/apisix/compositions/apisix-bootstrap.d.ts +2 -41
  352. package/dist/factories/apisix/compositions/apisix-bootstrap.d.ts.map +1 -1
  353. package/dist/factories/apisix/compositions/apisix-bootstrap.js +262 -217
  354. package/dist/factories/apisix/compositions/apisix-bootstrap.js.map +1 -1
  355. package/dist/factories/apisix/index.d.ts +2 -2
  356. package/dist/factories/apisix/index.js +2 -2
  357. package/dist/factories/apisix/resources/helm.d.ts +2 -2
  358. package/dist/factories/apisix/resources/helm.d.ts.map +1 -1
  359. package/dist/factories/apisix/resources/helm.js.map +1 -1
  360. package/dist/factories/apisix/types.d.ts +21 -11
  361. package/dist/factories/apisix/types.d.ts.map +1 -1
  362. package/dist/factories/apisix/types.js +106 -4
  363. package/dist/factories/apisix/types.js.map +1 -1
  364. package/dist/factories/apisix/utils/admin-credentials.d.ts +5 -3
  365. package/dist/factories/apisix/utils/admin-credentials.d.ts.map +1 -1
  366. package/dist/factories/apisix/utils/admin-credentials.js +14 -10
  367. package/dist/factories/apisix/utils/admin-credentials.js.map +1 -1
  368. package/dist/factories/apisix/utils/helm-values-mapper.d.ts.map +1 -1
  369. package/dist/factories/apisix/utils/helm-values-mapper.js +4 -2
  370. package/dist/factories/apisix/utils/helm-values-mapper.js.map +1 -1
  371. package/dist/factories/cert-manager/resources/challenges.js.map +1 -1
  372. package/dist/factories/cert-manager/types.d.ts +3 -3
  373. package/dist/factories/cert-manager/types.d.ts.map +1 -1
  374. package/dist/factories/cilium/compositions/cilium-bootstrap.d.ts +4 -4
  375. package/dist/factories/cilium/types.d.ts +3 -3
  376. package/dist/factories/cilium/types.d.ts.map +1 -1
  377. package/dist/factories/cnpg/compositions/cnpg-bootstrap.d.ts +1 -0
  378. package/dist/factories/cnpg/compositions/cnpg-bootstrap.d.ts.map +1 -1
  379. package/dist/factories/cnpg/compositions/cnpg-bootstrap.js +48 -0
  380. package/dist/factories/cnpg/compositions/cnpg-bootstrap.js.map +1 -1
  381. package/dist/factories/cnpg/resources/cluster.js +1 -1
  382. package/dist/factories/cnpg/resources/cluster.js.map +1 -1
  383. package/dist/factories/cnpg/resources/helm.d.ts.map +1 -1
  384. package/dist/factories/cnpg/resources/helm.js +1 -0
  385. package/dist/factories/cnpg/resources/helm.js.map +1 -1
  386. package/dist/factories/cnpg/resources/pooler.js +1 -1
  387. package/dist/factories/cnpg/resources/pooler.js.map +1 -1
  388. package/dist/factories/cnpg/types.d.ts +9 -8
  389. package/dist/factories/cnpg/types.d.ts.map +1 -1
  390. package/dist/factories/cnpg/types.js +11 -0
  391. package/dist/factories/cnpg/types.js.map +1 -1
  392. package/dist/factories/external-dns/compositions/external-dns-bootstrap.d.ts.map +1 -1
  393. package/dist/factories/external-dns/compositions/external-dns-bootstrap.js +153 -41
  394. package/dist/factories/external-dns/compositions/external-dns-bootstrap.js.map +1 -1
  395. package/dist/factories/external-dns/resources/dns-endpoint.js +1 -1
  396. package/dist/factories/external-dns/resources/dns-endpoint.js.map +1 -1
  397. package/dist/factories/external-dns/resources/helm.d.ts +1 -1
  398. package/dist/factories/external-dns/resources/helm.d.ts.map +1 -1
  399. package/dist/factories/external-dns/resources/helm.js +17 -10
  400. package/dist/factories/external-dns/resources/helm.js.map +1 -1
  401. package/dist/factories/external-dns/types.d.ts +5 -2
  402. package/dist/factories/external-dns/types.d.ts.map +1 -1
  403. package/dist/factories/external-dns/types.js.map +1 -1
  404. package/dist/factories/flux/git-repository.d.ts.map +1 -1
  405. package/dist/factories/flux/git-repository.js +1 -1
  406. package/dist/factories/flux/git-repository.js.map +1 -1
  407. package/dist/factories/flux/kustomize/kustomization.d.ts +2 -2
  408. package/dist/factories/flux/kustomize/kustomization.d.ts.map +1 -1
  409. package/dist/factories/flux/kustomize/readiness-evaluators.d.ts +1 -1
  410. package/dist/factories/flux/kustomize/readiness-evaluators.d.ts.map +1 -1
  411. package/dist/factories/flux/kustomize/readiness-evaluators.js +1 -1
  412. package/dist/factories/flux/kustomize/readiness-evaluators.js.map +1 -1
  413. package/dist/factories/helm/helm-release.d.ts +3 -2
  414. package/dist/factories/helm/helm-release.d.ts.map +1 -1
  415. package/dist/factories/helm/helm-release.js +1 -0
  416. package/dist/factories/helm/helm-release.js.map +1 -1
  417. package/dist/factories/helm/helm-repository.d.ts +1 -1
  418. package/dist/factories/helm/helm-repository.d.ts.map +1 -1
  419. package/dist/factories/helm/helm-repository.js +6 -4
  420. package/dist/factories/helm/helm-repository.js.map +1 -1
  421. package/dist/factories/helm/readiness-evaluators.d.ts +6 -6
  422. package/dist/factories/helm/readiness-evaluators.d.ts.map +1 -1
  423. package/dist/factories/helm/readiness-evaluators.js +15 -9
  424. package/dist/factories/helm/readiness-evaluators.js.map +1 -1
  425. package/dist/factories/helm/types.d.ts +5 -1
  426. package/dist/factories/helm/types.d.ts.map +1 -1
  427. package/dist/factories/inngest/compositions/inngest-bootstrap.d.ts +1 -0
  428. package/dist/factories/inngest/compositions/inngest-bootstrap.d.ts.map +1 -1
  429. package/dist/factories/inngest/compositions/inngest-bootstrap.js +4 -3
  430. package/dist/factories/inngest/compositions/inngest-bootstrap.js.map +1 -1
  431. package/dist/factories/inngest/resources/helm.js +1 -1
  432. package/dist/factories/inngest/resources/helm.js.map +1 -1
  433. package/dist/factories/inngest/types.d.ts +5 -4
  434. package/dist/factories/inngest/types.d.ts.map +1 -1
  435. package/dist/factories/inngest/types.js +2 -0
  436. package/dist/factories/inngest/types.js.map +1 -1
  437. package/dist/factories/kro/kro-custom-resource.js +1 -1
  438. package/dist/factories/kro/kro-custom-resource.js.map +1 -1
  439. package/dist/factories/kubernetes/config/config-map.d.ts +2 -2
  440. package/dist/factories/kubernetes/config/config-map.d.ts.map +1 -1
  441. package/dist/factories/kubernetes/config/secret.d.ts +2 -2
  442. package/dist/factories/kubernetes/config/secret.d.ts.map +1 -1
  443. package/dist/factories/kubernetes/config/secret.js +11 -1
  444. package/dist/factories/kubernetes/config/secret.js.map +1 -1
  445. package/dist/factories/kubernetes/networking/service.js +1 -1
  446. package/dist/factories/kubernetes/networking/service.js.map +1 -1
  447. package/dist/factories/kubernetes/yaml/yaml-directory.d.ts.map +1 -1
  448. package/dist/factories/kubernetes/yaml/yaml-directory.js +9 -0
  449. package/dist/factories/kubernetes/yaml/yaml-directory.js.map +1 -1
  450. package/dist/factories/kubernetes/yaml/yaml-file.d.ts.map +1 -1
  451. package/dist/factories/kubernetes/yaml/yaml-file.js +9 -0
  452. package/dist/factories/kubernetes/yaml/yaml-file.js.map +1 -1
  453. package/dist/factories/pebble/resources/helm.js.map +1 -1
  454. package/dist/factories/pebble/types.d.ts +2 -2
  455. package/dist/factories/searxng/compositions/index.d.ts +2 -0
  456. package/dist/factories/searxng/compositions/index.d.ts.map +1 -0
  457. package/dist/factories/searxng/compositions/index.js +2 -0
  458. package/dist/factories/searxng/compositions/index.js.map +1 -0
  459. package/dist/factories/searxng/compositions/searxng-bootstrap.d.ts +66 -0
  460. package/dist/factories/searxng/compositions/searxng-bootstrap.d.ts.map +1 -0
  461. package/dist/factories/searxng/compositions/searxng-bootstrap.js +275 -0
  462. package/dist/factories/searxng/compositions/searxng-bootstrap.js.map +1 -0
  463. package/dist/factories/searxng/index.d.ts +29 -0
  464. package/dist/factories/searxng/index.d.ts.map +1 -0
  465. package/dist/factories/searxng/index.js +27 -0
  466. package/dist/factories/searxng/index.js.map +1 -0
  467. package/dist/factories/searxng/resources/index.d.ts +2 -0
  468. package/dist/factories/searxng/resources/index.d.ts.map +1 -0
  469. package/dist/factories/searxng/resources/index.js +2 -0
  470. package/dist/factories/searxng/resources/index.js.map +1 -0
  471. package/dist/factories/searxng/resources/searxng.d.ts +65 -0
  472. package/dist/factories/searxng/resources/searxng.d.ts.map +1 -0
  473. package/dist/factories/searxng/resources/searxng.js +188 -0
  474. package/dist/factories/searxng/resources/searxng.js.map +1 -0
  475. package/dist/factories/searxng/types.d.ts +127 -0
  476. package/dist/factories/searxng/types.d.ts.map +1 -0
  477. package/dist/factories/searxng/types.js +177 -0
  478. package/dist/factories/searxng/types.js.map +1 -0
  479. package/dist/factories/searxng/utils/settings-builder.d.ts +47 -0
  480. package/dist/factories/searxng/utils/settings-builder.d.ts.map +1 -0
  481. package/dist/factories/searxng/utils/settings-builder.js +53 -0
  482. package/dist/factories/searxng/utils/settings-builder.js.map +1 -0
  483. package/dist/factories/simple/config/config-map.d.ts +2 -2
  484. package/dist/factories/simple/config/config-map.d.ts.map +1 -1
  485. package/dist/factories/simple/config/secret.d.ts +2 -2
  486. package/dist/factories/simple/config/secret.d.ts.map +1 -1
  487. package/dist/factories/simple/config/secret.js +28 -0
  488. package/dist/factories/simple/config/secret.js.map +1 -1
  489. package/dist/factories/simple/helm/index.d.ts +1 -1
  490. package/dist/factories/simple/helm/index.d.ts.map +1 -1
  491. package/dist/factories/simple/helm/index.js.map +1 -1
  492. package/dist/factories/simple/storage/persistent-volume.js.map +1 -1
  493. package/dist/factories/simple/types.d.ts +11 -1
  494. package/dist/factories/simple/types.d.ts.map +1 -1
  495. package/dist/factories/simple/workloads/deployment.d.ts.map +1 -1
  496. package/dist/factories/simple/workloads/deployment.js +3 -0
  497. package/dist/factories/simple/workloads/deployment.js.map +1 -1
  498. package/dist/factories/valkey/compositions/valkey-bootstrap.d.ts +1 -0
  499. package/dist/factories/valkey/compositions/valkey-bootstrap.d.ts.map +1 -1
  500. package/dist/factories/valkey/compositions/valkey-bootstrap.js +116 -0
  501. package/dist/factories/valkey/compositions/valkey-bootstrap.js.map +1 -1
  502. package/dist/factories/valkey/resources/valkey.js +1 -1
  503. package/dist/factories/valkey/resources/valkey.js.map +1 -1
  504. package/dist/factories/valkey/types.d.ts +6 -5
  505. package/dist/factories/valkey/types.d.ts.map +1 -1
  506. package/dist/factories/valkey/types.js +10 -0
  507. package/dist/factories/valkey/types.js.map +1 -1
  508. package/dist/factories/webapp/compositions/web-app-with-processing.d.ts +95 -12
  509. package/dist/factories/webapp/compositions/web-app-with-processing.d.ts.map +1 -1
  510. package/dist/factories/webapp/compositions/web-app-with-processing.js +185 -26
  511. package/dist/factories/webapp/compositions/web-app-with-processing.js.map +1 -1
  512. package/dist/factories/webapp/index.d.ts +3 -4
  513. package/dist/factories/webapp/index.d.ts.map +1 -1
  514. package/dist/factories/webapp/index.js +3 -4
  515. package/dist/factories/webapp/index.js.map +1 -1
  516. package/dist/factories/webapp/types.d.ts +60 -2
  517. package/dist/factories/webapp/types.d.ts.map +1 -1
  518. package/dist/factories/webapp/types.js +80 -3
  519. package/dist/factories/webapp/types.js.map +1 -1
  520. package/dist/index.d.ts +2 -0
  521. package/dist/index.d.ts.map +1 -1
  522. package/dist/index.js +1 -0
  523. package/dist/index.js.map +1 -1
  524. package/dist/shared/brands.d.ts +18 -8
  525. package/dist/shared/brands.d.ts.map +1 -1
  526. package/dist/shared/brands.js +19 -9
  527. package/dist/shared/brands.js.map +1 -1
  528. package/dist/utils/cel-escape.d.ts +12 -0
  529. package/dist/utils/cel-escape.d.ts.map +1 -0
  530. package/dist/utils/cel-escape.js +19 -0
  531. package/dist/utils/cel-escape.js.map +1 -0
  532. package/package.json +7 -2
@@ -5,9 +5,26 @@
5
5
  * simple schemas, and provides utility functions for generating schemas
6
6
  * from resource maps.
7
7
  */
8
+ import { escapeCelString } from '../../utils/cel-escape.js';
9
+ import { KUBERNETES_REF_SCHEMA_MARKER_SOURCE } from '../../shared/brands.js';
10
+ import { createCompositionContext, runWithCompositionContext } from '../composition/context.js';
11
+ import { getComponentLogger } from '../logging/index.js';
12
+ import { getMetadataField } from '../metadata/index.js';
8
13
  import { pascalCase } from '../../utils/string.js';
9
14
  import { separateStatusFields } from '../validation/cel-validator.js';
10
15
  import { serializeStatusMappingsToCel } from './cel-references.js';
16
+ const logger = getComponentLogger('schema-defaults');
17
+ const SCHEMA_MARKER_PATTERN_SOURCE = KUBERNETES_REF_SCHEMA_MARKER_SOURCE;
18
+ function deriveResourceIdAliases(resourceId) {
19
+ const aliases = [];
20
+ for (const match of resourceId.matchAll(/\d+/g)) {
21
+ const suffix = resourceId.slice((match.index ?? 0) + match[0].length);
22
+ if (suffix && /^[A-Z]/.test(suffix)) {
23
+ aliases.push(suffix.charAt(0).toLowerCase() + suffix.slice(1));
24
+ }
25
+ }
26
+ return aliases;
27
+ }
11
28
  // ---------------------------------------------------------------------------
12
29
  // Arktype JSON AST → Kro type helpers (private)
13
30
  // ---------------------------------------------------------------------------
@@ -27,6 +44,20 @@ function getKroTypeFromJson(node) {
27
44
  if (nodeObj.domain === 'number' && nodeObj.divisor === 1) {
28
45
  return 'integer';
29
46
  }
47
+ // Map / Record types — arktype represents `Record<string, V>` as
48
+ // `{ domain: "object", index: [{ signature: "string", value: V }] }`.
49
+ // KRO SimpleSchema uses `map[string]<value-type>` notation for these.
50
+ if (nodeObj.domain === 'object' &&
51
+ Array.isArray(nodeObj.index) &&
52
+ nodeObj.index.length === 1) {
53
+ const indexEntry = nodeObj.index[0];
54
+ if (indexEntry.signature === 'string') {
55
+ return `map[string]${getKroTypeFromJson(indexEntry.value)}`;
56
+ }
57
+ }
58
+ if (nodeObj.domain === 'object' || nodeObj.required || nodeObj.optional) {
59
+ return 'object';
60
+ }
30
61
  }
31
62
  // Case 2: Array → union of literals
32
63
  if (Array.isArray(node)) {
@@ -35,6 +66,9 @@ function getKroTypeFromJson(node) {
35
66
  node.some((branch) => branch.unit === false)) {
36
67
  return 'boolean';
37
68
  }
69
+ if (node.some((branch) => !branch || typeof branch !== 'object' || !('unit' in branch))) {
70
+ return 'object';
71
+ }
38
72
  const enumValues = node.map((branch) => {
39
73
  if (typeof branch.unit === 'string') {
40
74
  return branch.unit;
@@ -85,7 +119,6 @@ function arktypeJsonToKroFields(node) {
85
119
  if (typeof childNode === 'object' &&
86
120
  childNode !== null &&
87
121
  (childNode.required || childNode.optional)) {
88
- // Nested object — recurse to produce a nested schema
89
122
  fields[prop.key] = arktypeJsonToKroFields(childNode);
90
123
  }
91
124
  else {
@@ -95,6 +128,643 @@ function arktypeJsonToKroFields(node) {
95
128
  return fields;
96
129
  }
97
130
  // ---------------------------------------------------------------------------
131
+ // Nullish coalescing default extraction
132
+ // ---------------------------------------------------------------------------
133
+ /**
134
+ * Extract literal default values from `spec.field ?? literalValue` patterns
135
+ * in a composition function's source code.
136
+ *
137
+ * **FAST PATH — NOT AUTHORITATIVE.** This regex-based extraction runs first
138
+ * to catch the common case where a composition uses `spec.X ?? 'literal'`
139
+ * with a literal right-hand side. It is intentionally conservative:
140
+ *
141
+ * - Both `??` and the literal must be on the same source line.
142
+ * - The literal must be a string, number, boolean, or minified !0/!1 form.
143
+ * - The right-hand side cannot be a constant, variable, template literal,
144
+ * function call, or expression involving other spec fields.
145
+ *
146
+ * Anything this regex misses is caught by {@link resolveDefaultsByReExecution}
147
+ * (Phase 2), which actually executes the composition function with optional
148
+ * fields set to `undefined` and observes the concrete resolved values. Phase 2
149
+ * is the authoritative source; this function only exists to short-circuit
150
+ * the common case so the heavy re-execution doesn't need to compare every
151
+ * field for every composition.
152
+ *
153
+ * In KRO mode, `??` doesn't trigger for proxy objects (they're truthy).
154
+ * Instead, we detect these patterns via fn.toString() and add
155
+ * `| default=<value>` annotations to the KRO SimpleSchema so that KRO
156
+ * uses the intended default when the field isn't provided in the CR.
157
+ *
158
+ * NOTE: `.orValue()` is NOT supported in KRO resource templates (only in
159
+ * status CEL). Schema `| default=` is the correct mechanism for resource
160
+ * template defaults.
161
+ *
162
+ * Handles:
163
+ * - `spec.field ?? 'string'` or `spec.field ?? "string"`
164
+ * - `spec.field ?? 123` (numbers)
165
+ * - `spec.field ?? true/false` (booleans, including minified !0/!1)
166
+ * - `spec.parent?.child ?? value` (optional chaining)
167
+ */
168
+ export function extractNullishDefaults(fnSource) {
169
+ const defaults = {};
170
+ const pattern = /spec\.([\w]+(?:\??\.\w+)*)\s*\?\?\s*(?:(['"])([^'"]*)\2|(-?\d+(?:\.\d+)?)|true|false|!0|!1)(?=\s*[;,)}\n])/g;
171
+ let match = pattern.exec(fnSource);
172
+ while (match !== null) {
173
+ const fieldPath = match[1]?.replace(/\?\./g, '.');
174
+ if (!fieldPath) {
175
+ match = pattern.exec(fnSource);
176
+ continue;
177
+ }
178
+ const stringValue = match[3];
179
+ const numericValue = match[4];
180
+ if (stringValue !== undefined) {
181
+ defaults[fieldPath] = stringValue;
182
+ }
183
+ else if (numericValue !== undefined) {
184
+ defaults[fieldPath] = Number(numericValue);
185
+ }
186
+ else {
187
+ const fullMatch = match[0];
188
+ if (fullMatch.endsWith('true') || fullMatch.endsWith('!0'))
189
+ defaults[fieldPath] = true;
190
+ else if (fullMatch.endsWith('false') || fullMatch.endsWith('!1'))
191
+ defaults[fieldPath] = false;
192
+ }
193
+ match = pattern.exec(fnSource);
194
+ }
195
+ return defaults;
196
+ }
197
+ /**
198
+ * Placeholder value passed in place of required spec fields during the
199
+ * defaults-extraction re-execution. Any captured leaf value that IS this
200
+ * string, CONTAINS this string as a substring (from template-literal
201
+ * interpolation), or is NaN (from arithmetic coercion of the string) is
202
+ * filtered out by the sentinel-detection logic in `extractDefaultsByComparison`
203
+ * so it never gets recorded as a real schema default.
204
+ *
205
+ * The choice of a string (rather than a Symbol or object) is deliberate:
206
+ * - String concatenation preserves the token so template-literal usage
207
+ * produces a clearly-identifiable captured value (e.g. `"__typekro_default__-cfg"`).
208
+ * - Truthiness matches the real proxy behavior (proxies are truthy), so
209
+ * `if (spec.requiredField)` takes the same branch in both runs.
210
+ * - Numeric contexts coerce to NaN in both runs (proxies also coerce to
211
+ * NaN because functions have no numeric value), so arithmetic-derived
212
+ * NaN values are symmetric across runs and don't produce spurious defaults.
213
+ *
214
+ * If the propagation ever drifts out of this sentinel's shape (e.g. someone
215
+ * introduces a Symbol-based sentinel), update `isSentinelDerivedValue` below
216
+ * to match.
217
+ */
218
+ export const REQUIRED_FIELD_SENTINEL = '__typekro_default__';
219
+ /**
220
+ * Check whether a captured value is derived from the required-field sentinel
221
+ * and therefore should NOT be recorded as a real schema default. Handles:
222
+ * - Direct sentinel or substring (template-literal propagation)
223
+ * - NaN (numeric-coercion propagation — both runs produce NaN symmetrically,
224
+ * but the defaults run's NaN must be filtered out of the defaults map)
225
+ */
226
+ function isSentinelDerivedValue(v) {
227
+ if (typeof v === 'string')
228
+ return v.includes(REQUIRED_FIELD_SENTINEL);
229
+ if (typeof v === 'number')
230
+ return Number.isNaN(v);
231
+ return false;
232
+ }
233
+ /**
234
+ * Resolve ?? defaults by re-executing the composition function with undefined
235
+ * for optional spec fields. This triggers ?? fallbacks naturally, resolving
236
+ * imported constants from the closure (e.g., DEFAULT_SEARXNG_IMAGE).
237
+ *
238
+ * Also detects ternary conditional sections (spec.field ? section : '') by
239
+ * comparing resource strings between proxy-run and defaults-run.
240
+ *
241
+ * Runs in a temporary, isolated composition context. Resource registrations
242
+ * are contained within the temp context. However, composition functions with
243
+ * external side effects (API calls, logging, metrics) will fire during this
244
+ * re-execution. Composition functions should be side-effect-free beyond
245
+ * resource creation for this mechanism to work safely.
246
+ */
247
+ function resolveDefaultsByReExecution(compositionFn, specType, proxyResources) {
248
+ try {
249
+ const specJson = specType.json;
250
+ if (!specJson)
251
+ return undefined;
252
+ const defaultsSpec = {};
253
+ for (const p of specJson.required ?? [])
254
+ defaultsSpec[p.key] = REQUIRED_FIELD_SENTINEL;
255
+ for (const p of specJson.optional ?? [])
256
+ defaultsSpec[p.key] = undefined;
257
+ // Build the set of optional top-level field names. Only optional fields
258
+ // can be ternary-controlled (required fields always have a value).
259
+ const optionalFieldNames = new Set((specJson.optional ?? []).map(p => p.key));
260
+ const tempCtx = createCompositionContext('defaults-extraction');
261
+ runWithCompositionContext(tempCtx, () => {
262
+ compositionFn(defaultsSpec);
263
+ });
264
+ const defaults = {};
265
+ const ternaryConditionals = [];
266
+ const ternaryConditionFieldHints = extractTernaryConditionFieldHints(compositionFn.toString());
267
+ // Match resources by RESOURCE ID rather than by insertion order. Compositions
268
+ // that use `if (!spec.optional) { createResource(...) }` produce different
269
+ // resource counts between runs — positional matching silently pairs
270
+ // unrelated resources (e.g., the defaults run's auto-Secret against the
271
+ // proxy run's Deployment) and corrupts both default extraction and
272
+ // ternary detection. Key-based matching is robust against conditional
273
+ // resource creation: resources present in only one run are simply skipped
274
+ // for comparison.
275
+ const proxyEntries = Object.entries(proxyResources);
276
+ const defaultsEntries = Object.entries(tempCtx.resources);
277
+ const defaultsMap = new Map(defaultsEntries);
278
+ if (proxyEntries.length !== defaultsEntries.length) {
279
+ // Different resource counts between runs are expected when the
280
+ // composition uses conditional `createResource` patterns — the
281
+ // defaults run takes different branches than the proxy run.
282
+ // Key-based matching handles this correctly, so downgrade to debug.
283
+ logger.debug('Re-execution produced a different number of resources than the proxy run — matching by resource ID', {
284
+ proxyResourceCount: proxyEntries.length,
285
+ defaultsResourceCount: defaultsEntries.length,
286
+ });
287
+ }
288
+ for (const [id, proxyRes] of proxyEntries) {
289
+ const defaultsRes = defaultsMap.get(id);
290
+ if (!defaultsRes)
291
+ continue; // resource only in proxy run — skip
292
+ extractDefaultsByComparison(proxyRes, defaultsRes, defaults);
293
+ extractTernaryConditionals(proxyRes, defaultsRes, ternaryConditionals, optionalFieldNames, ternaryConditionFieldHints);
294
+ }
295
+ const hasResults = Object.keys(defaults).length > 0 || ternaryConditionals.length > 0;
296
+ return hasResults ? { defaults, ternaryConditionals } : undefined;
297
+ }
298
+ catch (err) {
299
+ // Best-effort: composition functions with undefined spec fields may throw
300
+ // (e.g., accessing spec.nested.field without optional chaining). Log at
301
+ // debug level for troubleshooting but don't fail schema generation.
302
+ logger.debug('Defaults re-execution failed', {
303
+ error: err instanceof Error ? err.message : String(err),
304
+ });
305
+ return undefined;
306
+ }
307
+ }
308
+ /**
309
+ * Recursively compare proxy-run resource values (with __KUBERNETES_REF__ markers)
310
+ * against defaults-run values (with resolved constants) to find field defaults.
311
+ *
312
+ * When a proxy value is `__KUBERNETES_REF___schema___spec.FIELD__` and the
313
+ * corresponding defaults value is a concrete scalar, we record FIELD → value.
314
+ */
315
+ function extractDefaultsByComparison(proxyVal, defaultsVal, result) {
316
+ // KubernetesRef proxies are functions whose toString() returns a marker string.
317
+ // Coerce to string to check for __KUBERNETES_REF__ markers.
318
+ const proxyStr = typeof proxyVal === 'function' || typeof proxyVal === 'string'
319
+ ? String(proxyVal) : null;
320
+ // Only match when the proxy value IS the marker (exact, single reference).
321
+ // If the marker is embedded inside a larger string (template literal), the
322
+ // value is a transformation, not a default — e.g., `\`has-${spec.extra}\``
323
+ // produces `"has-<marker>"` which is NOT a default for `extra`.
324
+ const exactMarkerMatch = proxyStr?.match(new RegExp(`^${SCHEMA_MARKER_PATTERN_SOURCE}$`));
325
+ const exactFieldPath = exactMarkerMatch?.[1];
326
+ if (exactFieldPath?.startsWith('spec.')) {
327
+ if (typeof defaultsVal === 'string' ||
328
+ typeof defaultsVal === 'number' ||
329
+ typeof defaultsVal === 'boolean') {
330
+ // Skip values derived from the required-field sentinel — these aren't
331
+ // real defaults. Covers: string containing the sentinel token
332
+ // (template-literal propagation), NaN (arithmetic-coercion
333
+ // propagation), and the sentinel itself.
334
+ if (isSentinelDerivedValue(defaultsVal))
335
+ return;
336
+ const fieldKey = exactFieldPath.slice('spec.'.length);
337
+ if (fieldKey) {
338
+ result[fieldKey] = defaultsVal;
339
+ }
340
+ }
341
+ return;
342
+ }
343
+ // If the proxy is a string but contains the marker as part of larger content,
344
+ // skip the defaults check (recursion into arrays/objects still applies below).
345
+ if (proxyStr?.includes('__KUBERNETES_REF___schema___')) {
346
+ return;
347
+ }
348
+ if (Array.isArray(proxyVal) && Array.isArray(defaultsVal)) {
349
+ for (let i = 0; i < Math.min(proxyVal.length, defaultsVal.length); i++) {
350
+ extractDefaultsByComparison(proxyVal[i], defaultsVal[i], result);
351
+ }
352
+ return;
353
+ }
354
+ if (proxyVal && typeof proxyVal === 'object' && defaultsVal && typeof defaultsVal === 'object') {
355
+ for (const key of Object.keys(proxyVal)) {
356
+ extractDefaultsByComparison(proxyVal[key], defaultsVal[key], result);
357
+ }
358
+ }
359
+ }
360
+ /**
361
+ * Detect ternary conditional sections by finding markers in the proxy-run
362
+ * string that are completely ABSENT from the defaults-run string.
363
+ *
364
+ * When `spec.redisUrl ? \`redis: ...\` : ''` is evaluated:
365
+ * - Proxy run (truthy): the redis section with markers is included
366
+ * - Defaults run (falsy): the redis section is empty — the marker is absent
367
+ *
368
+ * For each such "orphan" marker, we extract the surrounding section
369
+ * (from the previous newline-at-start-of-key to the marker's end) as a
370
+ * ternary-controlled conditional.
371
+ *
372
+ * Ternary test-field hints
373
+ * ------------------------
374
+ * Re-execution only shows that a marker-containing section disappeared;
375
+ * it does not leave runtime metadata for the ternary TEST expression. The
376
+ * caller supplies a best-effort map derived from the composition source so
377
+ * common decoupled patterns can guard on the tested field:
378
+ *
379
+ * spec.redisUrl ? `redis:\n url: ${spec.redisUrl}` : ''
380
+ *
381
+ * spec.enableRedis ? `redis:\n url: ${spec.connectionString}` : ''
382
+ *
383
+ * If no hint is available, we fall back to the referenced field, preserving
384
+ * the historical behavior for `spec.redisUrl ? ... ${spec.redisUrl} ...`.
385
+ *
386
+ * FRAGILE: this walks line-by-line through the proxy string and backs
387
+ * through parent YAML keys by regex-matching `^\s*\w+:\s*$` and comparing
388
+ * against a set of lines from the defaults string. It assumes the
389
+ * composition constructs YAML-ish content directly (typically via template
390
+ * literal) with 2-space indentation, canonical key: value syntax, and no
391
+ * block scalars (`|`, `>`). Compositions that emit flow-style YAML, that
392
+ * use tab indentation, that quote keys, or that assemble the string
393
+ * through multiple splice operations will not match cleanly. The
394
+ * extracted `proxySection` is later consumed by
395
+ * `applyTernaryConditionalsToResources` via exact substring match — keep
396
+ * both functions' assumptions in sync when updating either side.
397
+ *
398
+ * Tracked for replacement with AST-based detection in
399
+ * https://github.com/yehudacohen/typekro/issues/57
400
+ */
401
+ function extractTernaryConditionals(proxyVal, defaultsVal, result, optionalFieldNames, conditionFieldHints) {
402
+ const proxyStr = typeof proxyVal === 'string' ? proxyVal : null;
403
+ const defaultsStr = typeof defaultsVal === 'string' ? defaultsVal : null;
404
+ if (proxyStr && defaultsStr && proxyStr !== defaultsStr && proxyStr.length > defaultsStr.length) {
405
+ // Find all __KUBERNETES_REF__ markers in the proxy string
406
+ const markerPattern = new RegExp(SCHEMA_MARKER_PATTERN_SOURCE, 'g');
407
+ let match = markerPattern.exec(proxyStr);
408
+ while (match !== null) {
409
+ const fieldPath = match[1];
410
+ if (!fieldPath?.startsWith('spec.')) {
411
+ match = markerPattern.exec(proxyStr);
412
+ continue;
413
+ }
414
+ const field = fieldPath.slice('spec.'.length);
415
+ const conditionField = conditionFieldHints.get(field) ?? field;
416
+ const marker = match[0];
417
+ // Only optional fields can be ternary-controlled. Required fields always
418
+ // have a value (the sentinel in the defaults run), so their markers are
419
+ // always "absent" from the defaults string but they're substitutions,
420
+ // not conditionals.
421
+ const topLevelField = conditionField.split('.')[0];
422
+ if (!topLevelField) {
423
+ match = markerPattern.exec(proxyStr);
424
+ continue;
425
+ }
426
+ if (!optionalFieldNames.has(topLevelField)) {
427
+ match = markerPattern.exec(proxyStr);
428
+ continue;
429
+ }
430
+ // Check if this marker is a TERNARY conditional (section absent from defaults)
431
+ // vs a ?? fallback (same key exists with a different value in defaults).
432
+ // Find the YAML key before the marker (e.g., `url: ` before the redisUrl marker).
433
+ const lineStart = proxyStr.lastIndexOf('\n', match.index) + 1;
434
+ const line = proxyStr.slice(lineStart, match.index + marker.length);
435
+ const keyMatch = line.match(/(\w+):\s*$/)?.[1] || line.match(/^\s*(\w+):/)?.[1];
436
+ const keyExistsInDefaults = keyMatch
437
+ ? new RegExp(`\\b${keyMatch}\\s*:`).test(defaultsStr)
438
+ : false;
439
+ // If the key exists in defaults, this is a ?? fallback (handled elsewhere).
440
+ // If the key is absent, this is a ternary conditional (section not present).
441
+ if (!keyExistsInDefaults && !defaultsStr.includes(marker)) {
442
+ // Extract the section containing this marker.
443
+ // Walk backward from the marker to find the start of the conditional
444
+ // section. Include parent YAML keys at ALL nesting levels (not just one)
445
+ // so that `redis:\n connection:\n url: ${ref}` captures all three lines.
446
+ let sectionStart = match.index;
447
+ while (sectionStart > 0 && proxyStr[sectionStart - 1] !== '\n') {
448
+ sectionStart--;
449
+ }
450
+ // Walk back through preceding blank lines
451
+ while (sectionStart > 0 && proxyStr[sectionStart - 1] === '\n') {
452
+ sectionStart--;
453
+ }
454
+ // Walk back through parent YAML keys (lines with `key:` and no value).
455
+ // Keep walking as long as the previous line is a key-only line that
456
+ // doesn't exist as a complete line in the defaults. Set membership
457
+ // (not substring match) avoids false stops on coincidental substrings.
458
+ const defaultsLines = new Set(defaultsStr.split('\n'));
459
+ const allLines = proxyStr.slice(0, sectionStart).split('\n');
460
+ while (allLines.length > 0) {
461
+ const prevLine = allLines[allLines.length - 1];
462
+ if (prevLine === undefined)
463
+ break;
464
+ const isKeyOnlyLine = /^\s*\w+:\s*$/.test(prevLine);
465
+ const lineExistsInDefaults = defaultsLines.has(prevLine);
466
+ if (isKeyOnlyLine && !lineExistsInDefaults) {
467
+ sectionStart -= prevLine.length + 1; // +1 for the \n
468
+ allLines.pop();
469
+ }
470
+ else {
471
+ break;
472
+ }
473
+ }
474
+ // Section ends after the marker
475
+ const sectionEnd = match.index + marker.length;
476
+ const proxySection = proxyStr.slice(sectionStart, sectionEnd);
477
+ // Verify the section, when removed from proxy string, makes it closer to defaults
478
+ if (proxySection.includes(marker)) {
479
+ result.push({
480
+ proxySection,
481
+ falsyValue: '',
482
+ conditionField,
483
+ });
484
+ }
485
+ }
486
+ match = markerPattern.exec(proxyStr);
487
+ }
488
+ return;
489
+ }
490
+ // Recurse into arrays and objects
491
+ if (Array.isArray(proxyVal) && Array.isArray(defaultsVal)) {
492
+ for (let i = 0; i < Math.min(proxyVal.length, defaultsVal.length); i++) {
493
+ extractTernaryConditionals(proxyVal[i], defaultsVal[i], result, optionalFieldNames, conditionFieldHints);
494
+ }
495
+ return;
496
+ }
497
+ if (proxyVal && typeof proxyVal === 'object' && defaultsVal && typeof defaultsVal === 'object') {
498
+ for (const key of Object.keys(proxyVal)) {
499
+ extractTernaryConditionals(proxyVal[key], defaultsVal[key], result, optionalFieldNames, conditionFieldHints);
500
+ }
501
+ }
502
+ }
503
+ function extractTernaryConditionFieldHints(source) {
504
+ const hints = new Map();
505
+ const ternaryTemplatePattern = /spec\.([A-Za-z_$][\w$]*(?:\?\.[A-Za-z_$][\w$]*|\.[A-Za-z_$][\w$]*)*)\s*\?\s*`([\s\S]*?)`\s*:\s*(?:''|""|``)/g;
506
+ let match = ternaryTemplatePattern.exec(source);
507
+ while (match) {
508
+ const conditionField = normalizeSpecFieldPath(match[1]);
509
+ const consequent = match[2] ?? '';
510
+ if (conditionField) {
511
+ const refPattern = /\$\{\s*spec\.([A-Za-z_$][\w$]*(?:\?\.[A-Za-z_$][\w$]*|\.[A-Za-z_$][\w$]*)*)/g;
512
+ let refMatch = refPattern.exec(consequent);
513
+ while (refMatch) {
514
+ const referencedField = normalizeSpecFieldPath(refMatch[1]);
515
+ if (referencedField && !hints.has(referencedField)) {
516
+ hints.set(referencedField, conditionField);
517
+ }
518
+ refMatch = refPattern.exec(consequent);
519
+ }
520
+ }
521
+ match = ternaryTemplatePattern.exec(source);
522
+ }
523
+ return hints;
524
+ }
525
+ function normalizeSpecFieldPath(path) {
526
+ return path?.replace(/\?\./g, '.');
527
+ }
528
+ /**
529
+ * Apply extracted nullish defaults to a KRO SimpleSchema spec fields object.
530
+ * Appends `| default=<value>` to the field's type string.
531
+ *
532
+ * The two-phase default extraction uses this function twice:
533
+ * - Phase 1 (regex, `extractNullishDefaults`) — fast but may misfire on
534
+ * edge cases (multi-line expressions, nested parens). Called with
535
+ * `overwrite: false` so it doesn't clobber existing annotations.
536
+ * - Phase 2 (re-execution, `resolveDefaultsByReExecution`) — slower but
537
+ * authoritative. Called with `overwrite: true` so it REPLACES any
538
+ * Phase 1 annotation for the same field, ensuring a wrong Phase 1
539
+ * result doesn't survive when Phase 2 produces a corrected value.
540
+ */
541
+ function applyNullishDefaults(specFields, defaults, overwrite = false) {
542
+ for (const [path, value] of Object.entries(defaults)) {
543
+ const parts = path.split('.');
544
+ let current = specFields;
545
+ for (let i = 0; i < parts.length - 1; i++) {
546
+ const part = parts[i];
547
+ if (!part)
548
+ continue;
549
+ if (current[part] && typeof current[part] === 'object') {
550
+ current = current[part];
551
+ }
552
+ else {
553
+ current = undefined;
554
+ break;
555
+ }
556
+ }
557
+ if (!current)
558
+ continue;
559
+ const fieldName = parts[parts.length - 1];
560
+ if (!fieldName)
561
+ continue;
562
+ const existingType = current[fieldName];
563
+ if (typeof existingType !== 'string')
564
+ continue;
565
+ // Escape special characters in string defaults so they survive
566
+ // the KRO SimpleSchema default= annotation format. Without this,
567
+ // multiline strings (e.g., YAML config blobs) break the parser.
568
+ const defaultStr = typeof value === 'string'
569
+ ? `"${escapeCelString(value)}"`
570
+ : String(value);
571
+ const hasDefault = existingType.includes('default=');
572
+ if (!hasDefault) {
573
+ current[fieldName] = `${existingType} | default=${defaultStr}`;
574
+ }
575
+ else if (overwrite) {
576
+ // Strip any existing `| default=...` segment and replace with the
577
+ // authoritative Phase 2 value. The segment may appear as `| default="x"`,
578
+ // `| default=123`, or `| default=true` — match to the end of the value
579
+ // or to the next `|` (for additional annotations chained after it).
580
+ const withoutDefault = existingType.replace(/\s*\|\s*default=(?:"[^"]*"|[^|]*)/, '');
581
+ current[fieldName] = `${withoutDefault} | default=${defaultStr}`;
582
+ }
583
+ }
584
+ }
585
+ /**
586
+ * Infer the KRO SimpleSchema type string for a literal default value.
587
+ */
588
+ function kroTypeForLiteral(value) {
589
+ if (typeof value === 'string') {
590
+ return `string | default="${escapeCelString(value)}"`;
591
+ }
592
+ if (typeof value === 'number') {
593
+ return Number.isInteger(value)
594
+ ? `integer | default=${value}`
595
+ : `number | default=${value}`;
596
+ }
597
+ if (typeof value === 'boolean') {
598
+ return `boolean | default=${value}`;
599
+ }
600
+ return 'string';
601
+ }
602
+ /**
603
+ * For each default whose path doesn't exist in `specFields`, CREATE the
604
+ * missing leaf (and any intermediate object nodes along the way) with
605
+ * an appropriate KRO type string + default annotation.
606
+ *
607
+ * This is the complement to {@link applyNullishDefaults}, which only
608
+ * annotates fields that already exist. Adding missing fields enables the
609
+ * "propagate inner composition `?? defaults` to the outer schema"
610
+ * mechanism: when an inner composition reads `spec.X?.Y ?? <literal>`
611
+ * and the outer schema doesn't declare Y, we auto-declare it with the
612
+ * inner's default so KRO can resolve the reference at apply time.
613
+ *
614
+ * **Scope rules:**
615
+ * - We only auto-create a missing field if EITHER the full path's parent
616
+ * already exists as an object in `specFields` (we're filling in a
617
+ * single missing leaf under a known object), OR the top-level parent
618
+ * (the first segment) doesn't exist at all (we're adding a whole new
619
+ * optional field). We do NOT create intermediate objects where the
620
+ * outer has explicitly declared a non-object type.
621
+ * - Existing fields are never modified — use `applyNullishDefaults`
622
+ * for that.
623
+ */
624
+ function addMissingDefaultFields(specFields, defaults) {
625
+ for (const [path, value] of Object.entries(defaults)) {
626
+ const parts = path.split('.');
627
+ if (parts.length === 0)
628
+ continue;
629
+ // Walk existing intermediate objects.
630
+ let current = specFields;
631
+ let i = 0;
632
+ for (; i < parts.length - 1; i++) {
633
+ const part = parts[i];
634
+ if (!part)
635
+ continue;
636
+ const next = current[part];
637
+ if (next && typeof next === 'object' && !Array.isArray(next)) {
638
+ current = next;
639
+ }
640
+ else if (next === undefined) {
641
+ // Create missing intermediate object node.
642
+ const obj = {};
643
+ current[part] = obj;
644
+ current = obj;
645
+ }
646
+ else {
647
+ // Intermediate path conflicts with an existing scalar — bail.
648
+ current = undefined;
649
+ break;
650
+ }
651
+ }
652
+ if (!current)
653
+ continue;
654
+ const leafName = parts[parts.length - 1];
655
+ if (!leafName)
656
+ continue;
657
+ if (leafName in current)
658
+ continue; // existing — skip
659
+ current[leafName] = kroTypeForLiteral(value);
660
+ }
661
+ }
662
+ function remapDefaultPath(path, mappings) {
663
+ if (Object.hasOwn(mappings, '')) {
664
+ const mappedPrefix = mappings[''];
665
+ return mappedPrefix ? `${mappedPrefix}.${path}` : path;
666
+ }
667
+ const matches = Object.keys(mappings)
668
+ .filter((prefix) => path === prefix || path.startsWith(`${prefix}.`))
669
+ .sort((left, right) => right.length - left.length);
670
+ const prefix = matches[0];
671
+ if (prefix === undefined)
672
+ return undefined;
673
+ const mappedPrefix = mappings[prefix];
674
+ if (mappedPrefix === undefined)
675
+ return undefined;
676
+ const suffix = path.slice(prefix.length);
677
+ if (mappedPrefix === '') {
678
+ return suffix.startsWith('.') ? suffix.slice(1) : suffix;
679
+ }
680
+ return `${mappedPrefix}${suffix}`;
681
+ }
682
+ function remapNullishDefaults(defaults, mappings) {
683
+ const remapped = {};
684
+ for (const [path, value] of Object.entries(defaults)) {
685
+ const mappedPath = remapDefaultPath(path, mappings);
686
+ if (mappedPath !== undefined) {
687
+ remapped[mappedPath] = value;
688
+ }
689
+ }
690
+ return remapped;
691
+ }
692
+ function schemaSpecMarker(path) {
693
+ return `__KUBERNETES_REF___schema___spec.${path}__`;
694
+ }
695
+ function remapTernaryConditionals(conditionals, mappings) {
696
+ return conditionals.flatMap((conditional) => {
697
+ const conditionField = remapDefaultPath(conditional.conditionField, mappings);
698
+ if (conditionField === undefined)
699
+ return [];
700
+ const proxySection = conditional.proxySection.replace(new RegExp(SCHEMA_MARKER_PATTERN_SOURCE, 'g'), (marker, fieldPath) => {
701
+ if (!fieldPath.startsWith('spec.'))
702
+ return marker;
703
+ const remappedPath = remapDefaultPath(fieldPath.slice('spec.'.length), mappings);
704
+ return remappedPath === undefined ? marker : schemaSpecMarker(remappedPath);
705
+ });
706
+ return [{ ...conditional, conditionField, proxySection }];
707
+ });
708
+ }
709
+ /**
710
+ * Collect optional spec fields that don't have | default= annotations.
711
+ * These fields need omit() wrapping in resource templates (KRO 0.9+)
712
+ * so they're removed from the K8s resource when not provided, rather
713
+ * than failing with an invalid zero value.
714
+ *
715
+ * Recurses into ALL object types so that both top-level optionals
716
+ * (`env?`, `resources?`) and nested optionals (`database.storageClass?`,
717
+ * `cache.replicas?`) are collected. Returns dotted field paths
718
+ * (e.g., `['baseUrl', 'env', 'database.storageClass', 'cache', 'cache.replicas']`).
719
+ *
720
+ * **Both parent and children are tracked** when a whole object is
721
+ * optional: if the user provides `cache: { shards: 1 }` (present but
722
+ * with `replicas` absent), `has(cache)` is true and we must still
723
+ * guard `cache.replicas` with its own omit. The ancestor resolution
724
+ * in `maybeWrapWithOmit` prefers the deepest matching prefix, so a
725
+ * ref to `cache.replicas` gets `has(cache.replicas) ? ... : omit()`
726
+ * while a ref to `cache.shards` (required scalar with no default,
727
+ * not in the set) falls back to the ancestor `cache` guard.
728
+ */
729
+ function collectOmitFields(specFields, specType) {
730
+ const omitFields = [];
731
+ walk(specFields, specType.json, '');
732
+ return omitFields;
733
+ function walk(fields, node, pathPrefix) {
734
+ if (!node || typeof node !== 'object')
735
+ return;
736
+ const nodeObj = node;
737
+ const optionalKeys = new Set((nodeObj.optional ?? []).map((p) => p.key));
738
+ const allEntries = [...(nodeObj.required ?? []), ...(nodeObj.optional ?? [])];
739
+ for (const entry of allEntries) {
740
+ const fieldName = entry.key;
741
+ const fullPath = pathPrefix ? `${pathPrefix}.${fieldName}` : fieldName;
742
+ const existingType = fields[fieldName];
743
+ const isOptional = optionalKeys.has(fieldName);
744
+ // Scalar optional without default → needs its own omit() guard.
745
+ if (isOptional &&
746
+ typeof existingType === 'string' &&
747
+ !existingType.includes('default=')) {
748
+ omitFields.push(fullPath);
749
+ continue;
750
+ }
751
+ // Object (optional or required) → record the path if optional,
752
+ // AND recurse to catch nested optional leaves. Even when the
753
+ // object is required, its children may have their own optional
754
+ // markers. Even when the object is optional, its children still
755
+ // need their own guards because a present-but-sparse object
756
+ // (e.g., `cache: { shards: 1 }` with `replicas` absent) must
757
+ // not fail on ref-time `cache.replicas` access.
758
+ if (typeof existingType === 'object' && existingType !== null) {
759
+ if (isOptional) {
760
+ omitFields.push(fullPath);
761
+ }
762
+ walk(existingType, entry.value, fullPath);
763
+ }
764
+ }
765
+ }
766
+ }
767
+ // ---------------------------------------------------------------------------
98
768
  // Public API
99
769
  // ---------------------------------------------------------------------------
100
770
  /**
@@ -111,6 +781,112 @@ export function arktypeToKroSchema(name, schemaDefinition, resources, statusMapp
111
781
  if (!specFields.name) {
112
782
  specFields.name = `string | default="${name}"`;
113
783
  }
784
+ // Extract ?? defaults and add | default= annotations to the schema.
785
+ // KRO uses these when a field isn't provided in the CR instance.
786
+ //
787
+ // Phase 1: fn.toString() regex — extracts literal ?? fallbacks quickly,
788
+ // for BOTH the outer composition AND every nested composition in the
789
+ // tree. See the nested-composition branch below for why.
790
+ // Phase 2: Defaults resolution run — calls the composition function with
791
+ // undefined for optional fields, triggering ?? and resolving imported
792
+ // constants from the closure. Compares resolved resources with the
793
+ // proxy-run resources (markers) to extract field → default mappings.
794
+ const statusMeta = statusMappings;
795
+ const compositionFn = statusMeta?.__originalCompositionFn;
796
+ // Phase 1a: regex extraction for the outer composition's own defaults.
797
+ if (typeof compositionFn === 'function') {
798
+ const regexDefaults = extractNullishDefaults(compositionFn.toString());
799
+ applyNullishDefaults(specFields, regexDefaults);
800
+ // Missing-field variant: also ADD any default path whose first
801
+ // segment exists in specFields but whose full path doesn't. Handles
802
+ // cases where the outer's own spec has `cache: { ... }` and reads
803
+ // `spec.cache?.foo ?? bar` — the user expects `cache.foo` to be
804
+ // optional with a default, but they forgot to declare it. We auto-
805
+ // declare it for them.
806
+ addMissingDefaultFields(specFields, regexDefaults);
807
+ }
808
+ // Phase 1b: regex extraction for every nested composition in the tree.
809
+ //
810
+ // When an inner composition uses `spec.X?.Y ?? <literal>` and the outer
811
+ // passes its own `spec.X` proxy through (i.e., `innerComp({ X: spec.X, ... })`),
812
+ // the inner's KRO-mode emission produces `${schema.spec.X.Y}` in the
813
+ // flattened resource templates. The OUTER schema typically doesn't
814
+ // declare Y — KRO rejects the RGD with "undefined field".
815
+ //
816
+ // We scan every nested composition's fn source for these `??` defaults
817
+ // and ADD the missing fields to the outer specFields with the literal
818
+ // as a KRO default. KRO then resolves the field using the default at
819
+ // apply time, matching what direct-mode JS does via the `??` operator.
820
+ //
821
+ // This is the framework-side counterpart to the user workaround of
822
+ // explicitly mirroring inner fields in the outer schema. It preserves
823
+ // the "composition looks like native TypeScript" design goal — users
824
+ // shouldn't have to repeat every inner schema detail.
825
+ // `__nestedCompositionFns` was stored via Reflect.set. Read it via
826
+ // getOwnPropertyDescriptor to bypass the Enhanced proxy's get-trap
827
+ // (same pattern as `__nestedStatusCel`). The descriptor's value is
828
+ // the real Map, not a proxy.
829
+ const ternaryConditionals = [];
830
+ const nestedFnsRaw = statusMappings
831
+ ? Object.getOwnPropertyDescriptor(statusMappings, '__nestedCompositionFns')?.value
832
+ : undefined;
833
+ const nestedSpecMappingsRaw = statusMappings
834
+ ? Object.getOwnPropertyDescriptor(statusMappings, '__nestedCompositionSpecMappings')?.value
835
+ : undefined;
836
+ const nestedDefinitionsRaw = statusMappings
837
+ ? Object.getOwnPropertyDescriptor(statusMappings, '__nestedCompositionDefinitions')?.value
838
+ : undefined;
839
+ const nestedResourcesRaw = statusMappings
840
+ ? Object.getOwnPropertyDescriptor(statusMappings, '__nestedCompositionResources')?.value
841
+ : undefined;
842
+ if (nestedFnsRaw instanceof Map) {
843
+ for (const [baseId, fn] of nestedFnsRaw) {
844
+ if (typeof fn !== 'function')
845
+ continue;
846
+ const specMappings = nestedSpecMappingsRaw instanceof Map
847
+ ? nestedSpecMappingsRaw.get(baseId)
848
+ : undefined;
849
+ if (!specMappings || typeof specMappings !== 'object')
850
+ continue;
851
+ const extractedDefaults = extractNullishDefaults(fn.toString());
852
+ const nestedDefinition = nestedDefinitionsRaw instanceof Map
853
+ ? nestedDefinitionsRaw.get(baseId)
854
+ : undefined;
855
+ const nestedResources = nestedResourcesRaw instanceof Map
856
+ ? nestedResourcesRaw.get(baseId)
857
+ : undefined;
858
+ const reExecutionResult = nestedDefinition && nestedResources
859
+ ? resolveDefaultsByReExecution(fn, nestedDefinition.spec, nestedResources)
860
+ : undefined;
861
+ const reExecutionDefaults = reExecutionResult?.defaults ?? {};
862
+ const innerDefaults = remapNullishDefaults({ ...extractedDefaults, ...reExecutionDefaults }, specMappings);
863
+ // Non-destructive: don't overwrite existing outer defaults.
864
+ applyNullishDefaults(specFields, innerDefaults, false);
865
+ // Also add any fields the outer doesn't declare — the inner's
866
+ // `?? <literal>` is the only signal we have that the field should
867
+ // exist with a default.
868
+ addMissingDefaultFields(specFields, innerDefaults);
869
+ ternaryConditionals.push(...remapTernaryConditionals(reExecutionResult?.ternaryConditionals ?? [], specMappings));
870
+ }
871
+ }
872
+ // Phase 2: resolve imported constants + detect ternary conditionals.
873
+ // Phase 2 is AUTHORITATIVE — if it produces a value for a field that
874
+ // Phase 1 also annotated, Phase 2 wins. This corrects Phase 1 misfires
875
+ // on edge cases (multi-line expressions, nested parens) without breaking
876
+ // the common case where both phases agree.
877
+ if (typeof compositionFn === 'function' && resources) {
878
+ const reExecutionResult = resolveDefaultsByReExecution(compositionFn, schemaDefinition.spec, resources);
879
+ if (reExecutionResult) {
880
+ applyNullishDefaults(specFields, reExecutionResult.defaults, true);
881
+ ternaryConditionals.push(...reExecutionResult.ternaryConditionals);
882
+ }
883
+ }
884
+ // Collect optional fields that DON'T have | default= after ?? extraction
885
+ // AND aren't already handled by ternary conditionals.
886
+ // These need omit() wrapping in resource templates (KRO 0.9+).
887
+ const ternaryFieldNames = new Set(ternaryConditionals.map(t => t.conditionField));
888
+ const omitFields = collectOmitFields(specFields, schemaDefinition.spec)
889
+ .filter(f => !ternaryFieldNames.has(f));
114
890
  // Filter internal fields (prefixed with __) before classification.
115
891
  // These are TypeKro metadata (e.g., __nestedStatusCel, __originalCompositionFn)
116
892
  // that should never appear in the KRO schema.
@@ -125,25 +901,62 @@ export function arktypeToKroSchema(name, schemaDefinition, resources, statusMapp
125
901
  // Separate static and dynamic status fields.
126
902
  // Dynamic = references non-schema resource fields (status, metadata, spec) → KRO CEL
127
903
  // Static = references only schema.spec.* or literal values → TypeKro runtime hydration
128
- const { dynamicFields } = separateStatusFields(userStatusMappings);
129
- // Only serialize dynamic fields that need Kro resolution
130
- // Build the set of known resource IDs for schema.spec → resource.spec mapping
904
+ // Classification is transitive through nestedStatusCel: a nested composition
905
+ // reference whose inner analyzed value is schema-only/literal is treated as static.
131
906
  const resourceIds = resources ? new Set(Object.keys(resources)) : undefined;
907
+ const resourceAliases = resources ? new Map() : undefined;
908
+ if (resources && resourceAliases) {
909
+ for (const [resourceId, resource] of Object.entries(resources)) {
910
+ resourceAliases.set(resourceId, resourceId);
911
+ for (const alias of deriveResourceIdAliases(resourceId)) {
912
+ if (!resourceAliases.has(alias)) {
913
+ resourceAliases.set(alias, resourceId);
914
+ }
915
+ }
916
+ const aliases = getMetadataField(resource, 'resourceAliases');
917
+ if (aliases) {
918
+ for (const alias of aliases) {
919
+ resourceAliases.set(alias, resourceId);
920
+ }
921
+ }
922
+ }
923
+ }
924
+ const { dynamicFields } = separateStatusFields(userStatusMappings, nestedStatusCel, resourceIds);
132
925
  const statusCelExpressions = Object.keys(dynamicFields).length > 0
133
- ? serializeStatusMappingsToCel(dynamicFields, nestedStatusCel, resourceIds)
926
+ ? serializeStatusMappingsToCel(dynamicFields, nestedStatusCel, resourceIds, resourceAliases)
134
927
  : {};
135
928
  // Extract just the version part for the schema (Kro expects v1alpha1, not kro.run/v1alpha1)
136
929
  const schemaApiVersion = schemaDefinition.apiVersion.includes('/')
137
930
  ? schemaDefinition.apiVersion.split('/')[1] || schemaDefinition.apiVersion
138
931
  : schemaDefinition.apiVersion;
139
- return {
932
+ const schemaGroup = schemaDefinition.group ?? (schemaDefinition.apiVersion.includes('/')
933
+ ? schemaDefinition.apiVersion.split('/')[0]
934
+ : undefined);
935
+ const schema = {
140
936
  apiVersion: schemaApiVersion,
141
937
  kind: schemaDefinition.kind,
938
+ ...(schemaGroup && { group: schemaGroup }),
142
939
  spec: specFields,
143
940
  status: {
144
941
  ...statusCelExpressions,
145
942
  },
146
943
  };
944
+ // Attach metadata as non-enumerable properties — they're typed on
945
+ // KroSimpleSchemaWithMetadata so consumers can read them without casts,
946
+ // but non-enumerable so they don't appear in the schema YAML.
947
+ if (ternaryConditionals.length > 0) {
948
+ Object.defineProperty(schema, '__ternaryConditionals', {
949
+ value: ternaryConditionals,
950
+ enumerable: false,
951
+ });
952
+ }
953
+ if (omitFields.length > 0) {
954
+ Object.defineProperty(schema, '__omitFields', {
955
+ value: omitFields,
956
+ enumerable: false,
957
+ });
958
+ }
959
+ return schema;
147
960
  }
148
961
  /**
149
962
  * Generate a minimal Kro schema from a resource map (no Arktype).