@quadnix/octo 0.0.30 → 0.1.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 (362) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +25 -3
  3. package/dist/app.type.d.ts +143 -4
  4. package/dist/app.type.js +80 -9
  5. package/dist/app.type.js.map +1 -1
  6. package/dist/decorators/action.decorator.d.ts +10 -6
  7. package/dist/decorators/action.decorator.js +19 -6
  8. package/dist/decorators/action.decorator.js.map +1 -1
  9. package/dist/decorators/anchor.decorator.d.ts +12 -5
  10. package/dist/decorators/anchor.decorator.js +12 -5
  11. package/dist/decorators/anchor.decorator.js.map +1 -1
  12. package/dist/decorators/enable-hook.decorator.d.ts +1 -1
  13. package/dist/decorators/enable-hook.decorator.js +1 -1
  14. package/dist/decorators/factory.decorator.d.ts +18 -34
  15. package/dist/decorators/factory.decorator.js +18 -34
  16. package/dist/decorators/factory.decorator.js.map +1 -1
  17. package/dist/decorators/model.decorator.d.ts +1 -1
  18. package/dist/decorators/model.decorator.js +1 -1
  19. package/dist/decorators/module.decorator.d.ts +16 -10
  20. package/dist/decorators/module.decorator.js +16 -10
  21. package/dist/decorators/module.decorator.js.map +1 -1
  22. package/dist/decorators/on-event.decorator.d.ts +5 -0
  23. package/dist/decorators/on-event.decorator.js +5 -0
  24. package/dist/decorators/on-event.decorator.js.map +1 -1
  25. package/dist/decorators/validate.decorator.d.ts +15 -1
  26. package/dist/decorators/validate.decorator.js +15 -1
  27. package/dist/decorators/validate.decorator.js.map +1 -1
  28. package/dist/errors/container.error.d.ts +18 -0
  29. package/dist/errors/container.error.js +18 -0
  30. package/dist/errors/container.error.js.map +1 -1
  31. package/dist/errors/index.d.ts +1 -0
  32. package/dist/errors/index.js +1 -0
  33. package/dist/errors/index.js.map +1 -1
  34. package/dist/errors/module.error.d.ts +6 -0
  35. package/dist/errors/module.error.js +6 -0
  36. package/dist/errors/module.error.js.map +1 -1
  37. package/dist/errors/node.error.d.ts +56 -0
  38. package/dist/errors/node.error.js +56 -0
  39. package/dist/errors/node.error.js.map +1 -1
  40. package/dist/errors/schema.error.d.ts +6 -0
  41. package/dist/errors/schema.error.js +6 -0
  42. package/dist/errors/schema.error.js.map +1 -1
  43. package/dist/errors/terraform.error.d.ts +19 -0
  44. package/dist/errors/terraform.error.js +23 -0
  45. package/dist/errors/terraform.error.js.map +1 -0
  46. package/dist/errors/transaction.error.d.ts +61 -0
  47. package/dist/errors/transaction.error.js +61 -0
  48. package/dist/errors/transaction.error.js.map +1 -1
  49. package/dist/events/action.event.d.ts +0 -2
  50. package/dist/events/action.event.js +0 -2
  51. package/dist/events/action.event.js.map +1 -1
  52. package/dist/events/event.model.d.ts +21 -5
  53. package/dist/events/event.model.js +11 -5
  54. package/dist/events/event.model.js.map +1 -1
  55. package/dist/events/hook/commit.hook.event.d.ts +0 -6
  56. package/dist/events/hook/commit.hook.event.js +0 -6
  57. package/dist/events/hook/commit.hook.event.js.map +1 -1
  58. package/dist/events/hook/hook.event.d.ts +0 -2
  59. package/dist/events/hook/hook.event.js +0 -2
  60. package/dist/events/hook/hook.event.js.map +1 -1
  61. package/dist/events/hook/model.hook.event.d.ts +0 -6
  62. package/dist/events/hook/model.hook.event.js +0 -6
  63. package/dist/events/hook/model.hook.event.js.map +1 -1
  64. package/dist/events/hook/resource.hook.event.d.ts +0 -6
  65. package/dist/events/hook/resource.hook.event.js +0 -6
  66. package/dist/events/hook/resource.hook.event.js.map +1 -1
  67. package/dist/events/index.d.ts +3 -0
  68. package/dist/events/index.js +3 -0
  69. package/dist/events/index.js.map +1 -1
  70. package/dist/events/mode-result.event.d.ts +57 -0
  71. package/dist/events/mode-result.event.js +24 -0
  72. package/dist/events/mode-result.event.js.map +1 -0
  73. package/dist/events/mode.event.d.ts +57 -0
  74. package/dist/events/mode.event.js +32 -0
  75. package/dist/events/mode.event.js.map +1 -0
  76. package/dist/events/module.event.d.ts +0 -2
  77. package/dist/events/module.event.js +0 -2
  78. package/dist/events/module.event.js.map +1 -1
  79. package/dist/events/registration.event.d.ts +18 -14
  80. package/dist/events/registration.event.js +18 -14
  81. package/dist/events/registration.event.js.map +1 -1
  82. package/dist/events/serialization.event.d.ts +15 -12
  83. package/dist/events/serialization.event.js +15 -12
  84. package/dist/events/serialization.event.js.map +1 -1
  85. package/dist/events/terraform.event.d.ts +42 -0
  86. package/dist/events/terraform.event.js +31 -0
  87. package/dist/events/terraform.event.js.map +1 -0
  88. package/dist/events/transaction.event.d.ts +0 -37
  89. package/dist/events/transaction.event.js +0 -28
  90. package/dist/events/transaction.event.js.map +1 -1
  91. package/dist/functions/container/container.d.ts +54 -13
  92. package/dist/functions/container/container.js +63 -18
  93. package/dist/functions/container/container.js.map +1 -1
  94. package/dist/functions/container/test-container.d.ts +43 -29
  95. package/dist/functions/container/test-container.js +71 -43
  96. package/dist/functions/container/test-container.js.map +1 -1
  97. package/dist/functions/dependency/dependency.d.ts +82 -8
  98. package/dist/functions/dependency/dependency.js +80 -14
  99. package/dist/functions/dependency/dependency.js.map +1 -1
  100. package/dist/functions/diff/aggregate-resource-actions.d.ts +19 -0
  101. package/dist/functions/diff/aggregate-resource-actions.js +41 -0
  102. package/dist/functions/diff/aggregate-resource-actions.js.map +1 -0
  103. package/dist/functions/diff/diff-metadata.d.ts +21 -3
  104. package/dist/functions/diff/diff-metadata.js +18 -0
  105. package/dist/functions/diff/diff-metadata.js.map +1 -1
  106. package/dist/functions/diff/diff.d.ts +55 -1
  107. package/dist/functions/diff/diff.js +44 -3
  108. package/dist/functions/diff/diff.js.map +1 -1
  109. package/dist/functions/diff/diff.utility.d.ts +17 -2
  110. package/dist/functions/diff/diff.utility.js +53 -38
  111. package/dist/functions/diff/diff.utility.js.map +1 -1
  112. package/dist/functions/hook/commit.hook.d.ts +16 -1
  113. package/dist/functions/hook/commit.hook.js +21 -6
  114. package/dist/functions/hook/commit.hook.js.map +1 -1
  115. package/dist/functions/hook/model-action.hook.d.ts +16 -1
  116. package/dist/functions/hook/model-action.hook.js +21 -6
  117. package/dist/functions/hook/model-action.hook.js.map +1 -1
  118. package/dist/functions/hook/resource-action.hook.d.ts +16 -1
  119. package/dist/functions/hook/resource-action.hook.js +21 -6
  120. package/dist/functions/hook/resource-action.hook.js.map +1 -1
  121. package/dist/functions/node/node-action.abstract.d.ts +23 -0
  122. package/dist/functions/node/node-action.abstract.js +20 -0
  123. package/dist/functions/node/node-action.abstract.js.map +1 -1
  124. package/dist/functions/node/node.abstract.d.ts +56 -5
  125. package/dist/functions/node/node.abstract.js +85 -12
  126. package/dist/functions/node/node.abstract.js.map +1 -1
  127. package/dist/functions/node/node.interface.d.ts +1 -0
  128. package/dist/functions/schema/schema.d.ts +7 -0
  129. package/dist/functions/schema/schema.js +9 -0
  130. package/dist/functions/schema/schema.js.map +1 -1
  131. package/dist/index.d.ts +5 -1
  132. package/dist/index.js +4 -1
  133. package/dist/index.js.map +1 -1
  134. package/dist/main.d.ts +244 -14
  135. package/dist/main.js +361 -72
  136. package/dist/main.js.map +1 -1
  137. package/dist/models/account/account.model.d.ts +37 -7
  138. package/dist/models/account/account.model.js +40 -8
  139. package/dist/models/account/account.model.js.map +1 -1
  140. package/dist/models/account/account.schema.d.ts +13 -1
  141. package/dist/models/account/account.schema.js +12 -0
  142. package/dist/models/account/account.schema.js.map +1 -1
  143. package/dist/models/app/app.model.d.ts +16 -1
  144. package/dist/models/app/app.model.js +19 -4
  145. package/dist/models/app/app.model.js.map +1 -1
  146. package/dist/models/app/app.schema.d.ts +6 -0
  147. package/dist/models/app/app.schema.js +6 -0
  148. package/dist/models/app/app.schema.js.map +1 -1
  149. package/dist/models/deployment/deployment.model.d.ts +13 -1
  150. package/dist/models/deployment/deployment.model.js +16 -4
  151. package/dist/models/deployment/deployment.model.js.map +1 -1
  152. package/dist/models/deployment/deployment.schema.d.ts +2 -0
  153. package/dist/models/deployment/deployment.schema.js +2 -0
  154. package/dist/models/deployment/deployment.schema.js.map +1 -1
  155. package/dist/models/environment/environment.model.d.ts +13 -1
  156. package/dist/models/environment/environment.model.js +20 -8
  157. package/dist/models/environment/environment.model.js.map +1 -1
  158. package/dist/models/environment/environment.schema.d.ts +2 -0
  159. package/dist/models/environment/environment.schema.js +2 -0
  160. package/dist/models/environment/environment.schema.js.map +1 -1
  161. package/dist/models/execution/execution.model.d.ts +20 -2
  162. package/dist/models/execution/execution.model.js +46 -28
  163. package/dist/models/execution/execution.model.js.map +1 -1
  164. package/dist/models/execution/execution.schema.d.ts +11 -0
  165. package/dist/models/execution/execution.schema.js +11 -0
  166. package/dist/models/execution/execution.schema.js.map +1 -1
  167. package/dist/models/filesystem/filesystem.model.d.ts +27 -1
  168. package/dist/models/filesystem/filesystem.model.js +30 -4
  169. package/dist/models/filesystem/filesystem.model.js.map +1 -1
  170. package/dist/models/filesystem/filesystem.schema.d.ts +5 -0
  171. package/dist/models/filesystem/filesystem.schema.js +5 -0
  172. package/dist/models/filesystem/filesystem.schema.js.map +1 -1
  173. package/dist/models/image/image.model.d.ts +17 -4
  174. package/dist/models/image/image.model.js +20 -7
  175. package/dist/models/image/image.model.js.map +1 -1
  176. package/dist/models/image/image.schema.d.ts +8 -0
  177. package/dist/models/image/image.schema.js +8 -0
  178. package/dist/models/image/image.schema.js.map +1 -1
  179. package/dist/models/model-action.interface.d.ts +17 -4
  180. package/dist/models/model.abstract.d.ts +96 -1
  181. package/dist/models/model.abstract.js +106 -7
  182. package/dist/models/model.abstract.js.map +1 -1
  183. package/dist/models/model.interface.d.ts +3 -0
  184. package/dist/models/pipeline/pipeline.model.d.ts +13 -1
  185. package/dist/models/pipeline/pipeline.model.js +18 -6
  186. package/dist/models/pipeline/pipeline.model.js.map +1 -1
  187. package/dist/models/pipeline/pipeline.schema.d.ts +8 -0
  188. package/dist/models/pipeline/pipeline.schema.js +8 -0
  189. package/dist/models/pipeline/pipeline.schema.js.map +1 -1
  190. package/dist/models/region/region.model.d.ts +16 -1
  191. package/dist/models/region/region.model.js +19 -4
  192. package/dist/models/region/region.model.js.map +1 -1
  193. package/dist/models/region/region.schema.d.ts +6 -0
  194. package/dist/models/region/region.schema.js +6 -0
  195. package/dist/models/region/region.schema.js.map +1 -1
  196. package/dist/models/server/server.model.d.ts +13 -1
  197. package/dist/models/server/server.model.js +16 -4
  198. package/dist/models/server/server.model.js.map +1 -1
  199. package/dist/models/server/server.schema.d.ts +3 -1
  200. package/dist/models/server/server.schema.js +3 -1
  201. package/dist/models/server/server.schema.js.map +1 -1
  202. package/dist/models/service/service.model.d.ts +15 -1
  203. package/dist/models/service/service.model.js +15 -1
  204. package/dist/models/service/service.model.js.map +1 -1
  205. package/dist/models/service/service.schema.d.ts +4 -1
  206. package/dist/models/service/service.schema.js +4 -1
  207. package/dist/models/service/service.schema.js.map +1 -1
  208. package/dist/models/subnet/subnet.model.d.ts +27 -3
  209. package/dist/models/subnet/subnet.model.js +40 -16
  210. package/dist/models/subnet/subnet.model.js.map +1 -1
  211. package/dist/models/subnet/subnet.schema.d.ts +9 -0
  212. package/dist/models/subnet/subnet.schema.js +9 -0
  213. package/dist/models/subnet/subnet.schema.js.map +1 -1
  214. package/dist/modes/apply-transaction.d.ts +23 -0
  215. package/dist/modes/apply-transaction.js +25 -0
  216. package/dist/modes/apply-transaction.js.map +1 -0
  217. package/dist/modes/commit.mode.d.ts +54 -0
  218. package/dist/modes/commit.mode.js +235 -0
  219. package/dist/modes/commit.mode.js.map +1 -0
  220. package/dist/modes/generate.mode.d.ts +29 -0
  221. package/dist/modes/generate.mode.js +49 -0
  222. package/dist/modes/generate.mode.js.map +1 -0
  223. package/dist/modes/run-action.mode.d.ts +30 -0
  224. package/dist/modes/run-action.mode.js +140 -0
  225. package/dist/modes/run-action.mode.js.map +1 -0
  226. package/dist/modes/validate.mode.d.ts +162 -0
  227. package/dist/modes/validate.mode.js +480 -0
  228. package/dist/modes/validate.mode.js.map +1 -0
  229. package/dist/modules/module.abstract.d.ts +51 -0
  230. package/dist/modules/module.abstract.js +36 -0
  231. package/dist/modules/module.abstract.js.map +1 -1
  232. package/dist/modules/module.container.d.ts +36 -0
  233. package/dist/modules/module.container.js +36 -0
  234. package/dist/modules/module.container.js.map +1 -1
  235. package/dist/modules/test-module.container.d.ts +139 -25
  236. package/dist/modules/test-module.container.js +410 -178
  237. package/dist/modules/test-module.container.js.map +1 -1
  238. package/dist/overlays/anchor.abstract.d.ts +32 -1
  239. package/dist/overlays/anchor.abstract.js +42 -11
  240. package/dist/overlays/anchor.abstract.js.map +1 -1
  241. package/dist/overlays/anchor.schema.d.ts +21 -0
  242. package/dist/overlays/anchor.schema.js +21 -0
  243. package/dist/overlays/anchor.schema.js.map +1 -1
  244. package/dist/overlays/overlay-data.repository.d.ts +18 -0
  245. package/dist/overlays/overlay-data.repository.js +18 -0
  246. package/dist/overlays/overlay-data.repository.js.map +1 -1
  247. package/dist/overlays/overlay.abstract.d.ts +56 -1
  248. package/dist/overlays/overlay.abstract.js +79 -20
  249. package/dist/overlays/overlay.abstract.js.map +1 -1
  250. package/dist/overlays/overlay.schema.d.ts +20 -0
  251. package/dist/overlays/overlay.schema.js +20 -0
  252. package/dist/overlays/overlay.schema.js.map +1 -1
  253. package/dist/resources/resource-action.interface.d.ts +26 -2
  254. package/dist/resources/resource-data.repository.d.ts +63 -1
  255. package/dist/resources/resource-data.repository.js +94 -4
  256. package/dist/resources/resource-data.repository.js.map +1 -1
  257. package/dist/resources/resource.abstract.d.ts +118 -3
  258. package/dist/resources/resource.abstract.js +166 -33
  259. package/dist/resources/resource.abstract.js.map +1 -1
  260. package/dist/resources/resource.schema.d.ts +29 -1
  261. package/dist/resources/resource.schema.js +28 -0
  262. package/dist/resources/resource.schema.js.map +1 -1
  263. package/dist/resources/terraform-resource.abstract.d.ts +44 -0
  264. package/dist/resources/terraform-resource.abstract.js +45 -0
  265. package/dist/resources/terraform-resource.abstract.js.map +1 -0
  266. package/dist/services/event/event.service.d.ts +30 -3
  267. package/dist/services/event/event.service.js +58 -16
  268. package/dist/services/event/event.service.js.map +1 -1
  269. package/dist/services/input/input.service.d.ts +52 -3
  270. package/dist/services/input/input.service.js +72 -8
  271. package/dist/services/input/input.service.js.map +1 -1
  272. package/dist/services/schema-translation/schema-translation.service.d.ts +9 -0
  273. package/dist/services/schema-translation/schema-translation.service.js +9 -0
  274. package/dist/services/schema-translation/schema-translation.service.js.map +1 -1
  275. package/dist/services/serialization/model/model-serialization.service.d.ts +26 -2
  276. package/dist/services/serialization/model/model-serialization.service.js +110 -62
  277. package/dist/services/serialization/model/model-serialization.service.js.map +1 -1
  278. package/dist/services/serialization/resource/resource-serialization.service.d.ts +33 -4
  279. package/dist/services/serialization/resource/resource-serialization.service.js +64 -28
  280. package/dist/services/serialization/resource/resource-serialization.service.js.map +1 -1
  281. package/dist/services/state-management/file-app-lock.utility.d.ts +43 -0
  282. package/dist/services/state-management/file-app-lock.utility.js +124 -0
  283. package/dist/services/state-management/file-app-lock.utility.js.map +1 -0
  284. package/dist/services/state-management/local-encryption.state-provider.d.ts +53 -2
  285. package/dist/services/state-management/local-encryption.state-provider.js +75 -49
  286. package/dist/services/state-management/local-encryption.state-provider.js.map +1 -1
  287. package/dist/services/state-management/local.state-provider.d.ts +49 -2
  288. package/dist/services/state-management/local.state-provider.js +70 -49
  289. package/dist/services/state-management/local.state-provider.js.map +1 -1
  290. package/dist/services/state-management/state-management.service.d.ts +141 -6
  291. package/dist/services/state-management/state-management.service.js +149 -4
  292. package/dist/services/state-management/state-management.service.js.map +1 -1
  293. package/dist/services/state-management/state-provider.interface.d.ts +55 -1
  294. package/dist/services/state-management/terraform-state.service.d.ts +76 -0
  295. package/dist/services/state-management/terraform-state.service.js +154 -0
  296. package/dist/services/state-management/terraform-state.service.js.map +1 -0
  297. package/dist/services/state-management/test.state-provider.d.ts +38 -1
  298. package/dist/services/state-management/test.state-provider.js +52 -21
  299. package/dist/services/state-management/test.state-provider.js.map +1 -1
  300. package/dist/services/terraform/terraform.service.d.ts +426 -0
  301. package/dist/services/terraform/terraform.service.js +995 -0
  302. package/dist/services/terraform/terraform.service.js.map +1 -0
  303. package/dist/services/terraform/terraform.type.d.ts +387 -0
  304. package/dist/services/terraform/terraform.type.js +579 -0
  305. package/dist/services/terraform/terraform.type.js.map +1 -0
  306. package/dist/services/transaction/transaction.service.d.ts +86 -10
  307. package/dist/services/transaction/transaction.service.js +387 -232
  308. package/dist/services/transaction/transaction.service.js.map +1 -1
  309. package/dist/services/validation/validation.service.d.ts +13 -1
  310. package/dist/services/validation/validation.service.js +12 -0
  311. package/dist/services/validation/validation.service.js.map +1 -1
  312. package/dist/utilities/array/array.utility.d.ts +3 -0
  313. package/dist/utilities/array/array.utility.js +3 -0
  314. package/dist/utilities/array/array.utility.js.map +1 -1
  315. package/dist/utilities/file/file.utility.d.ts +15 -0
  316. package/dist/utilities/file/file.utility.js +18 -1
  317. package/dist/utilities/file/file.utility.js.map +1 -1
  318. package/dist/utilities/hcl/hcl.utility.d.ts +61 -0
  319. package/dist/utilities/hcl/hcl.utility.js +229 -0
  320. package/dist/utilities/hcl/hcl.utility.js.map +1 -0
  321. package/dist/utilities/node/node.utility.d.ts +3 -0
  322. package/dist/utilities/node/node.utility.js +3 -0
  323. package/dist/utilities/node/node.utility.js.map +1 -1
  324. package/dist/utilities/object/object.utility.d.ts +12 -0
  325. package/dist/utilities/object/object.utility.js +12 -0
  326. package/dist/utilities/object/object.utility.js.map +1 -1
  327. package/dist/utilities/project/project.utility.d.ts +28 -0
  328. package/dist/utilities/project/project.utility.js +35 -0
  329. package/dist/utilities/project/project.utility.js.map +1 -0
  330. package/dist/utilities/string/string.utility.d.ts +21 -0
  331. package/dist/utilities/string/string.utility.js +39 -0
  332. package/dist/utilities/string/string.utility.js.map +1 -0
  333. package/dist/utilities/terraform/terraform.utility.d.ts +227 -0
  334. package/dist/utilities/terraform/terraform.utility.js +434 -0
  335. package/dist/utilities/terraform/terraform.utility.js.map +1 -0
  336. package/dist/utilities/test-helpers/test-classes.d.ts +18 -31
  337. package/dist/utilities/test-helpers/test-classes.js +18 -41
  338. package/dist/utilities/test-helpers/test-classes.js.map +1 -1
  339. package/dist/utilities/test-helpers/test-models.js +12 -0
  340. package/dist/utilities/test-helpers/test-models.js.map +1 -1
  341. package/dist/utilities/test-helpers/test-modes.d.ts +315 -0
  342. package/dist/utilities/test-helpers/test-modes.js +463 -0
  343. package/dist/utilities/test-helpers/test-modes.js.map +1 -0
  344. package/dist/utilities/test-helpers/test-modules.d.ts +41 -0
  345. package/dist/utilities/test-helpers/test-modules.js +79 -0
  346. package/dist/utilities/test-helpers/test-modules.js.map +1 -0
  347. package/dist/utilities/test-helpers/test-overlays.d.ts +17 -1
  348. package/dist/utilities/test-helpers/test-overlays.js +49 -5
  349. package/dist/utilities/test-helpers/test-overlays.js.map +1 -1
  350. package/dist/utilities/test-helpers/test-resources.d.ts +19 -1
  351. package/dist/utilities/test-helpers/test-resources.js +61 -3
  352. package/dist/utilities/test-helpers/test-resources.js.map +1 -1
  353. package/dist/utilities/validation/validation.utility.d.ts +24 -0
  354. package/dist/utilities/validation/validation.utility.js +24 -0
  355. package/dist/utilities/validation/validation.utility.js.map +1 -1
  356. package/package.json +6 -5
  357. package/dist/services/capture/capture.service.d.ts +0 -19
  358. package/dist/services/capture/capture.service.js +0 -43
  359. package/dist/services/capture/capture.service.js.map +0 -1
  360. package/dist/utilities/cidr/cidr.utility.d.ts +0 -10
  361. package/dist/utilities/cidr/cidr.utility.js +0 -51
  362. package/dist/utilities/cidr/cidr.utility.js.map +0 -1
@@ -11,7 +11,7 @@ import { NodeType, ValidationType, } from '../../app.type.js';
11
11
  import { EventSource } from '../../decorators/event-source.decorator.js';
12
12
  import { Factory } from '../../decorators/factory.decorator.js';
13
13
  import { InputNotFoundTransactionError, InputRegistrationError, ResourceActionExceptionTransactionError, ResourceActionTimeoutTransactionError, TransactionError, ValidationTransactionError, } from '../../errors/index.js';
14
- import { ModelActionRegistrationEvent, ModelActionTransactionEvent, ModelDiffsTransactionEvent, ModelTransactionTransactionEvent, ResourceActionCompletedTransactionEvent, ResourceActionInformationTransactionEvent, ResourceActionInitiatedTransactionEvent, ResourceActionRegistrationEvent, ResourceActionSummaryTransactionEvent, ResourceDiffsTransactionEvent, ResourceTransactionTransactionEvent, } from '../../events/index.js';
14
+ import { ModelActionRegistrationEvent, ModelActionTransactionEvent, ModelDiffsTransactionEvent, ModelTransactionTransactionEvent, ResourceActionCompletedTransactionEvent, ResourceActionInitiatedTransactionEvent, ResourceActionRegistrationEvent, ResourceActionSummaryTransactionEvent, ResourceDiffsTransactionEvent, ResourceTransactionTransactionEvent, } from '../../events/index.js';
15
15
  import { Container } from '../../functions/container/container.js';
16
16
  import { DiffMetadata } from '../../functions/diff/diff-metadata.js';
17
17
  import { DiffAction } from '../../functions/diff/diff.js';
@@ -21,24 +21,293 @@ import { OverlayDataRepository } from '../../overlays/overlay-data.repository.js
21
21
  import { AOverlay } from '../../overlays/overlay.abstract.js';
22
22
  import { ResourceDataRepository } from '../../resources/resource-data.repository.js';
23
23
  import { AResource } from '../../resources/resource.abstract.js';
24
- import { CaptureService } from '../capture/capture.service.js';
24
+ import { ATerraformResource } from '../../resources/terraform-resource.abstract.js';
25
25
  import { EventService } from '../event/event.service.js';
26
26
  import { InputService } from '../input/input.service.js';
27
+ import { TerraformService } from '../terraform/terraform.service.js';
28
+ /**
29
+ * No-op resource action backing terraform resource diffs. Terraform manages the resource's lifecycle;
30
+ * octo only carries the diff for review and validation.
31
+ *
32
+ * @internal
33
+ */
34
+ class TerraformNoopResourceAction {
35
+ /**
36
+ * @internal
37
+ */
38
+ filter() {
39
+ return true;
40
+ }
41
+ /**
42
+ * @internal
43
+ */
44
+ async handle() { }
45
+ }
27
46
  /**
28
47
  * @internal
29
48
  */
30
49
  export class TransactionService {
31
- constructor(captureService, eventService, inputService, moduleContainer, overlayDataRepository, resourceDataRepository) {
32
- this.captureService = captureService;
50
+ /**
51
+ * @internal
52
+ */
53
+ constructor(eventService, inputService, moduleContainer, overlayDataRepository, resourceDataRepository, terraformService) {
33
54
  this.eventService = eventService;
34
55
  this.inputService = inputService;
35
56
  this.moduleContainer = moduleContainer;
36
57
  this.overlayDataRepository = overlayDataRepository;
37
58
  this.resourceDataRepository = resourceDataRepository;
59
+ this.terraformService = terraformService;
38
60
  this.modelActions = [];
39
61
  this.overlayActions = [];
40
62
  this.resourceActions = [];
41
63
  }
64
+ /**
65
+ * Runs the delete action of every **external** resource in `resourceDiffs` that is being deleted.
66
+ *
67
+ * Terraform cannot do this. Octo deletes a resource by removing its block from the configuration,
68
+ * and terraform will not run a `when = destroy` provisioner for a resource that is no longer in
69
+ * the configuration - it drops the `null_resource` from state and moves on. So the callback that
70
+ * would have told an external resource to delete itself never fires, and the real thing it created
71
+ * outlives both state files with nothing tracking it.
72
+ *
73
+ * Octo knows the resource was deleted, which is why this can be driven from its own diff instead.
74
+ * {@link commit} calls it before recording anything, so a failed delete leaves octo state untouched
75
+ * and the deletion is retried on the next run rather than being forgotten.
76
+ *
77
+ * Terraform-managed resources are excluded: terraform destroys those itself, and a resource action
78
+ * on one is rejected everywhere else in octo.
79
+ *
80
+ * @returns The diffs whose actions ran, in the order they ran.
81
+ */
82
+ async applyExternalResourceDeletes(resourceDiffs) {
83
+ const deleteDiffs = this.getExternalResourceDeletes(resourceDiffs);
84
+ if (deleteDiffs.length === 0) {
85
+ return [];
86
+ }
87
+ // Ordered among themselves: `applyOrder` is assigned across the whole resource diff set, and
88
+ // this is a subset of it, so the levels it carries can start anywhere. Re-basing them from zero
89
+ // keeps applyResources' level walk terminating.
90
+ const orders = [...new Set(deleteDiffs.map((d) => d.applyOrder))].sort((a, b) => a - b);
91
+ for (const diff of deleteDiffs) {
92
+ diff.applyOrder = orders.indexOf(diff.applyOrder);
93
+ }
94
+ // Stateless, like run-action: the actual resource graph is not touched here. `commit` syncs
95
+ // actual to new immediately afterwards, which is what records the deletion.
96
+ return this.applyResources(deleteDiffs, { skipActualResourceUpdate: true });
97
+ }
98
+ /**
99
+ * @internal
100
+ */
101
+ async *beginTransaction(diffs, { enableResourceValidation = false, filterResourceDiffsByResourceId = undefined, generateTerraform = false, skipActualResourceUpdate = false, yieldModelDiffs = false, yieldModelTransaction = false, yieldResourceDiffs = false, yieldResourceTransaction = false, } = {}) {
102
+ // Diff overlays and add to existing diffs.
103
+ const overlayDiffs = await this.overlayDataRepository.diff();
104
+ diffs.push(...overlayDiffs);
105
+ // Generate diff on models.
106
+ const modelDiffs = diffs.map((d) => {
107
+ if (d.node.constructor.NODE_TYPE === NodeType.OVERLAY) {
108
+ return new DiffMetadata(d, (this.overlayActions.find((a) => a.overlayClass === d.node.constructor)?.actions || []).filter((a) => a.filter(d)));
109
+ }
110
+ else {
111
+ return new DiffMetadata(d, (this.modelActions.find((a) => a.modelClass === d.node.constructor)?.actions || []).filter((a) => a.filter(d)));
112
+ }
113
+ });
114
+ // Set apply order on model diffs.
115
+ for (const diff of modelDiffs) {
116
+ this.setApplyOrder(diff, modelDiffs);
117
+ }
118
+ this.eventService.emit(new ModelDiffsTransactionEvent(undefined, [modelDiffs]));
119
+ if (yieldModelDiffs) {
120
+ yield [modelDiffs];
121
+ }
122
+ // Apply model diffs.
123
+ const modelTransaction = await this.applyModels(modelDiffs);
124
+ this.eventService.emit(new ModelTransactionTransactionEvent(undefined, modelTransaction));
125
+ if (yieldModelTransaction) {
126
+ yield modelTransaction;
127
+ }
128
+ // Generate resource diffs.
129
+ let newDiffs = await this.resourceDataRepository.diff();
130
+ let dirtyDiffs = await this.resourceDataRepository.diffDirty();
131
+ // Scope diffs to a single resource, e.g. when terraform invokes octo for one resource action.
132
+ if (filterResourceDiffsByResourceId !== undefined) {
133
+ newDiffs = newDiffs.filter((d) => d.node.resourceId === filterResourceDiffsByResourceId);
134
+ dirtyDiffs = dirtyDiffs.filter((d) => d.node.resourceId === filterResourceDiffsByResourceId);
135
+ // Prefer dirty diffs (actual-graph nodes) over equal new diffs (old-graph nodes) for
136
+ // execution: deserialized old resources are frozen and cannot receive injected inputs.
137
+ for (let i = newDiffs.length - 1; i >= 0; i--) {
138
+ const newDiff = newDiffs[i];
139
+ if (dirtyDiffs.some((d) => d.node.getContext() === newDiff.node.getContext() &&
140
+ d.action === newDiff.action &&
141
+ d.field === newDiff.field &&
142
+ DiffUtility.isObjectDeepEquals(d.value, newDiff.value))) {
143
+ newDiffs.splice(i, 1);
144
+ }
145
+ }
146
+ }
147
+ if (enableResourceValidation && (newDiffs.length > 0 || dirtyDiffs.length > 0)) {
148
+ throw new TransactionError('Cannot run resource validation with pending resource diffs!');
149
+ }
150
+ if (enableResourceValidation) {
151
+ const validationDiffs = this.resourceDataRepository.diffValidate();
152
+ newDiffs.push(...validationDiffs);
153
+ dirtyDiffs.push(...validationDiffs);
154
+ }
155
+ // new diffs = new - old | dirty diffs = new - actual
156
+ // Any new diff that is also not part of dirty diffs should be skipped, as the actual is already in desired state.
157
+ for (let i = newDiffs.length - 1; i >= 0; i--) {
158
+ const newDiff = newDiffs[i];
159
+ if (!dirtyDiffs.some((d) => d.node.getContext() === newDiff.node.getContext() &&
160
+ d.action === newDiff.action &&
161
+ d.field === newDiff.field &&
162
+ DiffUtility.isObjectDeepEquals(d.value, newDiff.value))) {
163
+ newDiffs.splice(i, 1);
164
+ }
165
+ }
166
+ // Ensure any new diffs are not operating on dirty resources.
167
+ this.resourceDataRepository.ensureDiffsNotOperatingOnDirtyResources(newDiffs);
168
+ // Skip processing dirty diffs that are already accounted for in new diffs.
169
+ for (let i = dirtyDiffs.length - 1; i >= 0; i--) {
170
+ const dirtyDiff = dirtyDiffs[i];
171
+ if (newDiffs.some((d) => d.node.getContext() === dirtyDiff.node.getContext() &&
172
+ d.action === dirtyDiff.action &&
173
+ d.field === dirtyDiff.field &&
174
+ DiffUtility.isObjectDeepEquals(d.value, dirtyDiff.value))) {
175
+ dirtyDiffs.splice(i, 1);
176
+ }
177
+ }
178
+ // Terraform resources have no resource actions - terraform owns their lifecycle. Their diffs
179
+ // remain part of the transaction as review artifacts, backed by a no-op action.
180
+ const resolveResourceActions = (d) => {
181
+ if (d.node instanceof ATerraformResource) {
182
+ return [new TerraformNoopResourceAction()];
183
+ }
184
+ return (this.resourceActions.find((a) => a.resourceClass === d.node.constructor)?.actions || []).filter((a) => a.filter(d));
185
+ };
186
+ // Generate diff on resources.
187
+ const resourceDiffs = newDiffs.map((d) => new DiffMetadata(d, resolveResourceActions(d)));
188
+ // Set apply order on resource diffs.
189
+ for (const diff of resourceDiffs) {
190
+ this.setApplyOrder(diff, resourceDiffs);
191
+ }
192
+ // Generate diff on dirty resources.
193
+ const dirtyResourceDiffs = dirtyDiffs.map((d) => new DiffMetadata(d, resolveResourceActions(d)));
194
+ // Set apply order on dirty resource diffs.
195
+ for (const diff of dirtyResourceDiffs) {
196
+ this.setApplyOrder(diff, dirtyResourceDiffs);
197
+ }
198
+ // Contribute the full desired resource graph to terraform. Deferred until after resource diffs
199
+ // are computed and ordered, so an author refusal (a throw in diff*) or an ordering/cycle error
200
+ // aborts generation before any HCL is built — diffs gate generation.
201
+ if (generateTerraform) {
202
+ await this.generateTerraform();
203
+ }
204
+ this.eventService.emit(new ResourceDiffsTransactionEvent(undefined, [[resourceDiffs], [dirtyResourceDiffs]]));
205
+ if (yieldResourceDiffs) {
206
+ yield [resourceDiffs, dirtyResourceDiffs];
207
+ }
208
+ // Apply resource diffs.
209
+ const resourceTransaction = await this.applyResources(resourceDiffs, { skipActualResourceUpdate });
210
+ // Apply dirty resource diffs.
211
+ const dirtyResourceTransaction = await this.applyResources(dirtyResourceDiffs, { skipActualResourceUpdate });
212
+ this.eventService.emit(new ResourceTransactionTransactionEvent(undefined, [resourceTransaction, dirtyResourceTransaction]));
213
+ if (yieldResourceTransaction) {
214
+ yield [...resourceTransaction, ...dirtyResourceTransaction];
215
+ }
216
+ return modelTransaction;
217
+ }
218
+ /**
219
+ * The diffs {@link TransactionService.applyExternalResourceDeletes} runs: the deletion of every
220
+ * external resource in `resourceDiffs`. A caller that has to decide whether to run them at all
221
+ * names them through this rather than repeating the filter.
222
+ *
223
+ * @internal
224
+ */
225
+ getExternalResourceDeletes(resourceDiffs) {
226
+ return resourceDiffs
227
+ .flat()
228
+ .filter((d) => d.action === DiffAction.DELETE &&
229
+ d.field === 'resourceId' &&
230
+ !(d.node instanceof ATerraformResource) &&
231
+ !d.applied);
232
+ }
233
+ /**
234
+ * @internal
235
+ */
236
+ registerModelActions(forModel, actions) {
237
+ const modelActions = this.modelActions.find((a) => a.modelClass === forModel);
238
+ if (!modelActions) {
239
+ this.modelActions.push({ actions: actions, modelClass: forModel });
240
+ }
241
+ else {
242
+ for (const action of actions) {
243
+ if (modelActions.actions.find((a) => a.constructor.name === action.constructor.name)) {
244
+ throw new Error(`Action "${action.constructor.name}" already registered for model "${forModel.name}"!`);
245
+ }
246
+ modelActions.actions.push(action);
247
+ }
248
+ }
249
+ }
250
+ /**
251
+ * @internal
252
+ */
253
+ registerOverlayActions(forOverlay, actions) {
254
+ const overlayActions = this.overlayActions.find((a) => a.overlayClass === forOverlay);
255
+ if (!overlayActions) {
256
+ this.overlayActions.push({ actions: actions, overlayClass: forOverlay });
257
+ }
258
+ else {
259
+ for (const action of actions) {
260
+ if (overlayActions.actions.find((a) => a.constructor.name === action.constructor.name)) {
261
+ throw new Error(`Action "${action.constructor.name}" already registered for overlay "${forOverlay.name}"!`);
262
+ }
263
+ overlayActions.actions.push(action);
264
+ }
265
+ }
266
+ }
267
+ /**
268
+ * @internal
269
+ */
270
+ registerResourceActions(forResource, actions) {
271
+ if (forResource.prototype instanceof ATerraformResource) {
272
+ throw new TransactionError(`Cannot register resource actions for terraform resource "${forResource.name}"! ` +
273
+ 'Terraform resources are managed by terraform via toHCL().');
274
+ }
275
+ const resourceActions = this.resourceActions.find((a) => a.resourceClass === forResource);
276
+ if (!resourceActions) {
277
+ this.resourceActions.push({ actions: actions, resourceClass: forResource });
278
+ }
279
+ else {
280
+ for (const action of actions) {
281
+ if (resourceActions.actions.find((a) => a.constructor.name === action.constructor.name)) {
282
+ throw new Error(`Action "${action.constructor.name}" already registered for resource "${forResource.name}"!`);
283
+ }
284
+ resourceActions.actions.push(action);
285
+ }
286
+ }
287
+ }
288
+ /**
289
+ * @internal
290
+ */
291
+ unregisterModelActions(forModel) {
292
+ const modelActions = this.modelActions.find((a) => a.modelClass === forModel);
293
+ if (!modelActions) {
294
+ return;
295
+ }
296
+ modelActions.actions = [];
297
+ }
298
+ /**
299
+ * @internal
300
+ */
301
+ unregisterOverlayActions(forOverlay) {
302
+ const overlayActions = this.overlayActions.find((a) => a.overlayClass === forOverlay);
303
+ if (!overlayActions) {
304
+ return;
305
+ }
306
+ overlayActions.actions = [];
307
+ }
308
+ /**
309
+ * @internal
310
+ */
42
311
  async applyModels(diffs) {
43
312
  const transaction = [];
44
313
  let currentApplyOrder = 0;
@@ -56,12 +325,11 @@ export class TransactionService {
56
325
  const diffToProcess = duplicateDiffs[0].diff;
57
326
  for (const a of diff.actions) {
58
327
  // Find moduleId.
59
- let moduleId;
60
- if (diff.node instanceof AOverlay) {
61
- moduleId = this.inputService.getModuleIdFromOverlay(diff.node);
62
- }
63
- else {
64
- moduleId = this.inputService.getModuleIdFromModel(diff.node);
328
+ const moduleId = diff.node instanceof AOverlay
329
+ ? this.inputService.getModuleIdFromOverlay(diff.node)
330
+ : this.inputService.getModuleIdFromModel(diff.node);
331
+ if (moduleId === undefined) {
332
+ throw new TransactionError(`Node "${diff.node.getContext()}" is not associated with any module!`);
65
333
  }
66
334
  // Resolve all keys of the module - inputs keys and nodes generated by the module keys.
67
335
  const inputs = { inputs: {}, metadata: {}, models: {}, overlays: {}, resources: {} };
@@ -96,12 +364,11 @@ export class TransactionService {
96
364
  // Apply all actions on the diff, then update diff metadata with inputs and outputs.
97
365
  const outputs = await a.handle(diffToProcess, inputs, {});
98
366
  for (const resource of Object.values(outputs)) {
99
- let moduleId;
100
- if (diffToProcess.node instanceof AOverlay) {
101
- moduleId = this.inputService.getModuleIdFromOverlay(diffToProcess.node);
102
- }
103
- else {
104
- moduleId = this.inputService.getModuleIdFromModel(diffToProcess.node);
367
+ const moduleId = diffToProcess.node instanceof AOverlay
368
+ ? this.inputService.getModuleIdFromOverlay(diffToProcess.node)
369
+ : this.inputService.getModuleIdFromModel(diffToProcess.node);
370
+ if (moduleId === undefined) {
371
+ throw new TransactionError(`Node "${diffToProcess.node.getContext()}" is not associated with any module!`);
105
372
  }
106
373
  // Set resource tags.
107
374
  const resourceTags = this.inputService.resolve(`${moduleId}.tag.${resource.getContext()}`);
@@ -142,34 +409,10 @@ export class TransactionService {
142
409
  }
143
410
  return transaction;
144
411
  }
145
- applyResourcesDryRun(diffs, { enableResourceCapture = false } = {}) {
146
- const captures = this.captureService.getAllCaptures();
147
- const captureContexts = Object.keys(captures);
148
- const missingCaptureContexts = [];
149
- if (enableResourceCapture) {
150
- for (const capturedContext of captureContexts) {
151
- if (!diffs.some((d) => d.node.getContext() === capturedContext)) {
152
- missingCaptureContexts.push(capturedContext);
153
- }
154
- }
155
- }
156
- if (missingCaptureContexts.length > 0) {
157
- throw new TransactionError(`Provided capture contexts do not match any resource diffs! ${missingCaptureContexts.join(', ')}`);
158
- }
159
- for (const diff of diffs) {
160
- const capture = enableResourceCapture && captureContexts.includes(diff.node.getContext())
161
- ? this.captureService.getCapture(diff.node.getContext())
162
- : undefined;
163
- for (const a of diff.actions) {
164
- this.eventService.emit(new ResourceActionInformationTransactionEvent(a.constructor.name, {
165
- action: { name: a.constructor.name },
166
- capture: a.mock ? capture : undefined,
167
- diff,
168
- }));
169
- }
170
- }
171
- }
172
- async applyResources(diffs, { enableResourceCapture = false } = {}) {
412
+ /**
413
+ * @internal
414
+ */
415
+ async applyResources(diffs, { skipActualResourceUpdate = false } = {}) {
173
416
  const transaction = [];
174
417
  const validationErrors = [];
175
418
  let currentApplyOrder = 0;
@@ -187,47 +430,66 @@ export class TransactionService {
187
430
  const diffToProcess = duplicateDiffs[0].diff;
188
431
  for (const a of diff.actions) {
189
432
  this.eventService.emit(new ResourceActionInitiatedTransactionEvent(a.constructor.name, diff));
190
- const capture = this.captureService.getCapture(diff.node.getContext());
191
- if (enableResourceCapture && capture?.response && a.mock) {
192
- const response = await a.mock(diff, capture.response);
193
- this.resourceDataRepository.getNewResourceByContext(diffToProcess.node.getContext())?.setResponse(response);
194
- }
195
- else {
196
- let actionTimeoutId;
197
- try {
198
- await Promise.race([
199
- new Promise(async (resolve, reject) => {
200
- try {
201
- const response = await a.handle(diffToProcess);
202
- this.resourceDataRepository
203
- .getNewResourceByContext(diffToProcess.node.getContext())
204
- ?.setResponse(response);
433
+ // Captured before `handle()` mutates the resource, for the summary event below.
434
+ const preHandleSynth = skipActualResourceUpdate
435
+ ? this.resourceDataRepository.getNewResourceByContext(diffToProcess.node.getContext())?.synth()
436
+ : undefined;
437
+ let actionTimeoutId;
438
+ try {
439
+ await Promise.race([
440
+ new Promise(async (resolve, reject) => {
441
+ try {
442
+ const response = await a.handle(diffToProcess);
443
+ this.resourceDataRepository
444
+ .getNewResourceByContext(diffToProcess.node.getContext())
445
+ ?.setResponse(response);
446
+ resolve();
447
+ }
448
+ catch (error) {
449
+ if (diffToProcess.action === DiffAction.VALIDATE) {
450
+ validationErrors.push(error);
205
451
  resolve();
206
452
  }
207
- catch (error) {
208
- if (diffToProcess.action === DiffAction.VALIDATE) {
209
- validationErrors.push(error);
210
- resolve();
211
- }
212
- else {
213
- reject(new ResourceActionExceptionTransactionError(error.message, error, diffToProcess, a.constructor.name));
214
- }
453
+ else {
454
+ reject(new ResourceActionExceptionTransactionError(error.message, error, diffToProcess, a.constructor.name));
215
455
  }
216
- }),
217
- new Promise((_resolve, reject) => (actionTimeoutId = setTimeout(() => {
218
- reject(new ResourceActionTimeoutTransactionError(`Resource action ${a.constructor.name} timed out after ${a.actionTimeoutInMs || 90000}ms!`, diffToProcess, a.constructor.name));
219
- }, a.actionTimeoutInMs || 90000))),
220
- ]);
221
- }
222
- finally {
223
- if (actionTimeoutId) {
224
- clearTimeout(actionTimeoutId);
225
- }
456
+ }
457
+ }),
458
+ new Promise((_resolve, reject) => (actionTimeoutId = setTimeout(() => {
459
+ reject(new ResourceActionTimeoutTransactionError(`Resource action ${a.constructor.name} timed out after ${a.actionTimeoutInMs || 90000}ms!`, diffToProcess, a.constructor.name));
460
+ }, a.actionTimeoutInMs || 90000))),
461
+ ]);
462
+ }
463
+ finally {
464
+ if (actionTimeoutId) {
465
+ clearTimeout(actionTimeoutId);
226
466
  }
227
467
  }
228
- // De-reference from actual resources.
468
+ if (skipActualResourceUpdate) {
469
+ // Stateless invocation (run-action): the actual resource graph is not maintained;
470
+ // terraform state is the source of truth until the next commit. The summary is therefore
471
+ // derived from the new-graph resource either side of `handle()`.
472
+ const postHandleSynth = diffToProcess.action === DiffAction.DELETE && diffToProcess.field === 'resourceId'
473
+ ? {}
474
+ : this.resourceDataRepository.getNewResourceByContext(diffToProcess.node.getContext())?.synth();
475
+ this.eventService.emit(new ResourceActionSummaryTransactionEvent(a.constructor.name, {
476
+ diff,
477
+ values: { current: postHandleSynth ?? {}, previous: preHandleSynth ?? {} },
478
+ }));
479
+ this.eventService.emit(new ResourceActionCompletedTransactionEvent(a.constructor.name, diff));
480
+ continue;
481
+ }
482
+ // De-reference from actual resources. The lookup can miss when a diff references a resource
483
+ // this transaction has not applied yet, so name what is missing rather than letting
484
+ // `undefined` surface as a TypeError several frames away.
229
485
  const deReferenceResource = async (context) => {
230
- return this.resourceDataRepository.getActualResourceByContext(context);
486
+ const referencedResource = this.resourceDataRepository.getActualResourceByContext(context);
487
+ if (!referencedResource) {
488
+ throw new TransactionError(`Cannot apply "${diffToProcess.action}" on field "${String(diffToProcess.field)}" of resource ` +
489
+ `"${diffToProcess.node.getContext()}": it references "${context}", which is not in the actual ` +
490
+ 'resource graph yet. The referenced resource is applied later in this transaction, or not at all.');
491
+ }
492
+ return referencedResource;
231
493
  };
232
494
  // Incrementally apply diff inverse to the respective actual resource.
233
495
  let actualResource = this.resourceDataRepository.getActualResourceByContext(diffToProcess.node.getContext());
@@ -274,15 +536,52 @@ export class TransactionService {
274
536
  }
275
537
  return transaction;
276
538
  }
539
+ /**
540
+ * Contributes every resource in the new (desired) resource graph to the shared
541
+ * {@link TerraformService}: terraform resources via `toHCL()`, external resources via
542
+ * `addOctoTerraformExternalResource()`. Always processes the full desired state — terraform
543
+ * computes the actual create/update/delete against its own state.
544
+ *
545
+ * Order does not matter: terraform resources reference each other through deferred refs, and
546
+ * external resources defer their parent-input wiring — both are resolved later, once every
547
+ * resource is registered (see {@link TerraformService}'s wiring phase).
548
+ */
549
+ async generateTerraform() {
550
+ const resources = this.resourceDataRepository.getNewResourcesByProperties().filter((r) => !r.isMarkedDeleted());
551
+ for (const resource of resources) {
552
+ // Every resource is produced by a model action inside a module, so it is always module-bound.
553
+ // A missing attribution means the resource was registered out-of-band — a hard error.
554
+ const moduleId = this.inputService.getModuleIdFromResource(resource);
555
+ if (moduleId === undefined) {
556
+ throw new TransactionError(`Resource "${resource.resourceId}" is not associated with any module!`);
557
+ }
558
+ const scope = this.terraformService.scope(moduleId);
559
+ if (resource instanceof ATerraformResource) {
560
+ await resource.toHCL(scope);
561
+ }
562
+ else {
563
+ scope.addOctoTerraformExternalResource(resource);
564
+ }
565
+ }
566
+ }
567
+ /**
568
+ * @internal
569
+ */
277
570
  getDiffsOfSameNode(diff, diffs, diffAction) {
278
571
  return diffs.filter((d) => d.node.getContext() === diff.node.getContext() && d.action === diffAction);
279
572
  }
573
+ /**
574
+ * @internal
575
+ */
280
576
  getDuplicateDiffs(diff, diffs) {
281
577
  return diffs.filter((d) => d.node.getContext() === diff.node.getContext() &&
282
578
  d.field === diff.field &&
283
579
  d.action === diff.action &&
284
580
  DiffUtility.isObjectDeepEquals(d.value, diff.value));
285
581
  }
582
+ /**
583
+ * @internal
584
+ */
286
585
  getMatchingDiffs(diff, diffs) {
287
586
  return diffs.filter((d) => d.node.getContext() === diff.node.getContext() &&
288
587
  d.field === diff.field &&
@@ -324,8 +623,9 @@ export class TransactionService {
324
623
  }
325
624
  }
326
625
  // Get diffs of same node.
327
- // Process ADD before UPDATE, and UPDATE before DELETE.
328
- if (diff.action === DiffAction.UPDATE) {
626
+ // Process ADD before UPDATE/REPLACE, and UPDATE/REPLACE before DELETE. A REPLACE recreates the
627
+ // resource in place (same context), so for ordering it behaves like an UPDATE.
628
+ if (diff.action === DiffAction.UPDATE || diff.action === DiffAction.REPLACE) {
329
629
  const diffsOfSameNodeWithAddAction = this.getDiffsOfSameNode(diff, diffs, DiffAction.ADD);
330
630
  for (const sameNodeDiffWithAddAction of diffsOfSameNodeWithAddAction) {
331
631
  this.setApplyOrder(sameNodeDiffWithAddAction, diffs, [...seen, diff]);
@@ -341,154 +641,6 @@ export class TransactionService {
341
641
  }
342
642
  diff.applyOrder = Math.max(...dependencyApplyOrders) + 1;
343
643
  }
344
- async *beginTransaction(diffs, { enableResourceCapture = false, enableResourceValidation = false, yieldModelDiffs = false, yieldModelTransaction = false, yieldResourceDiffs = false, yieldResourceTransaction = false, } = {}) {
345
- // Diff overlays and add to existing diffs.
346
- const overlayDiffs = await this.overlayDataRepository.diff();
347
- diffs.push(...overlayDiffs);
348
- // Generate diff on models.
349
- const modelDiffs = diffs.map((d) => {
350
- if (d.node.constructor.NODE_TYPE === NodeType.OVERLAY) {
351
- return new DiffMetadata(d, (this.overlayActions.find((a) => a.overlayClass === d.node.constructor)?.actions || []).filter((a) => a.filter(d)));
352
- }
353
- else {
354
- return new DiffMetadata(d, (this.modelActions.find((a) => a.modelClass === d.node.constructor)?.actions || []).filter((a) => a.filter(d)));
355
- }
356
- });
357
- // Set apply order on model diffs.
358
- for (const diff of modelDiffs) {
359
- this.setApplyOrder(diff, modelDiffs);
360
- }
361
- this.eventService.emit(new ModelDiffsTransactionEvent(undefined, [modelDiffs]));
362
- if (yieldModelDiffs) {
363
- yield [modelDiffs];
364
- }
365
- // Apply model diffs.
366
- const modelTransaction = await this.applyModels(modelDiffs);
367
- this.eventService.emit(new ModelTransactionTransactionEvent(undefined, modelTransaction));
368
- if (yieldModelTransaction) {
369
- yield modelTransaction;
370
- }
371
- // Generate resource diffs.
372
- const newDiffs = await this.resourceDataRepository.diff();
373
- const dirtyDiffs = await this.resourceDataRepository.diffDirty();
374
- if (enableResourceValidation && (newDiffs.length > 0 || dirtyDiffs.length > 0)) {
375
- throw new TransactionError('Cannot run resource validation with pending resource diffs!');
376
- }
377
- if (enableResourceValidation) {
378
- const validationDiffs = this.resourceDataRepository.diffValidate();
379
- newDiffs.push(...validationDiffs);
380
- dirtyDiffs.push(...validationDiffs);
381
- }
382
- // new diffs = new - old | dirty diffs = new - actual
383
- // Any new diff that is also not part of dirty diffs should be skipped, as the actual is already in desired state.
384
- for (let i = newDiffs.length - 1; i >= 0; i--) {
385
- const newDiff = newDiffs[i];
386
- if (!dirtyDiffs.some((d) => d.node.getContext() === newDiff.node.getContext() &&
387
- d.action === newDiff.action &&
388
- d.field === newDiff.field &&
389
- DiffUtility.isObjectDeepEquals(d.value, newDiff.value))) {
390
- newDiffs.splice(i, 1);
391
- }
392
- }
393
- // Ensure any new diffs are not operating on dirty resources.
394
- this.resourceDataRepository.ensureDiffsNotOperatingOnDirtyResources(newDiffs);
395
- // Skip processing dirty diffs that are already accounted for in new diffs.
396
- for (let i = dirtyDiffs.length - 1; i >= 0; i--) {
397
- const dirtyDiff = dirtyDiffs[i];
398
- if (newDiffs.some((d) => d.node.getContext() === dirtyDiff.node.getContext() &&
399
- d.action === dirtyDiff.action &&
400
- d.field === dirtyDiff.field &&
401
- DiffUtility.isObjectDeepEquals(d.value, dirtyDiff.value))) {
402
- dirtyDiffs.splice(i, 1);
403
- }
404
- }
405
- // Generate diff on resources.
406
- const resourceDiffs = newDiffs.map((d) => new DiffMetadata(d, (this.resourceActions.find((a) => a.resourceClass === d.node.constructor)?.actions || []).filter((a) => a.filter(d))));
407
- // Set apply order on resource diffs.
408
- for (const diff of resourceDiffs) {
409
- this.setApplyOrder(diff, resourceDiffs);
410
- }
411
- // Generate diff on dirty resources.
412
- const dirtyResourceDiffs = dirtyDiffs.map((d) => new DiffMetadata(d, (this.resourceActions.find((a) => a.resourceClass === d.node.constructor)?.actions || []).filter((a) => a.filter(d))));
413
- // Set apply order on dirty resource diffs.
414
- for (const diff of dirtyResourceDiffs) {
415
- this.setApplyOrder(diff, dirtyResourceDiffs);
416
- }
417
- this.applyResourcesDryRun([...resourceDiffs, ...dirtyResourceDiffs], { enableResourceCapture });
418
- this.eventService.emit(new ResourceDiffsTransactionEvent(undefined, [[resourceDiffs], [dirtyResourceDiffs]]));
419
- if (yieldResourceDiffs) {
420
- yield [resourceDiffs, dirtyResourceDiffs];
421
- }
422
- // Apply resource diffs.
423
- const resourceTransaction = await this.applyResources(resourceDiffs, {
424
- enableResourceCapture,
425
- });
426
- // Apply dirty resource diffs.
427
- const dirtyResourceTransaction = await this.applyResources(dirtyResourceDiffs, {
428
- enableResourceCapture,
429
- });
430
- this.eventService.emit(new ResourceTransactionTransactionEvent(undefined, [resourceTransaction, dirtyResourceTransaction]));
431
- if (yieldResourceTransaction) {
432
- yield [...resourceTransaction, ...dirtyResourceTransaction];
433
- }
434
- return modelTransaction;
435
- }
436
- registerModelActions(forModel, actions) {
437
- const modelActions = this.modelActions.find((a) => a.modelClass === forModel);
438
- if (!modelActions) {
439
- this.modelActions.push({ actions: actions, modelClass: forModel });
440
- }
441
- else {
442
- for (const action of actions) {
443
- if (modelActions.actions.find((a) => a.constructor.name === action.constructor.name)) {
444
- throw new Error(`Action "${action.constructor.name}" already registered for model "${forModel.name}"!`);
445
- }
446
- modelActions.actions.push(action);
447
- }
448
- }
449
- }
450
- registerOverlayActions(forOverlay, actions) {
451
- const overlayActions = this.overlayActions.find((a) => a.overlayClass === forOverlay);
452
- if (!overlayActions) {
453
- this.overlayActions.push({ actions: actions, overlayClass: forOverlay });
454
- }
455
- else {
456
- for (const action of actions) {
457
- if (overlayActions.actions.find((a) => a.constructor.name === action.constructor.name)) {
458
- throw new Error(`Action "${action.constructor.name}" already registered for overlay "${forOverlay.name}"!`);
459
- }
460
- overlayActions.actions.push(action);
461
- }
462
- }
463
- }
464
- registerResourceActions(forResource, actions) {
465
- const resourceActions = this.resourceActions.find((a) => a.resourceClass === forResource);
466
- if (!resourceActions) {
467
- this.resourceActions.push({ actions: actions, resourceClass: forResource });
468
- }
469
- else {
470
- for (const action of actions) {
471
- if (resourceActions.actions.find((a) => a.constructor.name === action.constructor.name)) {
472
- throw new Error(`Action "${action.constructor.name}" already registered for resource "${forResource.name}"!`);
473
- }
474
- resourceActions.actions.push(action);
475
- }
476
- }
477
- }
478
- unregisterModelActions(forModel) {
479
- const modelActions = this.modelActions.find((a) => a.modelClass === forModel);
480
- if (!modelActions) {
481
- return;
482
- }
483
- modelActions.actions = [];
484
- }
485
- unregisterOverlayActions(forOverlay) {
486
- const overlayActions = this.overlayActions.find((a) => a.overlayClass === forOverlay);
487
- if (!overlayActions) {
488
- return;
489
- }
490
- overlayActions.actions = [];
491
- }
492
644
  }
493
645
  __decorate([
494
646
  EventSource(ModelActionRegistrationEvent),
@@ -512,18 +664,21 @@ __decorate([
512
664
  * @internal
513
665
  */
514
666
  let TransactionServiceFactory = class TransactionServiceFactory {
667
+ /**
668
+ * @internal
669
+ */
515
670
  static async create() {
516
671
  const container = Container.getInstance();
517
- const [captureService, eventService, inputService, moduleContainer, overlayDataRepository, resourceDataRepository] = await Promise.all([
518
- container.get(CaptureService),
672
+ const [eventService, inputService, moduleContainer, overlayDataRepository, resourceDataRepository, terraformService,] = await Promise.all([
519
673
  container.get(EventService),
520
674
  container.get(InputService),
521
675
  container.get(ModuleContainer),
522
676
  container.get(OverlayDataRepository),
523
677
  container.get(ResourceDataRepository),
678
+ container.get(TerraformService),
524
679
  ]);
525
680
  if (!this.instance) {
526
- this.instance = new TransactionService(captureService, eventService, inputService, moduleContainer, overlayDataRepository, resourceDataRepository);
681
+ this.instance = new TransactionService(eventService, inputService, moduleContainer, overlayDataRepository, resourceDataRepository, terraformService);
527
682
  }
528
683
  return this.instance;
529
684
  }