@highstate/backend 0.9.18 → 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 (331) hide show
  1. package/dist/chunk-5WVU2AK4.js +1535 -0
  2. package/dist/chunk-5WVU2AK4.js.map +1 -0
  3. package/dist/{chunk-OU5OQBLB.js → chunk-I7BWSAN6.js} +3 -28
  4. package/dist/{chunk-OU5OQBLB.js.map → chunk-I7BWSAN6.js.map} +1 -1
  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 +7587 -7291
  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 +27 -0
  32. package/prisma/project/artifact.prisma +52 -0
  33. package/prisma/project/custom-status.prisma +46 -0
  34. package/prisma/project/evaluation.prisma +35 -0
  35. package/prisma/project/instance.prisma +160 -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 +41 -0
  72. package/prisma/project/secret.prisma +42 -0
  73. package/prisma/project/service-account.prisma +36 -0
  74. package/prisma/project/terminal.prisma +90 -0
  75. package/prisma/project/trigger.prisma +31 -0
  76. package/prisma/project/unlock-method.prisma +32 -0
  77. package/prisma/project/worker.prisma +138 -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 +465 -130
  97. package/src/business/secret.ts +186 -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 +435 -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 +33 -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 +64 -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 +222 -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 +1 -1
  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
@@ -1,281 +1,250 @@
1
1
  import type { Logger } from "pino"
2
+ import type { DatabaseManager, ProjectTransaction } from "../database"
2
3
  import type { LibraryBackend } from "../library"
3
- import type { StateManager } from "../state"
4
- import type { InstanceStateService } from "./instance-state"
5
- import type { RandomProvider } from "../common"
4
+ import type { PubSubManager } from "../pubsub"
5
+ import { randomBytes } from "node:crypto"
6
+ import { isUnitModel, parseInstanceId, type CommonObjectMeta } from "@highstate/contract"
6
7
  import {
7
- HighstateSignature,
8
- isUnitModel,
9
- parseInstanceId,
10
- type UnitSecretModel,
11
- } from "@highstate/contract"
12
- import { isNonNullish } from "remeda"
13
- import { formatSecretDescriptor, type Project, type Secret, type SecretDescriptor } from "../shared"
8
+ InstanceStateNotFoundError,
9
+ InvalidInstanceKindError,
10
+ ProjectNotFoundError,
11
+ SystemSecretNames,
12
+ } from "../shared"
14
13
 
15
14
  export class SecretService {
16
15
  constructor(
17
- private readonly stateManager: StateManager,
16
+ private readonly database: DatabaseManager,
17
+ private readonly pubsubManager: PubSubManager,
18
18
  private readonly libraryBackend: LibraryBackend,
19
- private readonly instanceStateService: InstanceStateService,
20
- private readonly random: RandomProvider,
21
19
  private readonly logger: Logger,
22
20
  ) {}
23
21
 
24
22
  /**
25
- * Updates secrets for a specific instance, handling both creation/updates and deletions.
26
- * Also updates the instance state's secretNames field.
23
+ * Updates the secrets for a specific instance within an existing transaction.
24
+ * Only works with unit instances.
27
25
  *
28
- * @param project The project to which the instance belongs.
29
- * @param instanceId The instance ID.
30
- * @param changedSecretValues The secrets to create or update.
31
- * @param deletedSecretNames The names of secrets to delete.
32
- * @param invalidateState Whether to invalidate the instance state after updating secrets.
26
+ * @param tx The database transaction to use.
27
+ * @param libraryId The ID of the library containing the component.
28
+ * @param stateId The ID of the instance state.
29
+ * @param secretValues The secrets to create or update. Missing secrets will be deleted.
30
+ * @returns The list of secret names that were updated or created.
33
31
  */
34
- async updateInstanceSecrets(
35
- project: Project,
36
- instanceId: string,
37
- changedSecretValues: Record<string, unknown>,
38
- deletedSecretNames: string[],
39
- invalidateState = true,
40
- ): Promise<void> {
41
- const library = await this.libraryBackend.loadLibrary(project.libraryId)
32
+ async updateInstanceSecretsCore(
33
+ tx: ProjectTransaction,
34
+ libraryId: string,
35
+ stateId: string,
36
+ secretValues: Record<string, unknown>,
37
+ ): Promise<string[]> {
38
+ // verify instance exists and is a unit
39
+ const state = await tx.instanceState.findUnique({
40
+ where: { id: stateId },
41
+ select: { kind: true, instanceId: true },
42
+ })
43
+
44
+ if (!state) {
45
+ throw new InstanceStateNotFoundError("", stateId)
46
+ }
47
+
48
+ if (state.kind !== "unit") {
49
+ throw new InvalidInstanceKindError("", stateId, "unit", state.kind)
50
+ }
51
+
52
+ const library = await this.libraryBackend.loadLibrary(libraryId)
53
+
54
+ const [componentType] = parseInstanceId(state.instanceId)
55
+ const component = library.components[componentType]
42
56
 
43
- const [type] = parseInstanceId(instanceId)
44
- const component = library.components[type]
45
57
  if (!component) {
46
- throw new Error(`Component type ${type} not found in library`)
58
+ throw new Error(`Component type "${componentType}" not found in library "${libraryId}"`)
47
59
  }
48
60
 
49
61
  if (!isUnitModel(component)) {
50
- throw new Error(`Component type ${type} is not a unit model`)
62
+ throw new Error(`Component type "${componentType}" is not a unit model`)
51
63
  }
52
64
 
53
- // validate changed secrets
54
- for (const secretName of Object.keys(changedSecretValues)) {
55
- if (!component.secrets[secretName]) {
56
- throw new Error(`Secret ${secretName} not found in component ${type}`)
65
+ // upsert provided secrets
66
+ for (const [secretName, value] of Object.entries(secretValues)) {
67
+ const componentSecret = component.secrets[secretName]
68
+ if (!componentSecret) {
69
+ throw new Error(`Secret "${secretName}" not defined in component "${componentType}"`)
57
70
  }
58
- }
59
71
 
60
- // validate deleted secrets
61
- for (const secretName of deletedSecretNames) {
62
- if (!component.secrets[secretName]) {
63
- throw new Error(`Secret ${secretName} not found in component ${type}`)
72
+ const meta: CommonObjectMeta = {
73
+ ...componentSecret.meta,
74
+
75
+ // fallback to component icon if secret icon is not defined
76
+ icon: componentSecret.meta.icon || component.meta.icon,
77
+ iconColor: componentSecret.meta.iconColor || component.meta.iconColor,
64
78
  }
65
- }
66
79
 
67
- // use a batch for atomic operations
68
- await using batch = this.stateManager.batch()
69
-
70
- const secretRepo = this.stateManager.getSecretRepository(project.id)
71
- const secretContentRepo = this.stateManager.getSecretContentRepository(project.id)
72
- const indexRepo = this.stateManager.getSecretIndexRepository(project.id)
73
-
74
- // process changed secrets
75
- for (const [secretName, value] of Object.entries(changedSecretValues)) {
76
- const descriptor: SecretDescriptor = { type: "instance", instanceId, secretName }
77
- const indexKey = formatSecretDescriptor(descriptor)
78
-
79
- // check if secret already exists
80
- const existingSecret = await indexRepo.get(indexKey)
81
-
82
- // create or update secret info
83
- const secret: Secret = {
84
- id: existingSecret?.id ?? this.random.uuidv7(),
85
- descriptor: descriptor,
86
- meta: {
87
- ...existingSecret?.meta,
88
- ...component.secrets[secretName].meta,
89
- icon: component.secrets[secretName].meta.icon ?? component.meta.icon,
80
+ await tx.secret.upsert({
81
+ where: {
82
+ stateId_name: {
83
+ stateId,
84
+ name: secretName,
85
+ },
90
86
  },
91
- }
87
+ update: {
88
+ meta,
89
+ content: value,
90
+ },
91
+ create: {
92
+ stateId,
93
+ name: secretName,
94
+ meta,
95
+ content: value,
96
+ },
97
+ })
98
+ }
92
99
 
93
- // store secret info and content
94
- await secretRepo.putItem(secret, batch)
95
- await secretContentRepo.put(indexKey, value, batch)
100
+ // delete secrets that are no longer provided
101
+ const providedSecretNames = Object.keys(secretValues)
102
+ await tx.secret.deleteMany({
103
+ where: {
104
+ stateId,
105
+ name: { notIn: providedSecretNames },
106
+ },
107
+ })
96
108
 
97
- // update index mapping
98
- await indexRepo.indexRepository.put(indexKey, secret.id, batch)
99
- }
109
+ return Object.keys(secretValues)
110
+ }
100
111
 
101
- // process deleted secrets
102
- for (const secretName of deletedSecretNames) {
103
- const indexKey = formatSecretDescriptor({ type: "instance", instanceId, secretName })
104
-
105
- // get the secret ID from index
106
- const secretId = await indexRepo.indexRepository.get(indexKey)
107
- if (secretId) {
108
- // delete secret info, content, and index entry
109
- await secretRepo.delete(secretId, batch)
110
- await secretContentRepo.delete(indexKey, batch)
111
- await indexRepo.indexRepository.delete(indexKey, batch)
112
- }
112
+ /**
113
+ * Updates secrets for a specific instance, handling both creation/updates and deletions.
114
+ * Only works with unit instances.
115
+ *
116
+ * @param projectId The project ID containing the instance.
117
+ * @param stateId The ID of the instance state.
118
+ * @param secretValues The secrets to create or update. Missing secrets will be deleted.
119
+ */
120
+ async updateInstanceSecrets(
121
+ projectId: string,
122
+ stateId: string,
123
+ secretValues: Record<string, unknown>,
124
+ ): Promise<void> {
125
+ const database = await this.database.forProject(projectId)
126
+
127
+ const project = await this.database.backend.project.findUnique({
128
+ where: { id: projectId },
129
+ select: { libraryId: true },
130
+ })
131
+
132
+ if (!project) {
133
+ throw new ProjectNotFoundError(projectId)
113
134
  }
114
135
 
115
- // write all changes atomically
116
- await batch.write()
136
+ const statePatch = await database.$transaction(async tx => {
137
+ await this.updateInstanceSecretsCore(tx, project.libraryId, stateId, secretValues)
117
138
 
118
- // update instance state secretNames
119
- await this.instanceStateService.updateStateSecretNames(
120
- project.id,
121
- instanceId,
122
- Object.keys(changedSecretValues),
123
- deletedSecretNames,
124
- invalidateState,
125
- )
139
+ // invalidate instance state
140
+ const state = await tx.instanceState.update({
141
+ where: { id: stateId },
142
+ data: { inputHashNonce: randomBytes(4).readInt32LE() },
143
+ select: { inputHashNonce: true },
144
+ })
145
+
146
+ return { ...state, secretNames: Object.keys(secretValues) }
147
+ })
148
+
149
+ this.pubsubManager.publish(["instance-state", projectId], {
150
+ type: "patched",
151
+ stateId,
152
+ patch: statePatch,
153
+ })
126
154
 
127
155
  this.logger.info(
128
156
  {
129
- projectId: project.id,
130
- instanceId,
131
- changedCount: Object.keys(changedSecretValues).length,
132
- deletedCount: deletedSecretNames.length,
157
+ projectId,
158
+ stateId,
159
+ secretCount: Object.keys(secretValues).length,
133
160
  },
134
- `updated instance secrets for instance "%s"`,
135
- instanceId,
161
+ "updated instance secrets",
136
162
  )
137
163
  }
138
164
 
139
165
  /**
140
166
  * Gets the values of all secrets for a specific instance.
167
+ * Only works with unit instances.
141
168
  *
142
- * @param project The project to which the instance belongs.
143
- * @param instanceId The instance ID.
169
+ * @param projectId The project ID containing the instance.
170
+ * @param stateId The ID of the instance state.
144
171
  * @returns A record of secret key-value pairs.
145
172
  */
146
173
  async getInstanceSecretValues(
147
- project: Project,
148
- instanceId: string,
174
+ projectId: string,
175
+ stateId: string,
149
176
  ): Promise<Record<string, unknown>> {
150
- const { indexKeys, secrets, secretContentMap } = await this.getInstanceSecretData(
151
- project,
152
- instanceId,
153
- )
177
+ const database = await this.database.forProject(projectId)
154
178
 
155
- // build the result mapping secret names to values
156
- const values: Record<string, unknown> = {}
157
- for (const descriptor of indexKeys) {
158
- const secret = secrets[descriptor]
159
- if (!secret) {
160
- continue
161
- }
179
+ // verify instance exists and is a unit
180
+ const state = await database.instanceState.findUnique({
181
+ where: { id: stateId },
182
+ select: { kind: true },
183
+ })
162
184
 
163
- const content = secretContentMap[secret.id]
164
- if (content) {
165
- values[descriptor] = content
166
- }
185
+ if (!state) {
186
+ throw new InstanceStateNotFoundError(projectId, stateId)
167
187
  }
168
188
 
169
- return secrets
170
- }
171
-
172
- /**
173
- * Gets the secrets for a specific instance, including metadata.
174
- * Will create missing secrets with empty values.
175
- *
176
- * @param project The project to which the instance belongs.
177
- * @param instanceId The instance ID.
178
- * @return A record of secret key-value pairs with metadata.
179
- */
180
- async getUnitSecrets(
181
- project: Project,
182
- instanceId: string,
183
- ): Promise<Record<string, UnitSecretModel>> {
184
- const { descriptors, component, secrets, secretContentMap } = await this.getInstanceSecretData(
185
- project,
186
- instanceId,
187
- )
188
-
189
- const missingDescriptors: SecretDescriptor[] = []
190
- const result: Record<string, UnitSecretModel> = {}
191
-
192
- for (const descriptor of descriptors) {
193
- const secret = secrets[formatSecretDescriptor(descriptor)]
194
-
195
- if (secret) {
196
- result[descriptor.secretName] = {
197
- [HighstateSignature.Secret]: true,
198
- id: secret.id,
199
- value: secretContentMap[secret.id],
200
- }
201
- } else {
202
- missingDescriptors.push(descriptor)
203
- }
189
+ if (state.kind !== "unit") {
190
+ throw new InvalidInstanceKindError(projectId, stateId, "unit", state.kind)
204
191
  }
205
192
 
206
- const batch = this.stateManager.batch()
207
-
208
- // create missing secrets with empty values
209
- for (const descriptor of missingDescriptors) {
210
- const secret: Secret = {
211
- id: this.random.uuidv7(),
212
- meta: {
213
- ...component.secrets[descriptor.secretName].meta,
214
- icon: component.secrets[descriptor.secretName].meta.icon ?? component.meta.icon,
215
- },
216
- descriptor,
217
- }
218
-
219
- const rawDescriptor = formatSecretDescriptor(descriptor)
220
-
221
- await this.stateManager.getSecretRepository(project.id).putItem(secret, batch)
222
- await this.stateManager.getSecretContentRepository(project.id).put(rawDescriptor, null, batch)
193
+ const secrets = await database.secret.findMany({
194
+ where: {
195
+ stateId,
196
+ name: { not: null },
197
+ },
198
+ })
223
199
 
224
- await this.stateManager
225
- .getSecretIndexRepository(project.id)
226
- .indexRepository.put(rawDescriptor, secret.id, batch)
200
+ const values: Record<string, unknown> = {}
227
201
 
228
- result[descriptor.secretName] = {
229
- [HighstateSignature.Secret]: true,
230
- id: secret.id,
231
- value: null,
202
+ for (const secret of secrets) {
203
+ if (secret.name) {
204
+ values[secret.name] = secret.content
232
205
  }
233
206
  }
234
207
 
235
- await batch.write()
236
-
237
- return result
208
+ return values
238
209
  }
239
210
 
240
- private async getInstanceSecretData(project: Project, instanceId: string) {
241
- const library = await this.libraryBackend.loadLibrary(project.libraryId)
242
-
243
- const [type] = parseInstanceId(instanceId)
244
- const component = library.components[type]
245
- if (!component) {
246
- throw new Error(`Component type ${type} not found in library`)
247
- }
248
-
249
- if (!isUnitModel(component)) {
250
- throw new Error(`Component type ${type} is not a unit model`)
251
- }
252
-
253
- const descriptors = Object.keys(component.secrets).map(secretName => ({
254
- type: "instance" as const,
255
- instanceId,
256
- secretName,
257
- }))
258
-
259
- const indexKeys = descriptors.map(formatSecretDescriptor)
211
+ /**
212
+ * Gets or creates the Pulumi password secret for the given project.
213
+ * Uses the new direct systemName field approach.
214
+ *
215
+ * @param projectId The ID of the project for which to get or create the Pulumi password.
216
+ * @returns The Pulumi password.
217
+ */
218
+ async getPulumiPassword(projectId: string): Promise<string> {
219
+ const database = await this.database.forProject(projectId)
260
220
 
261
- const secrets = await this.stateManager
262
- .getSecretIndexRepository(project.id)
263
- .getManyRecord(indexKeys)
221
+ return await database.$transaction(async tx => {
222
+ const existingSecret = await tx.secret.findUnique({
223
+ where: {
224
+ systemName: SystemSecretNames.PulumiPassword,
225
+ },
226
+ })
264
227
 
265
- const secretIds = Object.values(secrets)
266
- .map(secret => secret?.id)
267
- .filter(isNonNullish)
228
+ if (existingSecret) {
229
+ return existingSecret.content as string
230
+ }
268
231
 
269
- const secretContentMap = await this.stateManager
270
- .getSecretContentRepository(project.id)
271
- .getManyRecord(secretIds)
232
+ const newPassword = randomBytes(32).toString("hex")
233
+
234
+ await tx.secret.create({
235
+ data: {
236
+ systemName: SystemSecretNames.PulumiPassword,
237
+ meta: {
238
+ title: "Pulumi Password",
239
+ description: "The password used to encrypt the Pulumi state.",
240
+ icon: "devicon:pulumi",
241
+ },
242
+ content: newPassword,
243
+ },
244
+ })
272
245
 
273
- return {
274
- indexKeys,
275
- descriptors,
276
- component,
277
- secrets,
278
- secretContentMap,
279
- }
246
+ this.logger.info({ projectId }, "created new Pulumi password")
247
+ return newPassword
248
+ })
280
249
  }
281
250
  }