@highstate/backend 0.9.16 → 0.9.19

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 (302) hide show
  1. package/dist/chunk-5WVU2AK4.js +1535 -0
  2. package/dist/chunk-5WVU2AK4.js.map +1 -0
  3. package/dist/chunk-I7BWSAN6.js +49 -0
  4. package/dist/chunk-I7BWSAN6.js.map +1 -0
  5. package/dist/{chunk-RCB4AFGD.js → chunk-VB4YL327.js} +51 -71
  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 +5 -4
  10. package/dist/index.js +7676 -6634
  11. package/dist/index.js.map +1 -1
  12. package/dist/library/package-resolution-worker.js +8 -6
  13. package/dist/library/package-resolution-worker.js.map +1 -1
  14. package/dist/library/worker/main.js +63 -58
  15. package/dist/library/worker/main.js.map +1 -1
  16. package/dist/shared/index.js +3 -216
  17. package/dist/shared/index.js.map +1 -1
  18. package/package.json +23 -11
  19. package/prisma/backend/_schema/layout.prisma +7 -0
  20. package/prisma/backend/_schema/library.prisma +17 -0
  21. package/prisma/backend/_schema/project.prisma +101 -0
  22. package/prisma/backend/_schema/pulumi.prisma +17 -0
  23. package/prisma/backend/postgresql/main.prisma +17 -0
  24. package/prisma/backend/sqlite/main.prisma +17 -0
  25. package/prisma/backend/sqlite/migrations/20250817070609_initiial/migration.sql +34 -0
  26. package/prisma/backend/sqlite/migrations/20250817104948_add_fields/migration.sql +59 -0
  27. package/prisma/backend/sqlite/migrations/20250818082732_add_models/migration.sql +41 -0
  28. package/prisma/backend/sqlite/migrations/20250818083106_a/migration.sql +19 -0
  29. package/prisma/backend/sqlite/migrations/20250818101945_hi/migration.sql +1 -0
  30. package/prisma/backend/sqlite/migrations/20250819082315_a/migration.sql +5 -0
  31. package/prisma/backend/sqlite/migrations/migration_lock.toml +3 -0
  32. package/prisma/project/api-key.prisma +27 -0
  33. package/prisma/project/artifact.prisma +52 -0
  34. package/prisma/project/custom-status.prisma +46 -0
  35. package/prisma/project/evaluation.prisma +35 -0
  36. package/prisma/project/instance.prisma +160 -0
  37. package/prisma/project/layout.prisma +23 -0
  38. package/prisma/project/lock.prisma +18 -0
  39. package/prisma/project/main.prisma +17 -0
  40. package/prisma/project/migrations/20250816081310_initial/migration.sql +300 -0
  41. package/prisma/project/migrations/20250816082523_test/migration.sql +72 -0
  42. package/prisma/project/migrations/20250818065643_update/migration.sql +42 -0
  43. package/prisma/project/migrations/20250818070758_a/migration.sql +8 -0
  44. package/prisma/project/migrations/20250818070913_a/migration.sql +8 -0
  45. package/prisma/project/migrations/20250818082720_add_motels/migration.sql +11 -0
  46. package/prisma/project/migrations/20250818112523_hello/migration.sql +35 -0
  47. package/prisma/project/migrations/20250819082305_a/migration.sql +14 -0
  48. package/prisma/project/migrations/20250819165004_add_missing_fields/migration.sql +216 -0
  49. package/prisma/project/migrations/20250819171309_a/migration.sql +22 -0
  50. package/prisma/project/migrations/20250820113949_a/migration.sql +66 -0
  51. package/prisma/project/migrations/20250820144256_b/migration.sql +31 -0
  52. package/prisma/project/migrations/20250820145547_a/migration.sql +24 -0
  53. package/prisma/project/migrations/20250820182517_b/migration.sql +2 -0
  54. package/prisma/project/migrations/20250821172324_a/migration.sql +2 -0
  55. package/prisma/project/migrations/20250822081339_a/migration.sql +219 -0
  56. package/prisma/project/migrations/20250822083742_b/migration.sql +1 -0
  57. package/prisma/project/migrations/20250822105134_boom/migration.sql +1 -0
  58. package/prisma/project/migrations/20250822141028_b/migration.sql +1 -0
  59. package/prisma/project/migrations/20250822142342_b/migration.sql +16 -0
  60. package/prisma/project/migrations/20250824072720_a/migration.sql +1 -0
  61. package/prisma/project/migrations/20250824093656_b/migration.sql +21 -0
  62. package/prisma/project/migrations/20250825082518_a/migration.sql +1 -0
  63. package/prisma/project/migrations/20250825085343_b/migration.sql +1 -0
  64. package/prisma/project/migrations/20250825091312_a/migration.sql +1 -0
  65. package/prisma/project/migrations/20250903095431_hi/migration.sql +44 -0
  66. package/prisma/project/migrations/20250903174255_a/migration.sql +24 -0
  67. package/prisma/project/migrations/20250908095205_hi/migration.sql +18 -0
  68. package/prisma/project/migrations/20250909155857_hi/migration.sql +15 -0
  69. package/prisma/project/migrations/migration_lock.toml +3 -0
  70. package/prisma/project/model.prisma +37 -0
  71. package/prisma/project/operation.prisma +148 -0
  72. package/prisma/project/page.prisma +41 -0
  73. package/prisma/project/secret.prisma +42 -0
  74. package/prisma/project/service-account.prisma +36 -0
  75. package/prisma/project/terminal.prisma +90 -0
  76. package/prisma/project/trigger.prisma +31 -0
  77. package/prisma/project/unlock-method.prisma +32 -0
  78. package/prisma/project/worker.prisma +138 -0
  79. package/src/artifact/abstractions.ts +13 -13
  80. package/src/artifact/encryption.ts +31 -15
  81. package/src/artifact/factory.ts +7 -10
  82. package/src/artifact/local.ts +33 -50
  83. package/src/business/api-key.ts +24 -36
  84. package/src/business/artifact.test.ts +978 -0
  85. package/src/business/artifact.ts +136 -215
  86. package/src/business/evaluation.ts +328 -0
  87. package/src/business/index.ts +5 -1
  88. package/src/business/instance-lock.test.ts +1060 -0
  89. package/src/business/instance-lock.ts +387 -77
  90. package/src/business/instance-state.test.ts +735 -0
  91. package/src/business/instance-state.ts +604 -217
  92. package/src/business/operation.test.ts +439 -0
  93. package/src/business/operation.ts +174 -208
  94. package/src/business/project-model.ts +258 -0
  95. package/src/business/project-unlock.ts +172 -112
  96. package/src/business/project.ts +407 -0
  97. package/src/business/secret.test.ts +513 -0
  98. package/src/business/secret.ts +194 -131
  99. package/src/business/settings.test.ts +695 -0
  100. package/src/business/settings.ts +855 -0
  101. package/src/business/terminal-session.ts +90 -0
  102. package/src/business/unit-extra.test.ts +539 -0
  103. package/src/business/unit-extra.ts +160 -0
  104. package/src/business/worker.test.ts +391 -0
  105. package/src/business/worker.ts +250 -114
  106. package/src/common/codebase.ts +65 -0
  107. package/src/common/index.ts +3 -2
  108. package/src/common/logger.ts +5 -0
  109. package/src/common/utils.ts +4 -3
  110. package/src/config.ts +15 -12
  111. package/src/database/_generated/backend/postgresql/client.ts +72 -0
  112. package/src/database/_generated/backend/postgresql/commonInputTypes.ts +350 -0
  113. package/src/database/_generated/backend/postgresql/enums.ts +13 -0
  114. package/src/database/_generated/backend/postgresql/internal/class.ts +320 -0
  115. package/src/database/_generated/backend/postgresql/internal/prismaNamespace.ts +1238 -0
  116. package/src/database/_generated/backend/postgresql/models/Library.ts +1263 -0
  117. package/src/database/_generated/backend/postgresql/models/Project.ts +2175 -0
  118. package/src/database/_generated/backend/postgresql/models/ProjectModelStorage.ts +1263 -0
  119. package/src/database/_generated/backend/postgresql/models/ProjectSpace.ts +1602 -0
  120. package/src/database/_generated/backend/postgresql/models/PulumiBackend.ts +1263 -0
  121. package/src/database/_generated/backend/postgresql/models/UserWorkspaseLayout.ts +1065 -0
  122. package/src/database/_generated/backend/postgresql/models.ts +16 -0
  123. package/src/database/_generated/backend/postgresql/pjtg.ts +182 -0
  124. package/src/database/_generated/backend/sqlite/client.ts +72 -0
  125. package/src/database/_generated/backend/sqlite/commonInputTypes.ts +331 -0
  126. package/src/database/_generated/backend/sqlite/enums.ts +13 -0
  127. package/src/database/_generated/backend/sqlite/internal/class.ts +318 -0
  128. package/src/database/_generated/backend/sqlite/internal/prismaNamespace.ts +1207 -0
  129. package/src/database/_generated/backend/sqlite/models/Library.ts +1261 -0
  130. package/src/database/_generated/backend/sqlite/models/Project.ts +2169 -0
  131. package/src/database/_generated/backend/sqlite/models/ProjectModelStorage.ts +1261 -0
  132. package/src/database/_generated/backend/sqlite/models/ProjectSpace.ts +1599 -0
  133. package/src/database/_generated/backend/sqlite/models/PulumiBackend.ts +1261 -0
  134. package/src/database/_generated/backend/sqlite/models/UserWorkspaseLayout.ts +1063 -0
  135. package/src/database/_generated/backend/sqlite/models.ts +16 -0
  136. package/src/database/_generated/backend/sqlite/pjtg.ts +182 -0
  137. package/src/database/_generated/project/client.ts +204 -0
  138. package/src/database/_generated/project/commonInputTypes.ts +827 -0
  139. package/src/database/_generated/project/enums.ts +104 -0
  140. package/src/database/_generated/project/internal/class.ts +479 -0
  141. package/src/database/_generated/project/internal/prismaNamespace.ts +2974 -0
  142. package/src/database/_generated/project/models/ApiKey.ts +1506 -0
  143. package/src/database/_generated/project/models/Artifact.ts +2051 -0
  144. package/src/database/_generated/project/models/HubModel.ts +1125 -0
  145. package/src/database/_generated/project/models/InstanceCustomStatus.ts +1713 -0
  146. package/src/database/_generated/project/models/InstanceEvaluationState.ts +1312 -0
  147. package/src/database/_generated/project/models/InstanceLock.ts +1268 -0
  148. package/src/database/_generated/project/models/InstanceModel.ts +1125 -0
  149. package/src/database/_generated/project/models/InstanceOperationState.ts +1707 -0
  150. package/src/database/_generated/project/models/InstanceState.ts +4613 -0
  151. package/src/database/_generated/project/models/Operation.ts +1647 -0
  152. package/src/database/_generated/project/models/OperationLog.ts +1455 -0
  153. package/src/database/_generated/project/models/Page.ts +1838 -0
  154. package/src/database/_generated/project/models/Secret.ts +1692 -0
  155. package/src/database/_generated/project/models/ServiceAccount.ts +2165 -0
  156. package/src/database/_generated/project/models/Terminal.ts +2038 -0
  157. package/src/database/_generated/project/models/TerminalSession.ts +1454 -0
  158. package/src/database/_generated/project/models/TerminalSessionLog.ts +1280 -0
  159. package/src/database/_generated/project/models/Trigger.ts +1430 -0
  160. package/src/database/_generated/project/models/UnlockMethod.ts +1220 -0
  161. package/src/database/_generated/project/models/UserCompositeViewport.ts +1280 -0
  162. package/src/database/_generated/project/models/UserProjectViewport.ts +1059 -0
  163. package/src/database/_generated/project/models/Worker.ts +1459 -0
  164. package/src/database/_generated/project/models/WorkerUnitRegistration.ts +1524 -0
  165. package/src/database/_generated/project/models/WorkerVersion.ts +1974 -0
  166. package/src/database/_generated/project/models/WorkerVersionLog.ts +1318 -0
  167. package/src/database/_generated/project/models.ts +35 -0
  168. package/src/database/_generated/project/pjtg.ts +182 -0
  169. package/src/database/abstractions.ts +19 -0
  170. package/src/database/factory.ts +37 -0
  171. package/src/database/index.ts +6 -0
  172. package/src/database/local/backend.ts +134 -0
  173. package/src/database/local/index.ts +3 -0
  174. package/src/database/local/meta.ts +46 -0
  175. package/src/database/local/prisma.config.ts +25 -0
  176. package/src/database/local/project.ts +39 -0
  177. package/src/database/manager.ts +181 -0
  178. package/src/database/migrate.ts +35 -0
  179. package/src/database/prisma.ts +56 -0
  180. package/src/database/well-known.ts +38 -0
  181. package/src/index.ts +4 -4
  182. package/src/library/abstractions.ts +21 -14
  183. package/src/library/factory.ts +1 -1
  184. package/src/library/local.ts +86 -38
  185. package/src/library/package-resolution-worker.ts +1 -1
  186. package/src/library/worker/evaluator.ts +61 -48
  187. package/src/library/worker/loader.lite.ts +14 -1
  188. package/src/library/worker/main.ts +9 -16
  189. package/src/library/worker/protocol.ts +0 -12
  190. package/src/lock/manager.ts +12 -7
  191. package/src/orchestrator/manager.ts +198 -131
  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 +235 -583
  203. package/src/orchestrator/operation.ts +446 -904
  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 +49 -25
  217. package/src/pubsub/memory.ts +31 -0
  218. package/src/runner/abstractions.ts +38 -26
  219. package/src/runner/artifact-env.ts +17 -6
  220. package/src/runner/factory.ts +6 -6
  221. package/src/runner/force-abort.ts +3 -6
  222. package/src/runner/local.ts +79 -72
  223. package/src/runner/pulumi.ts +26 -63
  224. package/src/services.ts +214 -103
  225. package/src/shared/models/backend/index.ts +3 -1
  226. package/src/shared/models/backend/library.ts +12 -4
  227. package/src/shared/models/backend/project.ts +43 -23
  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 -109
  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 -56
  236. package/src/shared/models/project/artifact.ts +15 -105
  237. package/src/shared/models/project/custom-status.ts +12 -0
  238. package/src/shared/models/project/index.ts +9 -9
  239. package/src/shared/models/project/lock.ts +10 -78
  240. package/src/shared/models/project/model.ts +32 -0
  241. package/src/shared/models/project/operation.ts +222 -99
  242. package/src/shared/models/project/page.ts +37 -48
  243. package/src/shared/models/project/secret.ts +29 -103
  244. package/src/shared/models/project/service-account.ts +12 -17
  245. package/src/shared/models/project/state.ts +100 -390
  246. package/src/shared/models/project/terminal.ts +75 -89
  247. package/src/shared/models/project/trigger.ts +13 -49
  248. package/src/shared/models/project/unlock-method.ts +21 -20
  249. package/src/shared/models/project/worker.ts +89 -88
  250. package/src/shared/resolvers/graph-resolver.ts +62 -26
  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 +48 -6
  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 +29 -9
  257. package/src/shared/utils/index.ts +1 -1
  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 +31 -0
  266. package/src/unlock/index.ts +2 -0
  267. package/src/unlock/memory.ts +27 -0
  268. package/src/worker/abstractions.ts +7 -4
  269. package/src/worker/docker.ts +14 -19
  270. package/src/worker/manager.ts +376 -79
  271. package/dist/chunk-RCB4AFGD.js.map +0 -1
  272. package/dist/chunk-WHALQHEZ.js +0 -2017
  273. package/dist/chunk-WHALQHEZ.js.map +0 -1
  274. package/src/business/backend-unlock.ts +0 -10
  275. package/src/common/performance.ts +0 -44
  276. package/src/hotstate/abstractions.ts +0 -48
  277. package/src/hotstate/factory.ts +0 -17
  278. package/src/hotstate/index.ts +0 -3
  279. package/src/hotstate/manager.ts +0 -192
  280. package/src/hotstate/memory.ts +0 -100
  281. package/src/hotstate/validation.ts +0 -101
  282. package/src/project/abstractions.ts +0 -102
  283. package/src/project/factory.ts +0 -11
  284. package/src/project/index.ts +0 -3
  285. package/src/project/local.ts +0 -469
  286. package/src/project/manager.ts +0 -574
  287. package/src/pubsub/local.ts +0 -36
  288. package/src/pubsub/validation.ts +0 -33
  289. package/src/shared/models/project/component.ts +0 -45
  290. package/src/shared/models/project/instance.ts +0 -74
  291. package/src/state/abstractions.ts +0 -450
  292. package/src/state/encryption.ts +0 -59
  293. package/src/state/factory.ts +0 -20
  294. package/src/state/index.ts +0 -6
  295. package/src/state/local/backend.ts +0 -299
  296. package/src/state/local/collection.ts +0 -342
  297. package/src/state/local/index.ts +0 -2
  298. package/src/state/manager.ts +0 -819
  299. package/src/state/repository/index.ts +0 -2
  300. package/src/state/repository/repository.index.ts +0 -193
  301. package/src/state/repository/repository.ts +0 -458
  302. /package/src/{state → database/local}/keyring.ts +0 -0
@@ -0,0 +1,328 @@
1
+ import type { InstanceId, InstanceModel } from "@highstate/contract"
2
+ import type { Logger } from "pino"
3
+ import type { LibraryBackend } from "../library"
4
+ import type { PubSubManager } from "../pubsub"
5
+ import type { ProjectModelService } from "./project-model"
6
+ import type { ProjectUnlockService } from "./project-unlock"
7
+ import { isNonNullish } from "remeda"
8
+ import { renderTree, type TreeNode } from "../common"
9
+ import {
10
+ type DatabaseManager,
11
+ DbNull,
12
+ type InstanceEvaluationStateUncheckedCreateInput,
13
+ type InstanceEvaluationStateUpdateInput,
14
+ type InstanceStatus,
15
+ } from "../database"
16
+
17
+ type EvaluatedInstance = {
18
+ instanceId: InstanceId
19
+ status: "evaluated" | "error"
20
+ message?: string
21
+ model?: InstanceModel | null
22
+ }
23
+
24
+ export class ProjectEvaluationSubsystem {
25
+ private readonly projectWatchers = new Map<string, AbortController>()
26
+
27
+ constructor(
28
+ private readonly database: DatabaseManager,
29
+ private readonly libraryBackend: LibraryBackend,
30
+ private readonly projectModelService: ProjectModelService,
31
+ private readonly pubsubManager: PubSubManager,
32
+ private readonly projectUnlockService: ProjectUnlockService,
33
+ private readonly logger: Logger,
34
+ ) {
35
+ this.projectUnlockService.registerUnlockTask(
36
+ "evaluate-project",
37
+ //
38
+ projectId => this.evaluateProject(projectId),
39
+ )
40
+
41
+ this.projectUnlockService.registerUnlockTask(
42
+ //
43
+ "track-unlocked-project",
44
+ projectId => this.trackUnlockedProject(projectId),
45
+ )
46
+ }
47
+
48
+ async evaluateProject(projectId: string): Promise<void> {
49
+ const { project, instances, resolvedInputs } =
50
+ await this.projectModelService.resolveProject(projectId)
51
+
52
+ const instancesMap = new Map<string, InstanceModel>()
53
+ for (const instance of instances) {
54
+ instancesMap.set(instance.id, instance)
55
+ }
56
+
57
+ const compositeInstanceIds = instances
58
+ .filter(instance => instance.kind === "composite")
59
+ .map(instance => instance.id)
60
+
61
+ try {
62
+ const result = await this.libraryBackend.evaluateCompositeInstances(
63
+ project.libraryId,
64
+ instances,
65
+ resolvedInputs,
66
+ )
67
+
68
+ if (result.success) {
69
+ // create evaluation tree for success messages
70
+ const treeNodes = ProjectEvaluationSubsystem.createEvaluatedInstanceTree(
71
+ result.virtualInstances,
72
+ )
73
+
74
+ // store evaluation states for virtual instances
75
+ const evaluatedInstances: EvaluatedInstance[] = result.virtualInstances.map(instance => ({
76
+ instanceId: instance.id as InstanceId,
77
+ status: "evaluated",
78
+ message: ProjectEvaluationSubsystem.renderEvaluationMessage(treeNodes, instance.id),
79
+ model: instance,
80
+ }))
81
+
82
+ const errorEvaluatedInstances: EvaluatedInstance[] = Object.entries(
83
+ result.topLevelErrors,
84
+ ).map(([instanceId, message]) => ({
85
+ instanceId: instanceId as InstanceId,
86
+ status: "error",
87
+ message,
88
+ model: null,
89
+ }))
90
+
91
+ await this.setInstanceEvaluationStates(project.id, [
92
+ ...evaluatedInstances,
93
+ ...errorEvaluatedInstances,
94
+ ])
95
+ } else {
96
+ // set all composite instances to error state in evaluation
97
+ const errorEvaluationStates: EvaluatedInstance[] = compositeInstanceIds.map(instanceId => ({
98
+ instanceId: instanceId as InstanceId,
99
+ status: "error",
100
+ message: result.error,
101
+ model: instancesMap.get(instanceId) ?? null,
102
+ }))
103
+
104
+ await this.setInstanceEvaluationStates(project.id, errorEvaluationStates)
105
+ }
106
+
107
+ this.logger.info(
108
+ { projectId: project.id, compositeInstanceIds },
109
+ "composite instances evaluation completed",
110
+ )
111
+ } catch (error) {
112
+ this.logger.error({ projectId: project.id, error }, "failed to evaluate project")
113
+
114
+ // set all composite instances to internal error state
115
+ const internalErrorStates: EvaluatedInstance[] = compositeInstanceIds.map(instanceId => ({
116
+ instanceId: instanceId as InstanceId,
117
+ status: "error",
118
+ message: "Internal error occurred during evaluation.",
119
+ model: instancesMap.get(instanceId) ?? null,
120
+ }))
121
+
122
+ await this.setInstanceEvaluationStates(project.id, internalErrorStates)
123
+ }
124
+ }
125
+
126
+ /**
127
+ * Sets the evaluation states for multiple instances in a project.
128
+ *
129
+ * @param projectId The ID of the project to update.
130
+ * @param evaluatedInstances The evaluation states to set.
131
+ */
132
+ private async setInstanceEvaluationStates(
133
+ projectId: string,
134
+ evaluatedInstances: EvaluatedInstance[],
135
+ ): Promise<void> {
136
+ const database = await this.database.forProject(projectId)
137
+
138
+ await database.$transaction(async tx => {
139
+ // 1. resolve instanceId -> stateId for all evaluated instances
140
+ // also ensure that all states exist and has "virtual" source
141
+ const instanceIdToStateMap = new Map<
142
+ string,
143
+ { id: string; status: InstanceStatus; model: InstanceModel | null }
144
+ >()
145
+
146
+ for (const instance of evaluatedInstances) {
147
+ const state = await tx.instanceState.upsert({
148
+ select: { id: true, status: true, model: true },
149
+ where: { instanceId: instance.instanceId },
150
+ create: {
151
+ instanceId: instance.instanceId,
152
+ kind: instance.model?.kind ?? "unit",
153
+ source: "virtual",
154
+ status: "undeployed",
155
+ },
156
+ update: {
157
+ source: "virtual",
158
+ },
159
+ })
160
+
161
+ instanceIdToStateMap.set(instance.instanceId, state)
162
+ }
163
+
164
+ // 2. convert EvaluatedInstance[] to InstanceEvaluationStateUncheckedCreateInput[]
165
+ const states: InstanceEvaluationStateUncheckedCreateInput[] = evaluatedInstances.map(ei => {
166
+ return {
167
+ // biome-ignore lint/style/noNonNullAssertion: we ensure this is always set
168
+ stateId: instanceIdToStateMap.get(ei.instanceId)!.id,
169
+ status: ei.status,
170
+ message: ei.message,
171
+ model: ei.model ?? DbNull,
172
+ }
173
+ })
174
+
175
+ // 3. persist the evaluation states
176
+ const existingStates = await tx.instanceEvaluationState.findMany({
177
+ select: { stateId: true, state: { select: { instanceId: true } } },
178
+ })
179
+
180
+ const existingStateIds = new Set(existingStates.map(state => state.stateId))
181
+ const actualStateIds = new Set(states.map(state => state.stateId))
182
+
183
+ const newStates = states.filter(state => !existingStateIds.has(state.stateId))
184
+ const statesToUpdate = states.filter(state => existingStateIds.has(state.stateId))
185
+ const statesToDelete = existingStates.filter(state => !actualStateIds.has(state.stateId))
186
+
187
+ // create new states
188
+ await tx.instanceEvaluationState.createMany({ data: newStates })
189
+
190
+ // update existing states
191
+ const updatedStates = await Promise.all(
192
+ statesToUpdate.map(state =>
193
+ tx.instanceEvaluationState.update({
194
+ where: { stateId: state.stateId },
195
+ data: state as InstanceEvaluationStateUpdateInput,
196
+ }),
197
+ ),
198
+ )
199
+
200
+ // delete states that are no longer present
201
+ await tx.instanceEvaluationState.deleteMany({
202
+ where: {
203
+ stateId: {
204
+ in: statesToDelete.map(state => state.stateId),
205
+ },
206
+ },
207
+ })
208
+
209
+ // 5. publish evaluation state updates
210
+ for (const state of updatedStates) {
211
+ void this.pubsubManager.publish(["instance-state", projectId], {
212
+ type: "patched",
213
+ stateId: state.stateId,
214
+ patch: {
215
+ evaluationState: state,
216
+ },
217
+ })
218
+ }
219
+
220
+ // 6. publish project model update
221
+ void this.pubsubManager.publish(["project-model", projectId], {
222
+ updatedVirtualInstances: updatedStates.map(state => state.model).filter(isNonNullish),
223
+ deletedVirtualInstanceIds: statesToDelete.map(state => state.state.instanceId),
224
+
225
+ updatedGhostInstances: statesToDelete
226
+ .map(state => instanceIdToStateMap.get(state.state.instanceId))
227
+ .filter(isNonNullish)
228
+ .filter(state => state.status !== "undeployed")
229
+ .map(state => state.model)
230
+ .filter(isNonNullish),
231
+ })
232
+ })
233
+ }
234
+
235
+ private async trackUnlockedProject(projectId: string): Promise<void> {
236
+ if (this.projectWatchers.has(projectId)) {
237
+ this.logger.debug({ projectId }, "project already being tracked, skipping")
238
+ return
239
+ }
240
+
241
+ try {
242
+ const project = await this.database.backend.project.findUnique({
243
+ where: { id: projectId },
244
+ select: { id: true, libraryId: true },
245
+ })
246
+
247
+ if (!project) {
248
+ this.logger.warn({ projectId }, "project not found, cannot track")
249
+ return
250
+ }
251
+
252
+ const controller = new AbortController()
253
+
254
+ this.projectWatchers.set(projectId, controller)
255
+ this.logger.debug(
256
+ { projectId, libraryId: project.libraryId },
257
+ "tracking unlocked project for library reload evaluations",
258
+ )
259
+
260
+ void this.startProjectLibraryWatcher(projectId, project.libraryId, controller.signal)
261
+ } catch (error) {
262
+ this.logger.error({ projectId, error }, "failed to start tracking project")
263
+ }
264
+ }
265
+
266
+ private async startProjectLibraryWatcher(
267
+ projectId: string,
268
+ libraryId: string,
269
+ signal: AbortSignal,
270
+ ): Promise<void> {
271
+ try {
272
+ for await (const updates of this.libraryBackend.watchLibrary(libraryId, signal)) {
273
+ // only handle reload-completed events
274
+ if (updates.some(update => update.type === "reload-completed")) {
275
+ await this.handleProjectLibraryReload(projectId)
276
+ }
277
+ }
278
+ } catch (error) {
279
+ this.logger.error({ projectId, libraryId, error }, "project library reload watcher failed")
280
+ } finally {
281
+ this.projectWatchers.delete(projectId)
282
+ }
283
+ }
284
+
285
+ private async handleProjectLibraryReload(projectId: string): Promise<void> {
286
+ this.logger.info({ projectId }, "launching evaluation for project after library reload")
287
+
288
+ try {
289
+ await this.evaluateProject(projectId)
290
+ } catch (error) {
291
+ this.logger.error({ projectId, error }, "failed to evaluate project after library reload")
292
+ }
293
+ }
294
+
295
+ static createEvaluatedInstanceTree(virtualInstances: InstanceModel[]): Map<string, TreeNode> {
296
+ const treeNodes = new Map<string, TreeNode>()
297
+
298
+ for (const instance of virtualInstances) {
299
+ const node: TreeNode = {
300
+ text: instance.id,
301
+ children: [],
302
+ }
303
+
304
+ treeNodes.set(instance.id, node)
305
+
306
+ const parentNode = instance.parentId ? treeNodes.get(instance.parentId) : undefined
307
+
308
+ if (parentNode) {
309
+ parentNode.children.push(node)
310
+ }
311
+ }
312
+
313
+ return treeNodes
314
+ }
315
+
316
+ static renderEvaluationMessage(treeNodes: Map<string, TreeNode>, rootNodeId: string): string {
317
+ // get the root node
318
+ const rootNode = treeNodes.get(rootNodeId)
319
+ if (!rootNode) {
320
+ return `Composite instance evaluation completed successfully, but failed to build the tree structure.`
321
+ }
322
+
323
+ // render the tree structure
324
+ const tree = renderTree(rootNode)
325
+
326
+ return `Composite instance evaluation completed successfully.\n\nInstance Tree:\n${tree}`
327
+ }
328
+ }
@@ -1,9 +1,13 @@
1
1
  export * from "./api-key"
2
2
  export * from "./artifact"
3
- export * from "./backend-unlock"
4
3
  export * from "./instance-lock"
5
4
  export * from "./instance-state"
6
5
  export * from "./operation"
6
+ export * from "./project"
7
+ export * from "./project-model"
7
8
  export * from "./project-unlock"
8
9
  export * from "./secret"
10
+ export * from "./settings"
11
+ export * from "./terminal-session"
12
+ export * from "./unit-extra"
9
13
  export * from "./worker"