@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,87 +1,66 @@
1
+ import type { CommonObjectMeta } from "@highstate/contract"
1
2
  import type { Logger } from "pino"
2
- import type { StateManager } from "../state"
3
- import type { ArtifactBackend } from "../artifact/abstractions"
4
- import type { LockManager } from "../lock"
5
- import type { ObjectMeta } from "@highstate/contract"
6
- import { v7 as uuidv7 } from "uuid"
7
- import { unique } from "remeda"
8
- import { compareArtifactUsage, type Artifact, type ArtifactUsage } from "../shared"
3
+ import type { ArtifactBackend } from "../artifact"
4
+ import type { Artifact, DatabaseManager, ProjectTransaction } from "../database"
9
5
 
10
- const artifactChunkSize = 1024 * 1024 // 1 MB
6
+ export const artifactChunkSize = 1024 * 1024 // 1 MB
11
7
 
12
8
  /**
13
9
  * The service which handles the storage, retrieval, and management of artifacts in the system.
14
10
  */
15
11
  export class ArtifactService {
16
12
  constructor(
17
- private readonly stateManager: StateManager,
13
+ private readonly database: DatabaseManager,
18
14
  private readonly artifactBackend: ArtifactBackend,
19
- private readonly lockManager: LockManager,
20
15
  private readonly logger: Logger,
21
16
  ) {}
22
17
 
23
18
  /**
24
- * Stores an artifact in the backend and indexes it in the state manager.
19
+ * Stores an artifact in the backend and allows caller to set up references.
25
20
  * If the artifact already exists, it does nothing.
26
21
  *
27
- * Returns the artifact ID of the stored artifact.
28
- *
29
22
  * @param projectId The project ID to store the artifact under.
30
- * @param hash The unique hash of the artifact content.
31
- * @param meta The metadata for the artifact, including name, size, and other properties.
23
+ * @param hash The SHA256 hash of the artifact content.
32
24
  * @param size The total size of the artifact in bytes.
33
- * @param chunkSize The size of each chunk in bytes.
25
+ * @param meta The metadata for the artifact.
34
26
  * @param content An async iterable providing the artifact content in chunks.
35
- * @param usages The list of usages for this artifact. Can be provided later.
27
+ * @param track A callback to set up artifact references within the transaction.
28
+ * @returns The model of the stored artifact.
36
29
  */
37
30
  async store(
38
31
  projectId: string,
39
32
  hash: string,
40
33
  size: number,
41
- meta: ObjectMeta,
34
+ meta: CommonObjectMeta,
42
35
  content: AsyncIterable<Uint8Array>,
43
- usages: ArtifactUsage[] = [],
44
- ): Promise<string> {
45
- return await this.lockManager.acquire(["artifact-hash", projectId, hash], async () => {
46
- const existingArtifact = await this.stateManager
47
- .getArtifactHashIndexRepository(projectId)
48
- .get(hash)
49
-
36
+ track: (tx: ProjectTransaction, artifact: Artifact) => Promise<void>,
37
+ ): Promise<Artifact> {
38
+ // check if artifact already exists by hash
39
+ const database = await this.database.forProject(projectId)
40
+ const existingArtifact = await database.artifact.findUnique({ where: { hash } })
41
+
42
+ // only upload to backend if file doesn't exist there
43
+ if (!existingArtifact || !(await this.artifactBackend.exists(projectId, hash))) {
50
44
  if (existingArtifact) {
51
- const fileExists = await this.artifactBackend.exists(projectId, hash)
52
- if (fileExists) {
53
- this.logger.debug(`artifact with hash "%s" already exists`, hash)
54
- return existingArtifact.id
55
- }
56
-
57
- // TODO: make this check configurable
58
45
  this.logger.warn(
59
- `artifact with hash "%s" exists in index but not in backend, re-uploading`,
46
+ `artifact with hash "%s" exists in database but not in the storage backend, re-uploading`,
60
47
  hash,
61
48
  )
62
49
  }
63
50
 
64
51
  await this.artifactBackend.store(projectId, hash, artifactChunkSize, content)
52
+ }
65
53
 
66
- const artifact: Artifact = {
67
- id: uuidv7(),
68
- hash,
69
- size,
70
- usages,
71
- meta,
72
- chunkSize: artifactChunkSize,
73
- }
74
-
75
- const batch = this.stateManager.batch()
76
-
77
- await Promise.all([
78
- this.stateManager.getArtifactRepository(projectId).putItem(artifact, batch),
79
- this.stateManager
80
- .getArtifactHashIndexRepository(projectId)
81
- .indexRepository.put(hash, artifact.id, batch),
82
- ])
54
+ return await database.$transaction(async tx => {
55
+ // create or update the main artifact record
56
+ const artifact = await tx.artifact.upsert({
57
+ where: { hash },
58
+ create: { hash, size, meta, chunkSize: artifactChunkSize },
59
+ update: { meta },
60
+ })
83
61
 
84
- await batch.write()
62
+ // always allow caller to set up references
63
+ await track(tx, artifact)
85
64
 
86
65
  this.logger.info(
87
66
  { projectId },
@@ -90,200 +69,141 @@ export class ArtifactService {
90
69
  artifact.id,
91
70
  )
92
71
 
93
- return artifact.id
72
+ return artifact
94
73
  })
95
74
  }
96
75
 
97
76
  /**
98
- * Add usages for artifacts in the project.
77
+ * Clears all artifact references for a specific instance and runs garbage collection.
78
+ * This removes all associations between the instance and its artifacts, then
79
+ * cleans up any unreferenced artifacts.
99
80
  *
100
- * @param projectId The project ID to which the artifacts belong.
101
- * @param artifactIds The IDs of the artifacts to track usages for.
102
- * @param usages The list of usages to track for the artifacts.
103
- * @returns
81
+ * @param projectId The project ID.
82
+ * @param instanceId The instance ID to clear artifact references for.
104
83
  */
105
- async addUsages(
106
- projectId: string,
107
- artifactIds: string[],
108
- usages: ArtifactUsage[],
109
- ): Promise<void> {
110
- if (artifactIds.length === 0 || usages.length === 0) {
111
- return
112
- }
113
-
114
- await this.lockManager.acquire(
115
- artifactIds.map(id => ["artifact", projectId, id] as const),
116
- () => this._addUsages(projectId, artifactIds, usages),
117
- )
118
- }
119
-
120
- private async _addUsages(
121
- projectId: string,
122
- artifactIds: string[],
123
- usages: ArtifactUsage[],
124
- ): Promise<void> {
125
- this.logger.debug({
126
- msg: "tracking artifact usages",
127
- projectId,
128
- artifactIds,
129
- usages,
84
+ async clearInstanceArtifactReferences(projectId: string, instanceId: string): Promise<void> {
85
+ const database = await this.database.forProject(projectId)
86
+
87
+ this.logger.info({ projectId, instanceId }, "clearing instance artifact references")
88
+
89
+ // clear instance-artifact associations for the specific instance
90
+ await database.$transaction(async tx => {
91
+ // get all artifacts connected to this instance
92
+ const artifactsConnectedToInstance = await tx.artifact.findMany({
93
+ where: {
94
+ instances: {
95
+ some: {
96
+ id: instanceId,
97
+ },
98
+ },
99
+ },
100
+ select: {
101
+ id: true,
102
+ },
103
+ })
104
+
105
+ // disconnect the specific instance from these artifacts
106
+ await Promise.all(
107
+ artifactsConnectedToInstance.map(artifact =>
108
+ tx.artifact.update({
109
+ where: { id: artifact.id },
110
+ data: {
111
+ instances: {
112
+ disconnect: { id: instanceId },
113
+ },
114
+ },
115
+ }),
116
+ ),
117
+ )
130
118
  })
131
119
 
132
- const artifacts = await this.stateManager
133
- .getArtifactRepository(projectId)
134
- .getManyRecord(artifactIds)
135
-
136
- const artifactsToUpdate: Artifact[] = []
137
-
138
- for (const artifactId of artifactIds) {
139
- const artifact = artifacts[artifactId]
140
- if (!artifact) {
141
- this.logger.warn({
142
- msg: "artifact not found during usage addition",
143
- projectId,
144
- artifactId,
145
- })
146
- continue
147
- }
148
-
149
- // add new usages to the artifact
150
- for (const usage of usages) {
151
- if (!artifact.usages.some(u => compareArtifactUsage(u, usage))) {
152
- artifact.usages.push(usage)
153
- }
154
- }
155
-
156
- artifactsToUpdate.push(artifact)
157
- }
120
+ this.logger.info(
121
+ { projectId, instanceId },
122
+ "cleared instance artifact references, running garbage collection",
123
+ )
158
124
 
159
- await this.stateManager.getArtifactRepository(projectId).putManyItems(artifactsToUpdate)
125
+ // run garbage collection to clean up now-unreferenced artifacts
126
+ await this.collectGarbage(projectId)
160
127
  }
161
128
 
162
129
  /**
163
- * Removes usages for artifacts in the project.
164
- *
165
- * If an artifact has no usages left, it will be deleted immediately.
130
+ * Removes artifacts with no references and cleans up backend storage.
166
131
  *
167
- * @param projectId The project ID to which the artifacts belong.
168
- * @param artifactIds The IDs of the artifacts to remove usages for.
169
- * @param usages The list of usages to remove from the artifacts.
132
+ * @param projectId The project ID to clean up artifacts for.
170
133
  */
171
- async removeUsages(
172
- projectId: string,
173
- artifactIds: string[],
174
- usages: ArtifactUsage[],
175
- ): Promise<void> {
176
- if (artifactIds.length === 0 || usages.length === 0) {
134
+ async collectGarbage(projectId: string): Promise<void> {
135
+ const database = await this.database.forProject(projectId)
136
+
137
+ // find artifacts with no references using Prisma ORM
138
+ const unreferencedArtifacts = await database.artifact.findMany({
139
+ where: {
140
+ AND: [
141
+ { serviceAccounts: { none: {} } },
142
+ { instances: { none: {} } },
143
+ { terminals: { none: {} } },
144
+ { pages: { none: {} } },
145
+ ],
146
+ },
147
+ select: {
148
+ id: true,
149
+ hash: true,
150
+ },
151
+ })
152
+
153
+ if (unreferencedArtifacts.length === 0) {
154
+ this.logger.debug({ projectId }, "no unreferenced artifacts found")
177
155
  return
178
156
  }
179
157
 
180
- await this.lockManager.acquire(
181
- artifactIds.map(id => ["artifact", projectId, id] as const),
182
- () => this._removeUsages(projectId, artifactIds, usages),
158
+ this.logger.info(
159
+ { projectId, count: unreferencedArtifacts.length },
160
+ "collecting garbage artifacts",
183
161
  )
184
- }
185
162
 
186
- private async _removeUsages(
187
- projectId: string,
188
- artifactIds: string[],
189
- usages: ArtifactUsage[],
190
- ): Promise<void> {
191
- this.logger.debug({
192
- msg: "removing artifact usages",
193
- projectId,
194
- artifactIds,
195
- usages,
163
+ // delete unreferenced artifacts in transaction
164
+ await database.$transaction(async tx => {
165
+ await tx.artifact.deleteMany({
166
+ where: {
167
+ id: { in: unreferencedArtifacts.map(a => a.id) },
168
+ },
169
+ })
196
170
  })
197
171
 
198
- const artifacts = await this.stateManager
199
- .getArtifactRepository(projectId)
200
- .getManyRecord(artifactIds)
201
-
202
- const artifactsToUpdate: Artifact[] = []
203
- const artifactsToDelete: Artifact[] = []
204
-
205
- for (const artifactId of artifactIds) {
206
- const artifact = artifacts[artifactId]
207
- if (!artifact) {
208
- this.logger.warn({
209
- msg: "artifact not found during usage removal",
210
- projectId,
211
- artifactId,
212
- })
213
- continue
214
- }
215
-
216
- // remove specified usages from the artifact
217
- artifact.usages = artifact.usages.filter(
218
- u => !usages.some(usage => compareArtifactUsage(u, usage)),
219
- )
220
-
221
- if (artifact.usages.length === 0) {
222
- // if no usages left, mark for deletion
223
- artifactsToDelete.push(artifact)
224
- } else {
225
- // otherwise, update the artifact metadata
226
- artifactsToUpdate.push(artifact)
227
- }
228
- }
229
-
230
- if (artifactsToUpdate.length > 0) {
231
- await this.stateManager.getArtifactRepository(projectId).putManyItems(artifactsToUpdate)
232
- }
172
+ // clean up backend storage for deleted artifacts in foreground
173
+ await Promise.all(
174
+ unreferencedArtifacts.map(async artifact => {
175
+ try {
176
+ await this.artifactBackend.delete(projectId, artifact.hash)
177
+ } catch (error: unknown) {
178
+ this.logger.warn(
179
+ { error, projectId, hash: artifact.hash },
180
+ `failed to delete artifact from backend with hash "%s"`,
181
+ artifact.hash,
182
+ )
183
+ }
184
+ }),
185
+ )
233
186
 
234
- if (artifactsToDelete.length > 0) {
235
- await this.removeArtifacts(projectId, artifactsToDelete)
236
- }
187
+ this.logger.info(
188
+ { projectId, deletedCount: unreferencedArtifacts.length },
189
+ "garbage collection completed",
190
+ )
237
191
  }
238
192
 
239
193
  /**
240
- * Updates the usage of artifacts which still have it and removes the usage from artifacts which no longer have it.
194
+ * Gets artifact entities by their IDs.
241
195
  *
242
- * @param projectId The project ID to which the artifacts belong.
243
- * @param usage The usage to track for the artifacts.
244
- * @param oldArtifactIds The IDs of the artifacts that previously had this usage.
245
- * @param newArtifactIds The IDs of the artifacts that now have this usage.
196
+ * @param projectId The project ID to query artifacts from.
197
+ * @param artifactIds The IDs of the artifacts to retrieve.
198
+ * @returns Array of artifact models.
246
199
  */
247
- async updateUsage(
248
- projectId: string,
249
- usage: ArtifactUsage,
250
- oldArtifactIds: string[],
251
- newArtifactIds: string[],
252
- ): Promise<void> {
253
- const removedArtifactIds = oldArtifactIds.filter(id => !newArtifactIds.includes(id))
254
- const allArtifactIds = unique([...oldArtifactIds, ...newArtifactIds])
200
+ async getArtifactsByIds(projectId: string, artifactIds: string[]): Promise<Artifact[]> {
201
+ const database = await this.database.forProject(projectId)
255
202
 
256
- await this.lockManager.acquire(
257
- allArtifactIds.map(id => ["artifact", projectId, id] as const),
258
- async () => {
259
- await this._addUsages(projectId, newArtifactIds, [usage])
260
- await this._removeUsages(projectId, removedArtifactIds, [usage])
203
+ return await database.artifact.findMany({
204
+ where: {
205
+ id: { in: artifactIds },
261
206
  },
262
- )
263
- }
264
-
265
- private async removeArtifacts(projectId: string, artifacts: Artifact[]): Promise<void> {
266
- const batch = this.stateManager.batch()
267
-
268
- await Promise.all([
269
- this.stateManager.getArtifactRepository(projectId).deleteMany(
270
- artifacts.map(a => a.id),
271
- batch,
272
- ),
273
- this.stateManager.getArtifactHashIndexRepository(projectId).indexRepository.deleteMany(
274
- artifacts.map(a => a.hash),
275
- batch,
276
- ),
277
- ])
278
-
279
- await Promise.allSettled(
280
- artifacts.map(artifact => this.artifactBackend.delete(projectId, artifact.hash)),
281
- )
282
-
283
- this.logger.info({
284
- msg: "deleted dangling artifacts",
285
- projectId,
286
- artifactHashes: artifacts.map(a => a.hash),
287
207
  })
288
208
  }
289
209
  }