@highstate/backend 0.9.18 → 0.9.20

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 (331) hide show
  1. package/dist/{chunk-OU5OQBLB.js → chunk-I7BWSAN6.js} +3 -28
  2. package/dist/{chunk-OU5OQBLB.js.map → chunk-I7BWSAN6.js.map} +1 -1
  3. package/dist/chunk-RC6Q3XQQ.js +1547 -0
  4. package/dist/chunk-RC6Q3XQQ.js.map +1 -0
  5. package/dist/chunk-VB4YL327.js +139 -0
  6. package/dist/chunk-VB4YL327.js.map +1 -0
  7. package/dist/database/local/prisma.config.js +26 -0
  8. package/dist/database/local/prisma.config.js.map +1 -0
  9. package/dist/highstate.manifest.json +2 -1
  10. package/dist/index.js +7590 -7289
  11. package/dist/index.js.map +1 -1
  12. package/dist/library/package-resolution-worker.js +1 -1
  13. package/dist/library/package-resolution-worker.js.map +1 -1
  14. package/dist/library/worker/main.js +35 -29
  15. package/dist/library/worker/main.js.map +1 -1
  16. package/dist/shared/index.js +2 -2
  17. package/package.json +18 -9
  18. package/prisma/backend/_schema/layout.prisma +7 -0
  19. package/prisma/backend/_schema/library.prisma +17 -0
  20. package/prisma/backend/_schema/project.prisma +101 -0
  21. package/prisma/backend/_schema/pulumi.prisma +17 -0
  22. package/prisma/backend/postgresql/main.prisma +17 -0
  23. package/prisma/backend/sqlite/main.prisma +17 -0
  24. package/prisma/backend/sqlite/migrations/20250817070609_initiial/migration.sql +34 -0
  25. package/prisma/backend/sqlite/migrations/20250817104948_add_fields/migration.sql +59 -0
  26. package/prisma/backend/sqlite/migrations/20250818082732_add_models/migration.sql +41 -0
  27. package/prisma/backend/sqlite/migrations/20250818083106_a/migration.sql +19 -0
  28. package/prisma/backend/sqlite/migrations/20250818101945_hi/migration.sql +1 -0
  29. package/prisma/backend/sqlite/migrations/20250819082315_a/migration.sql +5 -0
  30. package/prisma/backend/sqlite/migrations/migration_lock.toml +3 -0
  31. package/prisma/project/api-key.prisma +32 -0
  32. package/prisma/project/artifact.prisma +52 -0
  33. package/prisma/project/custom-status.prisma +46 -0
  34. package/prisma/project/evaluation.prisma +45 -0
  35. package/prisma/project/instance.prisma +157 -0
  36. package/prisma/project/layout.prisma +23 -0
  37. package/prisma/project/lock.prisma +18 -0
  38. package/prisma/project/main.prisma +17 -0
  39. package/prisma/project/migrations/20250816081310_initial/migration.sql +300 -0
  40. package/prisma/project/migrations/20250816082523_test/migration.sql +72 -0
  41. package/prisma/project/migrations/20250818065643_update/migration.sql +42 -0
  42. package/prisma/project/migrations/20250818070758_a/migration.sql +8 -0
  43. package/prisma/project/migrations/20250818070913_a/migration.sql +8 -0
  44. package/prisma/project/migrations/20250818082720_add_motels/migration.sql +11 -0
  45. package/prisma/project/migrations/20250818112523_hello/migration.sql +35 -0
  46. package/prisma/project/migrations/20250819082305_a/migration.sql +14 -0
  47. package/prisma/project/migrations/20250819165004_add_missing_fields/migration.sql +216 -0
  48. package/prisma/project/migrations/20250819171309_a/migration.sql +22 -0
  49. package/prisma/project/migrations/20250820113949_a/migration.sql +66 -0
  50. package/prisma/project/migrations/20250820144256_b/migration.sql +31 -0
  51. package/prisma/project/migrations/20250820145547_a/migration.sql +24 -0
  52. package/prisma/project/migrations/20250820182517_b/migration.sql +2 -0
  53. package/prisma/project/migrations/20250821172324_a/migration.sql +2 -0
  54. package/prisma/project/migrations/20250822081339_a/migration.sql +219 -0
  55. package/prisma/project/migrations/20250822083742_b/migration.sql +1 -0
  56. package/prisma/project/migrations/20250822105134_boom/migration.sql +1 -0
  57. package/prisma/project/migrations/20250822141028_b/migration.sql +1 -0
  58. package/prisma/project/migrations/20250822142342_b/migration.sql +16 -0
  59. package/prisma/project/migrations/20250824072720_a/migration.sql +1 -0
  60. package/prisma/project/migrations/20250824093656_b/migration.sql +21 -0
  61. package/prisma/project/migrations/20250825082518_a/migration.sql +1 -0
  62. package/prisma/project/migrations/20250825085343_b/migration.sql +1 -0
  63. package/prisma/project/migrations/20250825091312_a/migration.sql +1 -0
  64. package/prisma/project/migrations/20250903095431_hi/migration.sql +44 -0
  65. package/prisma/project/migrations/20250903174255_a/migration.sql +24 -0
  66. package/prisma/project/migrations/20250908095205_hi/migration.sql +18 -0
  67. package/prisma/project/migrations/20250909155857_hi/migration.sql +15 -0
  68. package/prisma/project/migrations/migration_lock.toml +3 -0
  69. package/prisma/project/model.prisma +37 -0
  70. package/prisma/project/operation.prisma +148 -0
  71. package/prisma/project/page.prisma +49 -0
  72. package/prisma/project/secret.prisma +54 -0
  73. package/prisma/project/service-account.prisma +42 -0
  74. package/prisma/project/terminal.prisma +107 -0
  75. package/prisma/project/trigger.prisma +37 -0
  76. package/prisma/project/unlock-method.prisma +46 -0
  77. package/prisma/project/worker.prisma +169 -0
  78. package/src/artifact/abstractions.ts +13 -13
  79. package/src/artifact/encryption.ts +30 -54
  80. package/src/artifact/factory.ts +6 -9
  81. package/src/artifact/local.ts +33 -46
  82. package/src/business/api-key.ts +24 -36
  83. package/src/business/artifact.test.ts +978 -0
  84. package/src/business/artifact.ts +136 -216
  85. package/src/business/evaluation.ts +328 -0
  86. package/src/business/index.ts +5 -2
  87. package/src/business/instance-lock.test.ts +1060 -0
  88. package/src/business/instance-lock.ts +387 -78
  89. package/src/business/instance-state.test.ts +735 -0
  90. package/src/business/instance-state.ts +582 -337
  91. package/src/business/operation.test.ts +439 -0
  92. package/src/business/operation.ts +174 -208
  93. package/src/business/project-model.ts +258 -0
  94. package/src/business/project-unlock.ts +168 -126
  95. package/src/business/project.ts +287 -179
  96. package/src/business/secret.test.ts +469 -130
  97. package/src/business/secret.ts +177 -217
  98. package/src/business/settings.test.ts +695 -0
  99. package/src/business/settings.ts +855 -0
  100. package/src/business/terminal-session.ts +90 -0
  101. package/src/business/unit-extra.test.ts +539 -0
  102. package/src/business/unit-extra.ts +160 -0
  103. package/src/business/worker.test.ts +356 -579
  104. package/src/business/worker.ts +238 -339
  105. package/src/common/codebase.ts +65 -0
  106. package/src/common/index.ts +3 -5
  107. package/src/common/logger.ts +5 -0
  108. package/src/common/utils.ts +4 -3
  109. package/src/config.ts +10 -11
  110. package/src/database/_generated/backend/postgresql/client.ts +72 -0
  111. package/src/database/_generated/backend/postgresql/commonInputTypes.ts +350 -0
  112. package/src/database/_generated/backend/postgresql/enums.ts +13 -0
  113. package/src/database/_generated/backend/postgresql/internal/class.ts +320 -0
  114. package/src/database/_generated/backend/postgresql/internal/prismaNamespace.ts +1238 -0
  115. package/src/database/_generated/backend/postgresql/models/Library.ts +1263 -0
  116. package/src/database/_generated/backend/postgresql/models/Project.ts +2175 -0
  117. package/src/database/_generated/backend/postgresql/models/ProjectModelStorage.ts +1263 -0
  118. package/src/database/_generated/backend/postgresql/models/ProjectSpace.ts +1602 -0
  119. package/src/database/_generated/backend/postgresql/models/PulumiBackend.ts +1263 -0
  120. package/src/database/_generated/backend/postgresql/models/UserWorkspaseLayout.ts +1065 -0
  121. package/src/database/_generated/backend/postgresql/models.ts +16 -0
  122. package/src/database/_generated/backend/postgresql/pjtg.ts +182 -0
  123. package/src/database/_generated/backend/sqlite/client.ts +72 -0
  124. package/src/database/_generated/backend/sqlite/commonInputTypes.ts +331 -0
  125. package/src/database/_generated/backend/sqlite/enums.ts +13 -0
  126. package/src/database/_generated/backend/sqlite/internal/class.ts +318 -0
  127. package/src/database/_generated/backend/sqlite/internal/prismaNamespace.ts +1207 -0
  128. package/src/database/_generated/backend/sqlite/models/Library.ts +1261 -0
  129. package/src/database/_generated/backend/sqlite/models/Project.ts +2169 -0
  130. package/src/database/_generated/backend/sqlite/models/ProjectModelStorage.ts +1261 -0
  131. package/src/database/_generated/backend/sqlite/models/ProjectSpace.ts +1599 -0
  132. package/src/database/_generated/backend/sqlite/models/PulumiBackend.ts +1261 -0
  133. package/src/database/_generated/backend/sqlite/models/UserWorkspaseLayout.ts +1063 -0
  134. package/src/database/_generated/backend/sqlite/models.ts +16 -0
  135. package/src/database/_generated/backend/sqlite/pjtg.ts +182 -0
  136. package/src/database/_generated/project/client.ts +204 -0
  137. package/src/database/_generated/project/commonInputTypes.ts +827 -0
  138. package/src/database/_generated/project/enums.ts +104 -0
  139. package/src/database/_generated/project/internal/class.ts +479 -0
  140. package/src/database/_generated/project/internal/prismaNamespace.ts +2974 -0
  141. package/src/database/_generated/project/models/ApiKey.ts +1506 -0
  142. package/src/database/_generated/project/models/Artifact.ts +2051 -0
  143. package/src/database/_generated/project/models/HubModel.ts +1125 -0
  144. package/src/database/_generated/project/models/InstanceCustomStatus.ts +1713 -0
  145. package/src/database/_generated/project/models/InstanceEvaluationState.ts +1312 -0
  146. package/src/database/_generated/project/models/InstanceLock.ts +1268 -0
  147. package/src/database/_generated/project/models/InstanceModel.ts +1125 -0
  148. package/src/database/_generated/project/models/InstanceOperationState.ts +1707 -0
  149. package/src/database/_generated/project/models/InstanceState.ts +4613 -0
  150. package/src/database/_generated/project/models/Operation.ts +1647 -0
  151. package/src/database/_generated/project/models/OperationLog.ts +1455 -0
  152. package/src/database/_generated/project/models/Page.ts +1838 -0
  153. package/src/database/_generated/project/models/Secret.ts +1692 -0
  154. package/src/database/_generated/project/models/ServiceAccount.ts +2165 -0
  155. package/src/database/_generated/project/models/Terminal.ts +2038 -0
  156. package/src/database/_generated/project/models/TerminalSession.ts +1454 -0
  157. package/src/database/_generated/project/models/TerminalSessionLog.ts +1280 -0
  158. package/src/database/_generated/project/models/Trigger.ts +1430 -0
  159. package/src/database/_generated/project/models/UnlockMethod.ts +1220 -0
  160. package/src/database/_generated/project/models/UserCompositeViewport.ts +1280 -0
  161. package/src/database/_generated/project/models/UserProjectViewport.ts +1059 -0
  162. package/src/database/_generated/project/models/Worker.ts +1459 -0
  163. package/src/database/_generated/project/models/WorkerUnitRegistration.ts +1524 -0
  164. package/src/database/_generated/project/models/WorkerVersion.ts +1974 -0
  165. package/src/database/_generated/project/models/WorkerVersionLog.ts +1318 -0
  166. package/src/database/_generated/project/models.ts +35 -0
  167. package/src/database/_generated/project/pjtg.ts +182 -0
  168. package/src/database/abstractions.ts +19 -0
  169. package/src/database/factory.ts +37 -0
  170. package/src/database/index.ts +6 -0
  171. package/src/database/local/backend.ts +134 -0
  172. package/src/database/local/index.ts +3 -0
  173. package/src/database/local/meta.ts +46 -0
  174. package/src/database/local/prisma.config.ts +25 -0
  175. package/src/database/local/project.ts +39 -0
  176. package/src/database/manager.ts +181 -0
  177. package/src/database/migrate.ts +35 -0
  178. package/src/database/prisma.ts +56 -0
  179. package/src/database/well-known.ts +38 -0
  180. package/src/index.ts +4 -4
  181. package/src/library/abstractions.ts +3 -5
  182. package/src/library/factory.ts +1 -1
  183. package/src/library/local.ts +81 -26
  184. package/src/library/package-resolution-worker.ts +1 -1
  185. package/src/library/worker/evaluator.ts +40 -23
  186. package/src/library/worker/loader.lite.ts +1 -1
  187. package/src/library/worker/main.ts +3 -10
  188. package/src/library/worker/protocol.ts +0 -1
  189. package/src/lock/index.ts +0 -1
  190. package/src/lock/manager.ts +0 -10
  191. package/src/orchestrator/manager.ts +190 -104
  192. package/src/orchestrator/operation-context.ts +357 -0
  193. package/src/orchestrator/operation-plan.destroy.test.md +357 -0
  194. package/src/orchestrator/operation-plan.destroy.test.ts +775 -0
  195. package/src/orchestrator/operation-plan.fixtures.ts +213 -0
  196. package/src/orchestrator/operation-plan.md +198 -0
  197. package/src/orchestrator/operation-plan.refresh.test.md +199 -0
  198. package/src/orchestrator/operation-plan.refresh.test.ts +367 -0
  199. package/src/orchestrator/operation-plan.ts +709 -0
  200. package/src/orchestrator/operation-plan.update.test.md +485 -0
  201. package/src/orchestrator/operation-plan.update.test.ts +1066 -0
  202. package/src/orchestrator/operation-workset.ts +233 -578
  203. package/src/orchestrator/operation.ts +440 -948
  204. package/src/orchestrator/plan-test-builder.ts +267 -0
  205. package/src/project-model/abstractions.ts +118 -0
  206. package/src/project-model/backends/codebase.ts +365 -0
  207. package/src/project-model/backends/database.ts +440 -0
  208. package/src/project-model/errors.ts +81 -0
  209. package/src/project-model/factory.ts +24 -0
  210. package/src/project-model/index.ts +4 -0
  211. package/src/project-model/utils.test.ts +544 -0
  212. package/src/project-model/utils.ts +242 -0
  213. package/src/pubsub/abstractions.ts +10 -1
  214. package/src/pubsub/factory.ts +4 -4
  215. package/src/pubsub/index.ts +1 -0
  216. package/src/pubsub/manager.ts +29 -13
  217. package/src/pubsub/memory.ts +31 -0
  218. package/src/runner/abstractions.ts +40 -41
  219. package/src/runner/artifact-env.ts +19 -8
  220. package/src/runner/factory.ts +6 -6
  221. package/src/runner/force-abort.ts +3 -6
  222. package/src/runner/local.ts +74 -67
  223. package/src/runner/pulumi.ts +23 -63
  224. package/src/services.ts +181 -123
  225. package/src/shared/models/backend/index.ts +3 -1
  226. package/src/shared/models/backend/library.ts +9 -1
  227. package/src/shared/models/backend/project.ts +43 -42
  228. package/src/shared/models/backend/pulumi.ts +14 -0
  229. package/src/shared/models/backend/unlock-method.ts +1 -1
  230. package/src/shared/models/backend/well-known.ts +58 -0
  231. package/src/shared/models/base.ts +40 -26
  232. package/src/shared/models/errors.ts +82 -1
  233. package/src/shared/models/index.ts +3 -2
  234. package/src/shared/models/prisma.ts +36 -0
  235. package/src/shared/models/project/api-key.ts +37 -59
  236. package/src/shared/models/project/artifact.ts +16 -76
  237. package/src/shared/models/project/custom-status.ts +12 -0
  238. package/src/shared/models/project/index.ts +8 -7
  239. package/src/shared/models/project/lock.ts +10 -78
  240. package/src/shared/models/project/model.ts +19 -1
  241. package/src/shared/models/project/operation.ts +235 -99
  242. package/src/shared/models/project/page.ts +37 -48
  243. package/src/shared/models/project/secret.ts +29 -89
  244. package/src/shared/models/project/service-account.ts +12 -17
  245. package/src/shared/models/project/state.ts +100 -407
  246. package/src/shared/models/project/terminal.ts +75 -88
  247. package/src/shared/models/project/trigger.ts +13 -49
  248. package/src/shared/models/project/unlock-method.ts +20 -26
  249. package/src/shared/models/project/worker.ts +89 -90
  250. package/src/shared/resolvers/graph-resolver.ts +21 -0
  251. package/src/shared/resolvers/index.ts +1 -1
  252. package/src/shared/resolvers/input-hash.ts +24 -14
  253. package/src/shared/resolvers/input.ts +9 -2
  254. package/src/shared/resolvers/registry.ts +5 -4
  255. package/src/shared/resolvers/state.ts +12 -1
  256. package/src/shared/resolvers/validation.ts +7 -3
  257. package/src/shared/utils/index.ts +1 -2
  258. package/src/shared/utils/promise-tracker.ts +30 -3
  259. package/src/terminal/abstractions.ts +1 -1
  260. package/src/terminal/docker.ts +3 -3
  261. package/src/terminal/manager.ts +102 -118
  262. package/src/test-utils/database.ts +119 -0
  263. package/src/test-utils/index.ts +2 -0
  264. package/src/test-utils/services.ts +134 -0
  265. package/src/unlock/abstractions.ts +5 -23
  266. package/src/unlock/memory.ts +9 -14
  267. package/src/worker/abstractions.ts +7 -4
  268. package/src/worker/docker.ts +14 -19
  269. package/src/worker/manager.ts +366 -97
  270. package/dist/chunk-NAAIDR4U.js +0 -8499
  271. package/dist/chunk-NAAIDR4U.js.map +0 -1
  272. package/dist/chunk-Y7DXREVO.js +0 -1745
  273. package/dist/chunk-Y7DXREVO.js.map +0 -1
  274. package/dist/magic-string.es-5ABAC4JN.js +0 -1292
  275. package/dist/magic-string.es-5ABAC4JN.js.map +0 -1
  276. package/src/business/__traces__/secret/update-instance-secrets/create-and-delete-secrets-simultaneously.md +0 -356
  277. package/src/business/__traces__/secret/update-instance-secrets/create-new-secrets-for-instance.md +0 -274
  278. package/src/business/__traces__/secret/update-instance-secrets/delete-existing-secrets.md +0 -223
  279. package/src/business/__traces__/secret/update-instance-secrets/no-op-when-no-changes.md +0 -147
  280. package/src/business/__traces__/secret/update-instance-secrets/update-existing-secrets.md +0 -280
  281. package/src/business/__traces__/worker/update-unit-registrations/add-new-unit-registration-when-other-exists.md +0 -360
  282. package/src/business/__traces__/worker/update-unit-registrations/add-new-unit-registration.md +0 -215
  283. package/src/business/__traces__/worker/update-unit-registrations/create-multiple-workers-with-different-identities.md +0 -427
  284. package/src/business/__traces__/worker/update-unit-registrations/handle-nonexistent-registration-id-gracefully.md +0 -217
  285. package/src/business/__traces__/worker/update-unit-registrations/no-op-when-no-changes.md +0 -132
  286. package/src/business/__traces__/worker/update-unit-registrations/recreate-worker-when-image-changes.md +0 -454
  287. package/src/business/__traces__/worker/update-unit-registrations/recreate-worker-when-image-version-changes.md +0 -426
  288. package/src/business/__traces__/worker/update-unit-registrations/recreate-worker-with-same-identity-reuses-service-account.md +0 -372
  289. package/src/business/__traces__/worker/update-unit-registrations/remove-one-of-multiple-unit-registrations.md +0 -383
  290. package/src/business/__traces__/worker/update-unit-registrations/remove-unit-registration.md +0 -245
  291. package/src/business/__traces__/worker/update-unit-registrations/update-existing-unit-registration-when-params-change.md +0 -174
  292. package/src/business/__traces__/worker/update-unit-registrations/update-params-and-image-simultaneously.md +0 -432
  293. package/src/business/__traces__/worker/update-unit-registrations/worker-with-multiple-registrations-not-deleted-when-one-removed.md +0 -220
  294. package/src/business/backend-unlock.ts +0 -10
  295. package/src/common/clock.ts +0 -18
  296. package/src/common/performance.ts +0 -44
  297. package/src/common/random.ts +0 -68
  298. package/src/common/test/index.ts +0 -2
  299. package/src/common/test/render.ts +0 -98
  300. package/src/common/test/tracer.ts +0 -359
  301. package/src/hotstate/abstractions.ts +0 -48
  302. package/src/hotstate/factory.ts +0 -17
  303. package/src/hotstate/index.ts +0 -3
  304. package/src/hotstate/manager.ts +0 -192
  305. package/src/hotstate/memory.ts +0 -100
  306. package/src/hotstate/validation.ts +0 -100
  307. package/src/lock/test.ts +0 -108
  308. package/src/project/abstractions.ts +0 -78
  309. package/src/project/evaluation.ts +0 -248
  310. package/src/project/factory.ts +0 -11
  311. package/src/project/index.ts +0 -3
  312. package/src/project/local.ts +0 -417
  313. package/src/pubsub/local.ts +0 -36
  314. package/src/pubsub/validation.ts +0 -33
  315. package/src/shared/utils/args.ts +0 -25
  316. package/src/state/abstractions.ts +0 -289
  317. package/src/state/encryption.ts +0 -98
  318. package/src/state/factory.ts +0 -20
  319. package/src/state/index.ts +0 -7
  320. package/src/state/local/backend.ts +0 -106
  321. package/src/state/local/collection.ts +0 -361
  322. package/src/state/local/index.ts +0 -2
  323. package/src/state/manager.ts +0 -890
  324. package/src/state/memory/backend.ts +0 -70
  325. package/src/state/memory/collection.ts +0 -270
  326. package/src/state/memory/index.ts +0 -2
  327. package/src/state/repository/index.ts +0 -2
  328. package/src/state/repository/repository.index.ts +0 -193
  329. package/src/state/repository/repository.ts +0 -507
  330. package/src/state/test.ts +0 -457
  331. /package/src/{state → database/local}/keyring.ts +0 -0
@@ -7,5 +7,4 @@ export type WorkerData = {
7
7
 
8
8
  allInstances: InstanceModel[]
9
9
  resolvedInputs: Record<string, Record<string, ResolvedInstanceInput[]>>
10
- instanceIds: string[]
11
10
  }
package/src/lock/index.ts CHANGED
@@ -1,4 +1,3 @@
1
1
  export * from "./abstractions"
2
2
  export * from "./factory"
3
3
  export * from "./manager"
4
- export * from "./test"
@@ -2,11 +2,6 @@ import type { LockBackend } from "./abstractions"
2
2
  import { join } from "remeda"
3
3
 
4
4
  export type LockKeyMap = {
5
- /**
6
- * Lock for the project names to ensure uniqueness.
7
- */
8
- "project-name": [name: string]
9
-
10
5
  /**
11
6
  * Lock for instances and hubs of the project.
12
7
  */
@@ -32,11 +27,6 @@ export type LockKeyMap = {
32
27
  */
33
28
  "instance-lock": [projectId: string, instanceId: string]
34
29
 
35
- /**
36
- * Lock for a specific instance state within a project.
37
- */
38
- "instance-state": [projectId: string, instanceId: string]
39
-
40
30
  /**
41
31
  * Lock for a specific artifact within a project.
42
32
  */
@@ -1,87 +1,105 @@
1
- import type { LibraryBackend } from "../library"
2
- import type { ProjectBackend } from "../project"
3
- import type { RunnerBackend } from "../runner"
4
- import type { StateManager } from "../state"
5
- import type { ArtifactService } from "../business/artifact"
1
+ import type { InstanceId } from "@highstate/contract"
6
2
  import type { Logger } from "pino"
7
3
  import type {
8
4
  InstanceLockService,
5
+ InstanceStateService,
9
6
  OperationService,
10
- SecretService,
7
+ ProjectModelService,
11
8
  ProjectUnlockService,
12
- InstanceStateService,
13
- WorkerService,
9
+ SecretService,
10
+ UnitExtraService,
14
11
  } from "../business"
15
- import type { PubSubManager } from "../pubsub"
16
- import { v7 as uuidv7 } from "uuid"
12
+ import type { ArtifactService } from "../business/artifact"
13
+ import type { DatabaseManager, Operation, Project } from "../database"
14
+ import type { LibraryBackend } from "../library"
15
+ import type { RunnerBackend } from "../runner"
17
16
  import {
18
- isFinalOperationStatus,
19
- type Operation,
20
- type OperationRequest,
21
- type Project,
17
+ finalOperationStatuses,
18
+ type OperationInput,
19
+ type OperationPhase,
20
+ operationOptionsSchema,
22
21
  } from "../shared"
22
+ import { finalInstanceOperationStatuses } from "../shared/models/project/state"
23
23
  import { RuntimeOperation } from "./operation"
24
+ import { OperationContext } from "./operation-context"
25
+ import { createOperationPlan } from "./operation-plan"
24
26
 
25
27
  export class OperationManager {
26
28
  constructor(
27
29
  private readonly runnerBackend: RunnerBackend,
28
30
  private readonly libraryBackend: LibraryBackend,
29
- private readonly projectBackend: ProjectBackend,
30
- private readonly artifactManager: ArtifactService,
31
- private readonly stateManager: StateManager,
31
+ private readonly artifactService: ArtifactService,
32
32
  private readonly instanceLockService: InstanceLockService,
33
- private readonly stateUnlockService: ProjectUnlockService,
33
+ private readonly projectUnlockService: ProjectUnlockService,
34
34
  private readonly operationService: OperationService,
35
35
  private readonly secretService: SecretService,
36
36
  private readonly instanceStateService: InstanceStateService,
37
- private readonly pubsubManager: PubSubManager,
38
- private readonly workerService: WorkerService,
37
+ private readonly projectModelService: ProjectModelService,
38
+ private readonly unitExtraService: UnitExtraService,
39
+ private readonly database: DatabaseManager,
39
40
  private readonly logger: Logger,
40
41
  ) {
41
- this.stateUnlockService.registerUnlockTask(
42
+ this.projectUnlockService.registerUnlockTask(
42
43
  //
43
- "process-lost-operations",
44
- projectId => this.processLostOperations(projectId),
44
+ "recover-system-state",
45
+ projectId => this.recoverSystemState(projectId),
45
46
  )
46
47
  }
47
48
 
48
49
  private readonly runtimeOperations = new Map<string, RuntimeOperation>()
49
50
 
51
+ /**
52
+ * Plans the project operation without executing it.
53
+ *
54
+ * @param request The operation request to plan.
55
+ * @returns The planned phases for the operation.
56
+ */
57
+ async plan(request: OperationInput): Promise<OperationPhase[]> {
58
+ this.logger.info({ request }, "planning operation")
59
+
60
+ const project = await this.database.backend.project.findUnique({
61
+ where: { id: request.projectId },
62
+ })
63
+
64
+ if (!project) {
65
+ throw new Error(`Project with ID "${request.projectId}" not found`)
66
+ }
67
+
68
+ const context = await OperationContext.load(
69
+ request.projectId,
70
+ this.libraryBackend,
71
+ this.instanceStateService,
72
+ this.projectModelService,
73
+ this.logger,
74
+ )
75
+
76
+ const options = operationOptionsSchema.parse(request.options ?? {})
77
+
78
+ return createOperationPlan(context, request.type, request.instanceIds, options)
79
+ }
80
+
50
81
  /**
51
82
  * Launches the project operation.
52
83
  *
53
84
  * @param request The operation request to launch.
54
85
  */
55
- async launch(request: OperationRequest): Promise<Operation> {
56
- const operation: Operation = {
57
- id: uuidv7(),
58
- meta: {},
59
- type: request.type,
60
- requestedInstanceIds: request.instanceIds,
61
- instanceIdsToUpdate: [],
62
- instanceIdsToDestroy: [],
63
- status: "pending",
64
- options: {
65
- forceUpdateDependencies: request.options?.forceUpdateDependencies ?? false,
66
- forceUpdateChildren: request.options?.forceUpdateChildren ?? false,
67
- destroyDependentInstances: request.options?.destroyDependentInstances ?? true,
68
- invokeDestroyTriggers: request.options?.invokeDestroyTriggers ?? false,
69
- deleteUnreachableResources: request.options?.deleteUnreachableResources ?? false,
70
- forceDeleteState: request.options?.forceDeleteState ?? false,
71
- refresh: request.options?.refresh ?? false,
72
- allowPartialCompositeInstanceUpdates:
73
- request.options?.allowPartialCompositeInstanceUpdates ?? false,
74
- },
75
- error: null,
76
- startedAt: Date.now(),
77
- completedAt: null,
78
- }
86
+ async launch(request: OperationInput): Promise<Operation> {
87
+ const options = operationOptionsSchema.parse(request.options ?? {})
88
+
89
+ const operation = await this.operationService.createOperation(
90
+ request.projectId,
91
+ {},
92
+ request.type,
93
+ request.instanceIds,
94
+ options,
95
+ )
79
96
 
80
97
  this.logger.info({ operation }, "launching operation")
81
98
 
82
- await this.operationService.updateOperation(request.projectId, operation)
99
+ const project = await this.database.backend.project.findUnique({
100
+ where: { id: request.projectId },
101
+ })
83
102
 
84
- const project = await this.stateManager.getProjectRepository().get(request.projectId)
85
103
  if (!project) {
86
104
  throw new Error(`Project with ID "${request.projectId}" not found`)
87
105
  }
@@ -102,7 +120,7 @@ export class OperationManager {
102
120
  }
103
121
  }
104
122
 
105
- cancelInstance(operationId: string, instanceId: string): void {
123
+ cancelInstance(operationId: string, instanceId: InstanceId): void {
106
124
  const runtimeOperation = this.runtimeOperations.get(operationId)
107
125
  if (runtimeOperation) {
108
126
  runtimeOperation.cancelInstance(instanceId)
@@ -115,15 +133,13 @@ export class OperationManager {
115
133
  operation,
116
134
  this.runnerBackend,
117
135
  this.libraryBackend,
118
- this.projectBackend,
119
- this.artifactManager,
120
- this.stateManager,
136
+ this.artifactService,
121
137
  this.instanceLockService,
122
138
  this.operationService,
123
139
  this.secretService,
124
140
  this.instanceStateService,
125
- this.pubsubManager,
126
- this.workerService,
141
+ this.projectModelService,
142
+ this.unitExtraService,
127
143
  this.logger.child({ operationId: operation.id }),
128
144
  )
129
145
 
@@ -131,68 +147,138 @@ export class OperationManager {
131
147
  void runtimeOperation.operateSafe().finally(() => this.runtimeOperations.delete(operation.id))
132
148
  }
133
149
 
134
- private async processLostOperations(projectId: string): Promise<void> {
135
- const activeOperations = await this.stateManager
136
- .getActiveOperationIndexRepository(projectId)
137
- .getAllItems()
150
+ private async recoverSystemState(projectId: string): Promise<void> {
151
+ const database = await this.database.forProject(projectId)
152
+
153
+ await database.$transaction(async tx => {
154
+ // 1. process lost operations
155
+ const activeOperations = await tx.operation.findMany({
156
+ where: {
157
+ status: {
158
+ not: {
159
+ in: finalOperationStatuses,
160
+ },
161
+ },
162
+ },
163
+ })
164
+
165
+ if (activeOperations.length > 0) {
166
+ // mark all lost operations as failed
167
+ for (const operation of activeOperations) {
168
+ await tx.operation.update({
169
+ where: { id: operation.id },
170
+ data: {
171
+ status: "failed",
172
+ finishedAt: new Date(),
173
+ },
174
+ })
175
+
176
+ void this.operationService.appendLog(
177
+ projectId,
178
+ operation.id,
179
+ null,
180
+ "Operation was interrupted",
181
+ )
182
+ }
138
183
 
139
- for (const operation of activeOperations) {
140
- if (isFinalOperationStatus(operation.status)) {
141
184
  this.logger.warn(
142
- { projectId, operationId: operation.id },
143
- "operation is in final state but still marked as active, removing from index",
185
+ { projectId, operationCount: activeOperations.length },
186
+ "marked %s lost operations as failed",
187
+ activeOperations.length,
144
188
  )
145
-
146
- await this.stateManager
147
- .getActiveOperationIndexRepository(projectId)
148
- .indexRepository.delete(operation.id)
149
- continue
150
189
  }
151
190
 
152
- const errorMessagePrefix = operation.error ? `${operation.error}\n\n` : ""
153
-
154
- operation.status = "failed"
155
- operation.error = `${errorMessagePrefix}Operation was unexpectedly interrupted. Please restart it.`
191
+ // 2. cleanup orphaned locks
192
+ const existingLocks = await tx.instanceLock.findMany({
193
+ select: { stateId: true },
194
+ })
156
195
 
157
- await this.operationService.updateOperation(projectId, operation)
158
- }
196
+ if (existingLocks.length > 0) {
197
+ const stateIds = existingLocks.map(lock => lock.stateId)
198
+ await tx.instanceLock.deleteMany({
199
+ where: {
200
+ stateId: {
201
+ in: stateIds,
202
+ },
203
+ },
204
+ })
159
205
 
160
- // unlock instances that were locked by lost operations
161
- const activeOperationIds = new Set(activeOperations.map(op => op.id))
206
+ for (const lock of existingLocks) {
207
+ this.logger.warn({ projectId, stateId: lock.stateId }, "removed orphaned lock")
208
+ }
162
209
 
163
- const allLocks = await this.stateManager.getInstanceLockRepository(projectId).getAllItems()
164
- const lockIdsToRemvoe: string[] = []
165
-
166
- // clean up unexpected operation locks
167
- for (const lock of allLocks) {
168
- if (lock.spec.type !== "operation") {
169
- continue
210
+ this.logger.info(
211
+ { projectId, lockCount: existingLocks.length },
212
+ "cleaned up %s locks",
213
+ existingLocks.length,
214
+ )
170
215
  }
171
216
 
172
- if (activeOperationIds.has(lock.spec.operationId)) {
173
- // remove locks for lost operation which is expected
174
- lockIdsToRemvoe.push(lock.id)
175
- continue
176
- }
217
+ // 3. reset instances with transient status
218
+ const instancesToReset = await tx.instanceOperationState.findMany({
219
+ where: {
220
+ status: {
221
+ not: {
222
+ in: finalInstanceOperationStatuses,
223
+ },
224
+ },
225
+ },
226
+ select: {
227
+ stateId: true,
228
+ status: true,
229
+ },
230
+ })
177
231
 
178
- // unexpected operation lock found, also remove it
179
- this.logger.warn(
180
- { projectId, lockId: lock.id },
181
- `unexpected operation lock found for completed operation "${lock.spec.operationId}", removing lock`,
182
- )
232
+ if (instancesToReset.length > 0) {
233
+ // log each instance being reset
234
+ for (const instance of instancesToReset) {
235
+ this.logger.warn(
236
+ { projectId, stateId: instance.stateId, previousStatus: instance.status },
237
+ "resetting instance with transient status from %s to failed",
238
+ instance.status,
239
+ )
240
+ }
183
241
 
184
- lockIdsToRemvoe.push(lock.id)
185
- }
242
+ // reset operation states that were being processed by lost operations
243
+ const result = await tx.instanceOperationState.updateMany({
244
+ where: {
245
+ status: {
246
+ not: {
247
+ in: finalInstanceOperationStatuses,
248
+ },
249
+ },
250
+ },
251
+ data: {
252
+ status: "failed",
253
+ finishedAt: new Date(),
254
+ },
255
+ })
186
256
 
187
- if (lockIdsToRemvoe.length > 0) {
188
- await this.instanceLockService.unlockInstancesUnconditionally(projectId, lockIdsToRemvoe)
189
- }
257
+ this.logger.info(
258
+ { projectId, resetOperationCount: result.count },
259
+ "reset %s operation states",
260
+ result.count,
261
+ )
262
+ }
190
263
 
191
- this.logger.debug(
192
- { projectId, operationCount: activeOperations.length },
193
- `processed %s lost operations for project "%s"`,
194
- activeOperations.length,
195
- projectId,
196
- )
264
+ // Step 4: reset attempted instance states
265
+ const attemptedResult = await tx.instanceState.updateMany({
266
+ where: {
267
+ status: "attempted",
268
+ },
269
+ data: {
270
+ status: "failed",
271
+ message: "Operation was interrupted",
272
+ },
273
+ })
274
+
275
+ if (attemptedResult.count > 0) {
276
+ this.logger.info(
277
+ { projectId, resetAttemptedCount: attemptedResult.count },
278
+ "reset %s attempted instance states",
279
+ attemptedResult.count,
280
+ )
281
+ }
282
+ })
197
283
  }
198
284
  }