@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
@@ -1,14 +1,25 @@
1
- import { Container } from '../functions/container/container.js';
1
+ import { mkdtemp } from 'node:fs/promises';
2
+ import { tmpdir } from 'node:os';
3
+ import { join } from 'node:path';
4
+ import {} from '../functions/container/container.js';
5
+ import { DiffAction } from '../functions/diff/diff.js';
6
+ import { DiffUtility } from '../functions/diff/diff.utility.js';
2
7
  import { Octo } from '../main.js';
3
8
  import { AModel } from '../models/model.abstract.js';
9
+ import { applyModelTransaction } from '../modes/apply-transaction.js';
4
10
  import { OverlayDataRepository } from '../overlays/overlay-data.repository.js';
5
11
  import { AOverlay } from '../overlays/overlay.abstract.js';
12
+ import { ResourceDataRepository } from '../resources/resource-data.repository.js';
6
13
  import { AResource } from '../resources/resource.abstract.js';
7
14
  import { InputService } from '../services/input/input.service.js';
8
15
  import { LocalEncryptionStateProvider } from '../services/state-management/local-encryption.state-provider.js';
9
16
  import { LocalStateProvider } from '../services/state-management/local.state-provider.js';
17
+ import { StateManagementService } from '../services/state-management/state-management.service.js';
10
18
  import { TestStateProvider } from '../services/state-management/test.state-provider.js';
19
+ import { TerraformService } from '../services/terraform/terraform.service.js';
11
20
  import { TransactionService } from '../services/transaction/transaction.service.js';
21
+ import { HclUtility } from '../utilities/hcl/hcl.utility.js';
22
+ import { TerraformUtility } from '../utilities/terraform/terraform.utility.js';
12
23
  import { TestAnchor } from '../utilities/test-helpers/test-classes.js';
13
24
  import { create } from '../utilities/test-helpers/test-models.js';
14
25
  import { createTestOverlays } from '../utilities/test-helpers/test-overlays.js';
@@ -16,127 +27,116 @@ import { createResources, createTestResources } from '../utilities/test-helpers/
16
27
  import { AModule } from './module.abstract.js';
17
28
  import { ModuleContainer } from './module.container.js';
18
29
  // Universal Test Module and Actions.
30
+ /**
31
+ * @internal
32
+ */
19
33
  class UniversalTestModuleSchema {
20
34
  }
35
+ /**
36
+ * @internal
37
+ */
21
38
  class UniversalTestModule extends AModule {
22
39
  static { this.MODULE_PACKAGE = '@octo'; }
23
40
  static { this.MODULE_SCHEMA = UniversalTestModuleSchema; }
41
+ /**
42
+ * @internal
43
+ */
24
44
  async onInit() {
25
45
  return;
26
46
  }
27
47
  }
48
+ /**
49
+ * @internal
50
+ */
28
51
  class UniversalModelAction {
52
+ /**
53
+ * @internal
54
+ */
29
55
  filter() {
30
56
  return true;
31
57
  }
58
+ /**
59
+ * @internal
60
+ */
32
61
  async handle() {
33
62
  return {};
34
63
  }
35
64
  }
65
+ /**
66
+ * @internal
67
+ */
36
68
  class UniversalResourceAction {
69
+ /**
70
+ * @internal
71
+ */
37
72
  filter() {
38
73
  return true;
39
74
  }
75
+ /**
76
+ * @internal
77
+ */
40
78
  async handle() { }
41
- async mock() { }
42
79
  }
43
80
  /**
81
+ * The harness for testing a module.
82
+ *
83
+ * It runs your module through the real Octo lifecycle against an in-memory state provider, and hands
84
+ * back everything a test needs to assert on: the model and resource transactions, the generated HCL,
85
+ * and the resource responses. Create it inside a {@link TestContainer} so each test file is isolated.
86
+ *
87
+ * @example
88
+ * ```ts
89
+ * const container = await TestContainer.create({ mocks: [] }, { factoryTimeoutInMs: 500 });
90
+ * const testModuleContainer = new TestModuleContainer(container);
91
+ * await testModuleContainer.initialize();
92
+ *
93
+ * const generator = testModuleContainer.runModules([
94
+ * { inputs: { name: 'test-app' }, moduleId: 'app', type: SimpleAppModule },
95
+ * ]);
96
+ * const { modelTransaction } = (await generator.next()).value;
97
+ * ```
98
+ *
44
99
  * @group Modules
45
100
  */
46
101
  export class TestModuleContainer {
47
- constructor() {
102
+ /**
103
+ * @param container The {@link TestContainer} this run resolves its services from.
104
+ */
105
+ constructor(container) {
106
+ this.container = container;
48
107
  this.octo = new Octo();
49
108
  }
50
- async commit(app, { appLockId, enableResourceCapture = false, filterByModuleIds = [], skipResourceTransaction = false, } = {}) {
51
- if (!appLockId) {
52
- const { lockId } = await this.stateProvider.lockApp();
53
- appLockId = lockId;
54
- }
55
- const generator = this.octo.beginTransaction(app, {
56
- appLockId: skipResourceTransaction ? undefined : appLockId,
57
- enableResourceCapture,
58
- yieldModelDiffs: true,
59
- yieldModelTransaction: true,
60
- yieldResourceDiffs: true,
61
- yieldResourceTransaction: true,
62
- });
63
- const response = {};
64
- try {
65
- response.modelDiffs = (await generator.next()).value;
66
- response.modelTransaction = (await generator.next()).value;
67
- response.resourceDiffs = (await generator.next()).value;
68
- response.resourceTransaction = (await generator.next()).value;
69
- await generator.next();
70
- }
71
- catch (error) {
72
- if (!skipResourceTransaction || error.message !== 'App is not in lock state!') {
73
- await this.reset();
74
- throw error;
75
- }
76
- }
77
- finally {
78
- await this.stateProvider.unlockApp(appLockId);
79
- }
80
- const container = Container.getInstance();
81
- const inputService = await container.get(InputService);
82
- for (const moduleId of filterByModuleIds) {
83
- response.modelDiffs = response.modelDiffs
84
- .map((i) => i.filter((j) => {
85
- if (j.node instanceof AModel && !(j.node instanceof AOverlay)) {
86
- return inputService.getModuleIdFromModel(j.node) === moduleId;
87
- }
88
- else if (j.node instanceof AOverlay) {
89
- return inputService.getModuleIdFromOverlay(j.node) === moduleId;
90
- }
91
- else {
92
- return false;
93
- }
94
- }))
95
- .filter((i) => i.length);
96
- response.modelTransaction = response.modelTransaction
97
- .map((i) => i.filter((j) => {
98
- if (j.node instanceof AModel && !(j.node instanceof AOverlay)) {
99
- return inputService.getModuleIdFromModel(j.node) === moduleId;
100
- }
101
- else if (j.node instanceof AOverlay) {
102
- return inputService.getModuleIdFromOverlay(j.node) === moduleId;
103
- }
104
- else {
105
- return false;
106
- }
107
- }))
108
- .filter((i) => i.length);
109
- response.resourceDiffs = response.resourceDiffs
110
- .map((i) => i.filter((j) => {
111
- if (j.node instanceof AResource) {
112
- return inputService.getModuleIdFromResource(j.node) === moduleId;
113
- }
114
- else {
115
- return false;
116
- }
117
- }))
118
- .filter((i) => i.length);
119
- if (!skipResourceTransaction) {
120
- response.resourceTransaction = response.resourceTransaction
121
- .map((i) => i.filter((j) => {
122
- if (j.node instanceof AResource) {
123
- return inputService.getModuleIdFromResource(j.node) === moduleId;
124
- }
125
- else {
126
- return false;
127
- }
128
- }))
129
- .filter((i) => i.length);
130
- }
109
+ /**
110
+ * To seed resources that already exist, as though the given module had produced them.
111
+ * Use it to stand up the prerequisites a module under test expects to find.
112
+ */
113
+ async createResources(moduleId, args, options) {
114
+ const [inputService, moduleContainer] = await Promise.all([
115
+ this.container.get(InputService),
116
+ this.container.get(ModuleContainer),
117
+ ]);
118
+ // Register new moduleId as instance of the universal module.
119
+ moduleContainer.load(UniversalTestModule, moduleId, {});
120
+ // Immediately unload the universal module to ensure it does not run in apply().
121
+ moduleContainer.unload(UniversalTestModule);
122
+ const result = await createResources(args, options);
123
+ for (const resource of Object.values(result)) {
124
+ inputService.registerResource(moduleId, resource);
131
125
  }
132
- await this.reset();
133
- return response;
126
+ return result;
134
127
  }
128
+ /**
129
+ * To build a throwaway anchor for a test double, without declaring an anchor class.
130
+ */
135
131
  createTestAnchor(anchorId, properties, parent) {
136
132
  return new TestAnchor(anchorId, properties, parent);
137
133
  }
134
+ /**
135
+ * To seed models, as though the given module had produced them.
136
+ * Each model gets a no-op action, so the models appear in a transaction without doing any work.
137
+ */
138
138
  async createTestModels(moduleId, args) {
139
- const container = Container.getInstance();
139
+ const container = this.container;
140
140
  const [inputService, moduleContainer, transactionService] = await Promise.all([
141
141
  container.get(InputService),
142
142
  container.get(ModuleContainer),
@@ -148,16 +148,11 @@ export class TestModuleContainer {
148
148
  moduleContainer.unload(UniversalTestModule);
149
149
  const result = create(args);
150
150
  for (const models of Object.values(result)) {
151
- // Check if the model has already been registered.
152
- // If no error thrown, the model has already been registered.
153
151
  for (const model of models) {
154
- try {
155
- inputService.getModuleIdFromModel(model);
152
+ // Skip models that are already registered (a defined moduleId means it is known).
153
+ if (inputService.getModuleIdFromModel(model) !== undefined) {
156
154
  continue;
157
155
  }
158
- catch (error) {
159
- /* do nothing. */
160
- }
161
156
  inputService.registerModel(moduleId, model);
162
157
  const universalAction = new UniversalModelAction();
163
158
  Object.assign(universalAction, {
@@ -177,41 +172,44 @@ export class TestModuleContainer {
177
172
  }
178
173
  return result;
179
174
  }
175
+ /**
176
+ * To seed overlays, as though the given module had produced them.
177
+ * An overlay given no actions gets a no-op one.
178
+ */
180
179
  async createTestOverlays(moduleId, args) {
181
- const container = Container.getInstance();
182
- const [inputService, moduleContainer, transactionService] = await Promise.all([
180
+ const container = this.container;
181
+ const [inputService, moduleContainer] = await Promise.all([
183
182
  container.get(InputService),
184
183
  container.get(ModuleContainer),
185
- container.get(TransactionService),
186
184
  ]);
187
185
  // Register new moduleId as instance of the universal module.
188
186
  moduleContainer.load(UniversalTestModule, moduleId, {});
189
187
  // Immediately unload the universal module to ensure it does not run in apply().
190
188
  moduleContainer.unload(UniversalTestModule);
189
+ // Default any overlay without explicit actions to a universal no-op action, named after the
190
+ // overlay so it reads clearly in a transaction snapshot. createTestOverlays registers them.
191
+ for (const arg of args) {
192
+ if (!arg.overlayActions || arg.overlayActions.length === 0) {
193
+ const [, nodeName] = arg.context.split('=')[0].split('/');
194
+ const universalAction = new UniversalModelAction();
195
+ Object.assign(universalAction, {
196
+ constructor: { name: `${UniversalModelAction.name}For${nodeName}` },
197
+ });
198
+ arg.overlayActions = [universalAction];
199
+ }
200
+ }
191
201
  const result = await createTestOverlays(args);
192
202
  for (const overlay of Object.values(result)) {
193
203
  inputService.registerOverlay(moduleId, overlay);
194
- const universalAction = new UniversalModelAction();
195
- Object.assign(universalAction, {
196
- constructor: { name: `${UniversalModelAction.name}For${overlay.constructor.name}` },
197
- });
198
- try {
199
- transactionService.unregisterOverlayActions(overlay.constructor);
200
- transactionService.registerOverlayActions(overlay.constructor, [
201
- universalAction,
202
- ]);
203
- }
204
- catch (error) {
205
- if (error.message !==
206
- `Action "${universalAction.constructor.name}" already registered for overlay "${overlay.constructor.name}"!`) {
207
- throw error;
208
- }
209
- }
210
204
  }
211
205
  return result;
212
206
  }
213
- async createResources(moduleId, args, options) {
214
- const container = Container.getInstance();
207
+ /**
208
+ * To seed resources, as though the given module had produced them.
209
+ * A resource given no actions gets a no-op one, unless terraform owns it.
210
+ */
211
+ async createTestResources(moduleId, args, options) {
212
+ const container = this.container;
215
213
  const [inputService, moduleContainer] = await Promise.all([
216
214
  container.get(InputService),
217
215
  container.get(ModuleContainer),
@@ -220,43 +218,66 @@ export class TestModuleContainer {
220
218
  moduleContainer.load(UniversalTestModule, moduleId, {});
221
219
  // Immediately unload the universal module to ensure it does not run in apply().
222
220
  moduleContainer.unload(UniversalTestModule);
223
- const result = await createResources(args, options);
221
+ // Default any resource without explicit actions to a universal no-op action, named after the
222
+ // resource so it reads clearly in a transaction snapshot. createTestResources registers them,
223
+ // and skips terraform resources (which terraform owns and cannot have actions).
224
+ for (const arg of args) {
225
+ if (!arg.resourceActions || arg.resourceActions.length === 0) {
226
+ const [, nodeName] = arg.resourceContext.split('=')[0].split('/');
227
+ const universalAction = new UniversalResourceAction();
228
+ Object.assign(universalAction, {
229
+ constructor: { name: `${UniversalResourceAction.name}For${nodeName}` },
230
+ });
231
+ arg.resourceActions = [universalAction];
232
+ }
233
+ }
234
+ const result = await createTestResources(args, options);
224
235
  for (const resource of Object.values(result)) {
225
236
  inputService.registerResource(moduleId, resource);
226
237
  }
227
238
  return result;
228
239
  }
229
- async createTestResources(moduleId, args, options) {
230
- const container = Container.getInstance();
231
- const [inputService, moduleContainer, transactionService] = await Promise.all([
232
- container.get(InputService),
233
- container.get(ModuleContainer),
234
- container.get(TransactionService),
235
- ]);
236
- // Register new moduleId as instance of the universal module.
237
- moduleContainer.load(UniversalTestModule, moduleId, {});
238
- // Immediately unload the universal module to ensure it does not run in apply().
239
- moduleContainer.unload(UniversalTestModule);
240
- const result = await createTestResources(args, options);
241
- for (const resource of Object.values(result)) {
242
- inputService.registerResource(moduleId, resource);
243
- try {
244
- transactionService.registerResourceActions(resource.constructor, [
245
- new UniversalResourceAction(),
246
- ]);
240
+ /**
241
+ * Reduces a transaction's diffs, model or resource, to a stable digest you can snapshot:
242
+ * `+ <context>` (create), `- <context>` (delete), `^ <context>` (replace), `* <context>` (update).
243
+ *
244
+ * A diff is classified exactly as {@link Octo.validate} aggregates it, so a digest and a validate
245
+ * report of the same transaction never disagree. Only an add/delete of a `resourceId` creates or
246
+ * destroys a resource; an add/delete on any other field changes a resource that stays -
247
+ * re-pointing a `parent`, most commonly - and reads as an update.
248
+ *
249
+ * The digest has one entry per diff, not per resource, so a resource may appear more than once.
250
+ */
251
+ digestDiffs(diffs) {
252
+ const changes = [];
253
+ for (const d of diffs.flat()) {
254
+ if (d.action === DiffAction.ADD && d.field === 'resourceId') {
255
+ changes.push(`+ ${d.node.getContext()}`);
247
256
  }
248
- catch (error) {
249
- if (error.message !==
250
- `Action "${UniversalResourceAction.name}" already registered for resource "${resource.constructor.name}"!`) {
251
- throw error;
252
- }
257
+ else if (d.action === DiffAction.DELETE && d.field === 'resourceId') {
258
+ changes.push(`- ${d.node.getContext()}`);
259
+ }
260
+ else if (d.action === DiffAction.REPLACE) {
261
+ changes.push(`^ ${d.node.getContext()}`);
262
+ }
263
+ else if (d.action === DiffAction.VALIDATE) {
264
+ // Not a change: validation runs as its own transaction, one diff per surviving resource.
265
+ continue;
266
+ }
267
+ else {
268
+ changes.push(`* ${d.node.getContext()}`);
253
269
  }
254
270
  }
255
- return result;
256
- }
257
- mapTransactionActions(transaction) {
258
- return transaction.map((i) => i.map((j) => j.actions.map((a) => a.constructor.name)).flat());
271
+ return changes;
259
272
  }
273
+ /**
274
+ * Boots Octo for the test. Call it once, before running any module.
275
+ *
276
+ * @param stateProvider Defaults to an in-memory {@link TestStateProvider}. Only the local and test
277
+ * providers are accepted - a test must never write to shared state.
278
+ * @param initializeInContainer Factories to resolve at boot. See {@link Octo.initialize}.
279
+ * @param excludeInContainer Factories to unregister at boot. See {@link Octo.initialize}.
280
+ */
260
281
  async initialize(stateProvider, initializeInContainer, excludeInContainer) {
261
282
  if (stateProvider &&
262
283
  !(stateProvider instanceof LocalEncryptionStateProvider ||
@@ -267,36 +288,90 @@ export class TestModuleContainer {
267
288
  this.stateProvider = stateProvider ?? new TestStateProvider();
268
289
  await this.octo.initialize(this.stateProvider, initializeInContainer, excludeInContainer);
269
290
  // Always register the UniversalTestModule to allow users to create test prerequisites.
270
- const moduleContainer = await Container.getInstance().get(ModuleContainer);
291
+ const moduleContainer = await this.container.get(ModuleContainer);
271
292
  moduleContainer.register(UniversalTestModule, { packageName: '@octo' });
272
293
  }
273
- async orderModules(modules) {
274
- const moduleContainer = await Container.getInstance().get(ModuleContainer);
275
- moduleContainer.order(modules);
294
+ /**
295
+ * Whether the committed resource state matches the previous one, i.e. whether the last run
296
+ * settled without leaving a pending change.
297
+ */
298
+ async isResourceStateEqual() {
299
+ const container = this.container;
300
+ const stateManagementService = await container.get(StateManagementService);
301
+ const resourcesActual = await stateManagementService.getResourceState('resources-actual.json');
302
+ const resourcesOld = await stateManagementService.getResourceState('resources-old.json');
303
+ return DiffUtility.isObjectDeepEquals(resourcesActual.data, resourcesOld.data);
304
+ }
305
+ /**
306
+ * Reduces a transaction to the names of the actions it ran, in order. Snapshot it to assert that
307
+ * a change triggered the actions you expected.
308
+ */
309
+ mapTransactionActions(transaction) {
310
+ return transaction.map((i) => i.map((j) => j.actions.map((a) => a.constructor.name)).flat());
311
+ }
312
+ /**
313
+ * See {@link Octo.registerHooks}.
314
+ */
315
+ registerHooks(...args) {
316
+ return this.octo.registerHooks(...args);
276
317
  }
277
- registerCapture(resourceContext, response) {
278
- this.octo.registerCapture(resourceContext, response);
318
+ /**
319
+ * See {@link Octo.registerTags}.
320
+ */
321
+ registerTags(...args) {
322
+ return this.octo.registerTags(...args);
279
323
  }
280
- async runModule(module) {
281
- const moduleContainer = await Container.getInstance().get(ModuleContainer);
282
- const moduleMetadataIndex = moduleContainer.getMetadataIndex(module.type);
283
- if (moduleMetadataIndex === -1) {
284
- moduleContainer.register(module.type, { packageName: module.type.MODULE_PACKAGE });
285
- }
286
- const moduleMetadata = moduleContainer.getMetadata(module.type);
287
- if (module.hidden === true) {
288
- moduleContainer.unload(moduleMetadata.module);
289
- }
290
- else {
291
- moduleContainer.load(moduleMetadata.module, module.moduleId, module.inputs);
292
- }
293
- return (await this.octo.compose());
324
+ /**
325
+ * See {@link Octo.registerTerraformConfig}. Required before a run that generates terraform.
326
+ */
327
+ registerTerraformConfig(...args) {
328
+ return this.octo.registerTerraformConfig(...args);
329
+ }
330
+ /**
331
+ * See {@link Octo.registerTerraformProvider}. Required before a run that generates terraform.
332
+ */
333
+ registerTerraformProvider(...args) {
334
+ return this.octo.registerTerraformProvider(...args);
294
335
  }
295
- async runModules(modules) {
296
- const moduleContainer = await Container.getInstance().get(ModuleContainer);
297
- for (const module of modules) {
298
- const moduleMetadataIndex = moduleContainer.getMetadataIndex(module.type);
299
- if (moduleMetadataIndex === -1) {
336
+ /**
337
+ * Tears down the composed graph - models, overlays, inputs, and modules - so the next
338
+ * {@link TestModuleContainer.runModules} composes fresh. Call it in your test teardown.
339
+ */
340
+ async reset() {
341
+ const container = this.container;
342
+ // Reset ModuleContainer constructor injections.
343
+ await container.get(InputService, { args: [true] });
344
+ await container.get(OverlayDataRepository, {
345
+ args: [true, []],
346
+ });
347
+ // Reset module container.
348
+ const moduleContainer = await container.get(ModuleContainer);
349
+ moduleContainer.reset();
350
+ }
351
+ /**
352
+ * Runs the given modules through Octo's lifecycle, and yields the result once.
353
+ *
354
+ * How far the lifecycle goes is set by `terraformTarget`, and this is the choice that decides what
355
+ * the test costs:
356
+ * - `'skip'` - composes and commits in memory. No terraform, no cloud. This is a unit test.
357
+ * - `'plan'` - generates the module folders and runs `terraform plan` against them.
358
+ * - `'apply'` - generates, plans, applies, and commits. **This creates real infrastructure.**
359
+ *
360
+ * @param modules The modules to load, in order.
361
+ * @param options `filterByModuleIds` narrows the run to those modules; `outputDir` is where the
362
+ * terraform folders are written, defaulting to a temporary directory.
363
+ */
364
+ async *runModules(modules, { filterByModuleIds = [], outputDir, terraformTarget = 'skip', } = {}) {
365
+ const [moduleContainer, resourceDataRepository, terraformService, terraformUtility] = await Promise.all([
366
+ this.container.get(ModuleContainer),
367
+ this.container.get(ResourceDataRepository),
368
+ this.container.get(TerraformService),
369
+ this.container.get(TerraformUtility),
370
+ ]);
371
+ // Load modules.
372
+ const moduleList = Array.isArray(modules) ? modules : [modules];
373
+ for (const module of moduleList) {
374
+ if (moduleContainer.getMetadataIndex(module.type) === -1) {
300
375
  moduleContainer.register(module.type, {
301
376
  packageName: module.type.MODULE_PACKAGE,
302
377
  });
@@ -309,18 +384,175 @@ export class TestModuleContainer {
309
384
  moduleContainer.load(moduleMetadata.module, module.moduleId, module.inputs);
310
385
  }
311
386
  }
312
- return (await this.octo.compose());
387
+ // Compose modules.
388
+ moduleContainer.order(moduleList.map((m) => m.type));
389
+ await this.octo.compose();
390
+ const app = this.octo.getRootModel();
391
+ const collectResponses = () => Object.fromEntries(resourceDataRepository.getActualResourcesByProperties().map((r) => [r.getContext(), r.response]));
392
+ if (terraformTarget === 'skip') {
393
+ // this.commit() applies the model graph exactly once and, before its internal reset, stores the
394
+ // rendered desired state in `previousHcl`. Derive the render and block diff from that single
395
+ // apply rather than a separate preview render — a preview would re-run model actions, and
396
+ // non-idempotent ones (e.g. sibling associations) would then apply twice.
397
+ const priorHcl = this.previousHcl ?? '';
398
+ const transaction = await this.commit(app, { filterByModuleIds });
399
+ const currentHcl = this.previousHcl ?? '';
400
+ yield {
401
+ hclDiff: HclUtility.diffBlocks(priorHcl, currentHcl),
402
+ hclRender: currentHcl,
403
+ modelTransaction: transaction.modelTransaction,
404
+ resourceDiffs: transaction.resourceDiffs,
405
+ resourceTransaction: transaction.resourceTransaction,
406
+ responses: collectResponses(),
407
+ validateReport: undefined,
408
+ warnings: [],
409
+ };
410
+ return;
411
+ }
412
+ // In production octo runs generate/validate/commit as three separate processes, each of which
413
+ // derives the resource graph once from intent. runModules drives all three in one process, so it
414
+ // builds the model transaction once and hands the same result to each mode.
415
+ const transaction = await applyModelTransaction(app);
416
+ // Generate.
417
+ const dir = outputDir ?? (await mkdtemp(join(tmpdir(), 'octo-hcl-')));
418
+ const resourceDiffs = await this.octo.generate(app, { outputDir: dir }, transaction);
419
+ // Render the full HCL and the block diff from the sweep the transaction produced.
420
+ const currentHcl = HclUtility.serialize(terraformService.renderAllModules());
421
+ const diffHcl = HclUtility.diffBlocks(this.previousHcl ?? '', currentHcl);
422
+ this.previousHcl = currentHcl;
423
+ // Validate.
424
+ const validateReport = await this.octo.validate(app, await terraformUtility.plan(dir, { json: true }), transaction);
425
+ if (validateReport.failures.length > 0) {
426
+ throw new Error(`runModules(): terraform could not plan:\n${validateReport.failures
427
+ .map((failure) => `${failure.moduleId ? `[${failure.moduleId}] ` : ''}${failure.message}`)
428
+ .join('\n')}`);
429
+ }
430
+ const result = {
431
+ hclDiff: diffHcl,
432
+ hclRender: currentHcl,
433
+ modelTransaction: [],
434
+ resourceDiffs,
435
+ resourceTransaction: [],
436
+ responses: collectResponses(),
437
+ validateReport,
438
+ warnings: [],
439
+ };
440
+ if (terraformTarget === 'plan') {
441
+ yield result;
442
+ return;
443
+ }
444
+ // Apply.
445
+ await terraformUtility.apply(dir);
446
+ // Commit. The lock is taken and released here, the way the CLI does it around octo.commit().
447
+ const { lockId } = await this.stateProvider.lockApp();
448
+ let warnings;
449
+ try {
450
+ ({ warnings } = await this.octo.commit(app, { lockId, outputs: await terraformUtility.output(dir, { json: true }) }, transaction));
451
+ }
452
+ finally {
453
+ await this.stateProvider.unlockApp(lockId);
454
+ }
455
+ // Re-collect responses after the commit: it is commit that maps terraform outputs onto the
456
+ // actual resource graph, so a snapshot taken before it (as `response` holds) would miss them.
457
+ yield { ...result, responses: collectResponses(), warnings };
313
458
  }
314
- async reset() {
315
- const container = Container.getInstance();
316
- // Reset ModuleContainer constructor injections.
317
- await container.get(InputService, { args: [true] });
318
- await container.get(OverlayDataRepository, {
319
- args: [true, []],
459
+ /**
460
+ * Persists the run's state, so a later run in the same test diffs against it.
461
+ */
462
+ async commit(app, { filterByModuleIds = [], skipResourceTransaction = false, } = {}) {
463
+ const [terraformService, transactionService] = await Promise.all([
464
+ this.container.get(TerraformService),
465
+ this.container.get(TransactionService),
466
+ ]);
467
+ // Each transaction sweeps the full resource graph into the terraform service, so the previous
468
+ // call's sweep must be cleared first. Config and providers survive, as they would in a real
469
+ // process — tests register them once (in beforeEach), not per transaction.
470
+ terraformService.resetTransactionState();
471
+ const diffs = await app.diff();
472
+ const generator = transactionService.beginTransaction(diffs, {
473
+ generateTerraform: true,
474
+ yieldModelDiffs: true,
475
+ yieldModelTransaction: true,
476
+ yieldResourceDiffs: true,
477
+ yieldResourceTransaction: true,
320
478
  });
321
- // Reset module container.
322
- const moduleContainer = await container.get(ModuleContainer);
323
- moduleContainer.reset();
479
+ const response = {};
480
+ try {
481
+ response.modelDiffs = (await generator.next()).value;
482
+ response.modelTransaction = (await generator.next()).value;
483
+ response.resourceDiffs = (await generator.next()).value;
484
+ if (!skipResourceTransaction) {
485
+ response.resourceTransaction = (await generator.next()).value;
486
+ await generator.next();
487
+ }
488
+ else {
489
+ response.resourceTransaction = [];
490
+ }
491
+ }
492
+ catch (error) {
493
+ await this.reset();
494
+ throw error;
495
+ }
496
+ if (!skipResourceTransaction) {
497
+ await this.octo['commitTransaction'](app, response.modelTransaction, response.resourceTransaction);
498
+ }
499
+ // Establish the HCL diff baseline from the same transaction that just applied the models, so the
500
+ // next runModules() render reports its `diff` against the committed state. Captured before
501
+ // reset(), which wipes the model attribution renderAllModules() depends on.
502
+ this.previousHcl = HclUtility.serialize(terraformService.renderAllModules());
503
+ const inputService = await this.container.get(InputService);
504
+ for (const moduleId of filterByModuleIds) {
505
+ response.modelDiffs = response.modelDiffs
506
+ .map((i) => i.filter((j) => {
507
+ if (j.node instanceof AModel && !(j.node instanceof AOverlay)) {
508
+ return inputService.getModuleIdFromModel(j.node) === moduleId;
509
+ }
510
+ else if (j.node instanceof AOverlay) {
511
+ return inputService.getModuleIdFromOverlay(j.node) === moduleId;
512
+ }
513
+ else {
514
+ return false;
515
+ }
516
+ }))
517
+ .filter((i) => i.length);
518
+ response.modelTransaction = response.modelTransaction
519
+ .map((i) => i.filter((j) => {
520
+ if (j.node instanceof AModel && !(j.node instanceof AOverlay)) {
521
+ return inputService.getModuleIdFromModel(j.node) === moduleId;
522
+ }
523
+ else if (j.node instanceof AOverlay) {
524
+ return inputService.getModuleIdFromOverlay(j.node) === moduleId;
525
+ }
526
+ else {
527
+ return false;
528
+ }
529
+ }))
530
+ .filter((i) => i.length);
531
+ response.resourceDiffs = response.resourceDiffs
532
+ .map((i) => i.filter((j) => {
533
+ if (j.node instanceof AResource) {
534
+ return inputService.getModuleIdFromResource(j.node) === moduleId;
535
+ }
536
+ else {
537
+ return false;
538
+ }
539
+ }))
540
+ .filter((i) => i.length);
541
+ if (!skipResourceTransaction) {
542
+ response.resourceTransaction = response.resourceTransaction
543
+ .map((i) => i.filter((j) => {
544
+ if (j.node instanceof AResource) {
545
+ return inputService.getModuleIdFromResource(j.node) === moduleId;
546
+ }
547
+ else {
548
+ return false;
549
+ }
550
+ }))
551
+ .filter((i) => i.length);
552
+ }
553
+ }
554
+ await this.reset();
555
+ return response;
324
556
  }
325
557
  }
326
558
  //# sourceMappingURL=test-module.container.js.map