@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
@@ -0,0 +1,695 @@
1
+ import { createId } from "@paralleldrive/cuid2"
2
+ import { describe } from "vitest"
3
+ import { test } from "../test-utils"
4
+ import { SettingsService } from "./settings"
5
+
6
+ const settingsTest = test.extend<{
7
+ settingsService: SettingsService
8
+ }>({
9
+ settingsService: [
10
+ async ({ database }, use) => {
11
+ const settingsService = new SettingsService(database)
12
+
13
+ await use(settingsService)
14
+ },
15
+ { scope: "file" },
16
+ ],
17
+ })
18
+
19
+ describe("SettingsService", () => {
20
+ describe("queryOperations", () => {
21
+ settingsTest(
22
+ "returns operations without search",
23
+ async ({ settingsService, projectDatabase, project, expect }) => {
24
+ // clear operations table
25
+ await projectDatabase.operation.deleteMany()
26
+
27
+ // arrange
28
+ const operation1 = await projectDatabase.operation.create({
29
+ data: {
30
+ id: createId(),
31
+ type: "update",
32
+ status: "completed",
33
+ meta: { title: "Update Operation" },
34
+ options: {},
35
+ requestedInstanceIds: [],
36
+ startedAt: new Date("2023-01-01"),
37
+ updatedAt: new Date("2023-01-01"),
38
+ },
39
+ })
40
+
41
+ const operation2 = await projectDatabase.operation.create({
42
+ data: {
43
+ id: createId(),
44
+ type: "destroy",
45
+ status: "pending",
46
+ meta: { title: "Destroy Operation" },
47
+ options: {},
48
+ requestedInstanceIds: [],
49
+ startedAt: new Date("2023-01-02"),
50
+ updatedAt: new Date("2023-01-02"),
51
+ },
52
+ })
53
+
54
+ // act
55
+ const result = await settingsService.queryOperations(project.id, {
56
+ sortBy: [{ key: "startedAt", order: "asc" }],
57
+ })
58
+
59
+ // assert
60
+ expect(result.items).toHaveLength(2)
61
+ expect(result.total).toBe(2)
62
+ expect(result.items[0].id).toBe(operation1.id)
63
+ expect(result.items[1].id).toBe(operation2.id)
64
+ },
65
+ )
66
+
67
+ settingsTest(
68
+ "searches operations by meta title",
69
+ async ({ settingsService, projectDatabase, project, expect }) => {
70
+ // clear operations table
71
+ await projectDatabase.operation.deleteMany()
72
+
73
+ // arrange
74
+ await projectDatabase.operation.create({
75
+ data: {
76
+ id: createId(),
77
+ type: "update",
78
+ status: "completed",
79
+ meta: { title: "Update Operation" },
80
+ options: {},
81
+ requestedInstanceIds: [],
82
+ startedAt: new Date("2023-01-01"),
83
+ updatedAt: new Date("2023-01-01"),
84
+ },
85
+ })
86
+
87
+ await projectDatabase.operation.create({
88
+ data: {
89
+ id: createId(),
90
+ type: "destroy",
91
+ status: "pending",
92
+ meta: { title: "Destroy Operation" },
93
+ options: {},
94
+ requestedInstanceIds: [],
95
+ startedAt: new Date("2023-01-02"),
96
+ updatedAt: new Date("2023-01-02"),
97
+ },
98
+ })
99
+
100
+ // act
101
+ const result = await settingsService.queryOperations(project.id, {
102
+ sortBy: [{ key: "startedAt", order: "asc" }],
103
+ search: "Update",
104
+ })
105
+
106
+ // assert
107
+ expect(result.items).toHaveLength(1)
108
+ expect(result.items[0].meta.title).toBe("Update Operation")
109
+ },
110
+ )
111
+
112
+ settingsTest(
113
+ "searches operations by type",
114
+ async ({ settingsService, projectDatabase, project, expect }) => {
115
+ // clear operations table
116
+ await projectDatabase.operation.deleteMany()
117
+
118
+ // arrange
119
+ await projectDatabase.operation.create({
120
+ data: {
121
+ id: createId(),
122
+ type: "update",
123
+ status: "completed",
124
+ meta: { title: "Update Operation" },
125
+ options: {},
126
+ requestedInstanceIds: [],
127
+ startedAt: new Date("2023-01-01"),
128
+ updatedAt: new Date("2023-01-01"),
129
+ },
130
+ })
131
+
132
+ await projectDatabase.operation.create({
133
+ data: {
134
+ id: createId(),
135
+ type: "destroy",
136
+ status: "pending",
137
+ meta: { title: "Destroy Operation" },
138
+ options: {},
139
+ requestedInstanceIds: [],
140
+ startedAt: new Date("2023-01-02"),
141
+ updatedAt: new Date("2023-01-02"),
142
+ },
143
+ })
144
+
145
+ // act
146
+ const result = await settingsService.queryOperations(project.id, {
147
+ sortBy: [{ key: "startedAt", order: "asc" }],
148
+ search: "destroy",
149
+ })
150
+
151
+ // assert
152
+ expect(result.items).toHaveLength(1)
153
+ expect(result.items[0].type).toBe("destroy")
154
+ },
155
+ )
156
+
157
+ settingsTest(
158
+ "supports page-based pagination",
159
+ async ({ settingsService, projectDatabase, project, expect }) => {
160
+ // clear operations table
161
+ await projectDatabase.operation.deleteMany()
162
+
163
+ // arrange
164
+ const startDate1 = new Date("2023-01-01")
165
+ const startDate2 = new Date("2023-01-02")
166
+ const startDate3 = new Date("2023-01-03")
167
+
168
+ await projectDatabase.operation.create({
169
+ data: {
170
+ id: createId(),
171
+ type: "update",
172
+ status: "completed",
173
+ meta: { title: "Operation 1" },
174
+ options: {},
175
+ requestedInstanceIds: [],
176
+ startedAt: startDate1,
177
+ updatedAt: startDate1,
178
+ },
179
+ })
180
+
181
+ await projectDatabase.operation.create({
182
+ data: {
183
+ id: createId(),
184
+ type: "update",
185
+ status: "completed",
186
+ meta: { title: "Operation 2" },
187
+ options: {},
188
+ requestedInstanceIds: [],
189
+ startedAt: startDate2,
190
+ updatedAt: startDate2,
191
+ },
192
+ })
193
+
194
+ await projectDatabase.operation.create({
195
+ data: {
196
+ id: createId(),
197
+ type: "update",
198
+ status: "completed",
199
+ meta: { title: "Operation 3" },
200
+ options: {},
201
+ requestedInstanceIds: [],
202
+ startedAt: startDate3,
203
+ updatedAt: startDate3,
204
+ },
205
+ })
206
+
207
+ // act - get first page
208
+ const firstPage = await settingsService.queryOperations(project.id, {
209
+ sortBy: [{ key: "startedAt", order: "asc" }],
210
+ count: 2,
211
+ skip: 0,
212
+ })
213
+
214
+ // assert first page
215
+ expect(firstPage.items).toHaveLength(2)
216
+ expect(firstPage.total).toBe(3)
217
+ expect(firstPage.items[0].meta.title).toBe("Operation 1")
218
+ expect(firstPage.items[1].meta.title).toBe("Operation 2")
219
+
220
+ // act - get second page
221
+ const secondPage = await settingsService.queryOperations(project.id, {
222
+ sortBy: [{ key: "startedAt", order: "asc" }],
223
+ count: 2,
224
+ skip: 2,
225
+ })
226
+
227
+ // assert second page
228
+ expect(secondPage.items).toHaveLength(1)
229
+ expect(secondPage.total).toBe(3)
230
+ expect(secondPage.items[0].meta.title).toBe("Operation 3")
231
+ },
232
+ )
233
+
234
+ settingsTest(
235
+ "respects sort order",
236
+ async ({ settingsService, projectDatabase, project, expect }) => {
237
+ // clear operations table
238
+ await projectDatabase.operation.deleteMany()
239
+
240
+ // arrange
241
+ const startDate1 = new Date("2023-01-01")
242
+ const startDate2 = new Date("2023-01-02")
243
+
244
+ await projectDatabase.operation.create({
245
+ data: {
246
+ id: createId(),
247
+ type: "update",
248
+ status: "completed",
249
+ meta: { title: "Earlier Operation" },
250
+ options: {},
251
+ requestedInstanceIds: [],
252
+ startedAt: startDate1,
253
+ updatedAt: startDate1,
254
+ },
255
+ })
256
+
257
+ await projectDatabase.operation.create({
258
+ data: {
259
+ id: createId(),
260
+ type: "update",
261
+ status: "completed",
262
+ meta: { title: "Later Operation" },
263
+ options: {},
264
+ requestedInstanceIds: [],
265
+ startedAt: startDate2,
266
+ updatedAt: startDate2,
267
+ },
268
+ })
269
+
270
+ // act - descending order
271
+ const descResult = await settingsService.queryOperations(project.id, {
272
+ sortBy: [{ key: "startedAt", order: "desc" }],
273
+ })
274
+
275
+ // assert
276
+ expect(descResult.items[0].meta.title).toBe("Later Operation")
277
+ expect(descResult.items[1].meta.title).toBe("Earlier Operation")
278
+ },
279
+ )
280
+ })
281
+
282
+ describe("queryTerminals", () => {
283
+ settingsTest(
284
+ "returns terminals without search",
285
+ async ({ settingsService, projectDatabase, project, expect }) => {
286
+ // clear terminals table
287
+ await projectDatabase.terminal.deleteMany()
288
+
289
+ // arrange
290
+ const terminal1 = await projectDatabase.terminal.create({
291
+ data: {
292
+ id: createId(),
293
+ name: "terminal-1",
294
+ meta: { title: "Terminal 1" },
295
+ status: "active",
296
+ spec: {},
297
+ createdAt: new Date("2023-01-01"),
298
+ },
299
+ })
300
+
301
+ const terminal2 = await projectDatabase.terminal.create({
302
+ data: {
303
+ id: createId(),
304
+ name: "terminal-2",
305
+ meta: { title: "Terminal 2" },
306
+ status: "active",
307
+ spec: {},
308
+ createdAt: new Date("2023-01-02"),
309
+ },
310
+ })
311
+
312
+ // act
313
+ const result = await settingsService.queryTerminals(project.id, {
314
+ sortBy: [{ key: "createdAt", order: "asc" }],
315
+ })
316
+
317
+ // assert
318
+ expect(result.items).toHaveLength(2)
319
+ expect(result.total).toBe(2)
320
+ expect(result.items[0].id).toBe(terminal1.id)
321
+ expect(result.items[1].id).toBe(terminal2.id)
322
+ },
323
+ )
324
+
325
+ settingsTest(
326
+ "searches terminals by name and meta title",
327
+ async ({ settingsService, projectDatabase, project, expect }) => {
328
+ // clear terminals table
329
+ await projectDatabase.terminal.deleteMany()
330
+
331
+ // arrange
332
+ await projectDatabase.terminal.create({
333
+ data: {
334
+ id: createId(),
335
+ name: "web-terminal",
336
+ meta: { title: "Web Terminal" },
337
+ status: "active",
338
+ spec: {},
339
+ createdAt: new Date("2023-01-01"),
340
+ },
341
+ })
342
+
343
+ await projectDatabase.terminal.create({
344
+ data: {
345
+ id: createId(),
346
+ name: "ssh-terminal",
347
+ meta: { title: "SSH Terminal" },
348
+ status: "active",
349
+ spec: {},
350
+ createdAt: new Date("2023-01-02"),
351
+ },
352
+ })
353
+
354
+ // act - search by name
355
+ const nameResult = await settingsService.queryTerminals(project.id, {
356
+ sortBy: [{ key: "createdAt", order: "asc" }],
357
+ search: "web",
358
+ })
359
+
360
+ // assert
361
+ expect(nameResult.items).toHaveLength(1)
362
+ expect(nameResult.items[0].name).toBe("web-terminal")
363
+
364
+ // act - search by meta title
365
+ const titleResult = await settingsService.queryTerminals(project.id, {
366
+ sortBy: [{ key: "createdAt", order: "asc" }],
367
+ search: "SSH",
368
+ })
369
+
370
+ // assert
371
+ expect(titleResult.items).toHaveLength(1)
372
+ expect(titleResult.items[0].meta.title).toBe("SSH Terminal")
373
+ },
374
+ )
375
+ })
376
+
377
+ describe("queryWorkers", () => {
378
+ settingsTest(
379
+ "returns workers and searches by id and identity",
380
+ async ({ settingsService, projectDatabase, project, expect }) => {
381
+ // clear workers and service accounts tables
382
+ await projectDatabase.worker.deleteMany()
383
+ await projectDatabase.serviceAccount.deleteMany()
384
+
385
+ // arrange
386
+ const serviceAccount1 = await projectDatabase.serviceAccount.create({
387
+ data: {
388
+ id: createId(),
389
+ meta: { title: "Test Service Account 1" },
390
+ },
391
+ })
392
+
393
+ const serviceAccount2 = await projectDatabase.serviceAccount.create({
394
+ data: {
395
+ id: createId(),
396
+ meta: { title: "Test Service Account 2" },
397
+ },
398
+ })
399
+
400
+ await projectDatabase.worker.create({
401
+ data: {
402
+ id: "worker-123",
403
+ identity: "ghcr.io/org/worker1",
404
+ serviceAccountId: serviceAccount1.id,
405
+ createdAt: new Date("2023-01-01"),
406
+ },
407
+ })
408
+
409
+ await projectDatabase.worker.create({
410
+ data: {
411
+ id: "worker-456",
412
+ identity: "ghcr.io/org/worker2",
413
+ serviceAccountId: serviceAccount2.id,
414
+ createdAt: new Date("2023-01-02"),
415
+ },
416
+ })
417
+
418
+ // act - no search
419
+ const allResult = await settingsService.queryWorkers(project.id, {
420
+ sortBy: [{ key: "createdAt", order: "asc" }],
421
+ })
422
+
423
+ // assert
424
+ expect(allResult.items).toHaveLength(2)
425
+
426
+ // act - search by id
427
+ const idResult = await settingsService.queryWorkers(project.id, {
428
+ sortBy: [{ key: "createdAt", order: "asc" }],
429
+ search: "123",
430
+ })
431
+
432
+ // assert
433
+ expect(idResult.items).toHaveLength(1)
434
+ expect(idResult.items[0].id).toBe("worker-123")
435
+
436
+ // act - search by identity
437
+ const identityResult = await settingsService.queryWorkers(project.id, {
438
+ sortBy: [{ key: "createdAt", order: "asc" }],
439
+ search: "worker2",
440
+ })
441
+
442
+ // assert
443
+ expect(identityResult.items).toHaveLength(1)
444
+ expect(identityResult.items[0].identity).toBe("ghcr.io/org/worker2")
445
+ },
446
+ )
447
+ })
448
+
449
+ describe("queryUnlockMethods", () => {
450
+ settingsTest(
451
+ "returns unlock methods and searches by type",
452
+ async ({ settingsService, projectDatabase, project, expect }) => {
453
+ // clear unlock methods table
454
+ await projectDatabase.unlockMethod.deleteMany()
455
+
456
+ // arrange
457
+ await projectDatabase.unlockMethod.create({
458
+ data: {
459
+ id: createId(),
460
+ type: "password",
461
+ meta: { title: "Password Method" },
462
+ encryptedIdentity: "encrypted-pwd",
463
+ recipient: "user@example.com",
464
+ createdAt: new Date("2023-01-01"),
465
+ updatedAt: new Date("2023-01-01"),
466
+ },
467
+ })
468
+
469
+ await projectDatabase.unlockMethod.create({
470
+ data: {
471
+ id: createId(),
472
+ type: "passkey",
473
+ meta: { title: "Passkey Method" },
474
+ encryptedIdentity: "encrypted-key",
475
+ recipient: "user@example.com",
476
+ createdAt: new Date("2023-01-02"),
477
+ updatedAt: new Date("2023-01-02"),
478
+ },
479
+ })
480
+
481
+ // act - no search
482
+ const allResult = await settingsService.queryUnlockMethods(project.id, {
483
+ sortBy: [{ key: "createdAt", order: "asc" }],
484
+ })
485
+
486
+ // assert
487
+ expect(allResult.items).toHaveLength(2)
488
+
489
+ // act - search by type
490
+ const passwordResult = await settingsService.queryUnlockMethods(project.id, {
491
+ sortBy: [{ key: "createdAt", order: "asc" }],
492
+ search: "password",
493
+ })
494
+
495
+ // assert
496
+ expect(passwordResult.items).toHaveLength(1)
497
+ expect(passwordResult.items[0].type).toBe("password")
498
+
499
+ // act - search by meta title
500
+ const titleResult = await settingsService.queryUnlockMethods(project.id, {
501
+ sortBy: [{ key: "createdAt", order: "asc" }],
502
+ search: "Passkey",
503
+ })
504
+
505
+ // assert
506
+ expect(titleResult.items).toHaveLength(1)
507
+ expect(titleResult.items[0].meta.title).toBe("Passkey Method")
508
+ },
509
+ )
510
+ })
511
+
512
+ describe("queryArtifacts", () => {
513
+ settingsTest(
514
+ "returns artifacts and searches by hash",
515
+ async ({ settingsService, projectDatabase, project, expect }) => {
516
+ // clear artifacts table
517
+ await projectDatabase.artifact.deleteMany()
518
+
519
+ // arrange
520
+ await projectDatabase.artifact.create({
521
+ data: {
522
+ id: createId(),
523
+ meta: { title: "Test Artifact 1" },
524
+ hash: "abc123",
525
+ size: 1024,
526
+ chunkSize: 512,
527
+ createdAt: new Date("2023-01-01"),
528
+ updatedAt: new Date("2023-01-01"),
529
+ },
530
+ })
531
+
532
+ await projectDatabase.artifact.create({
533
+ data: {
534
+ id: createId(),
535
+ meta: { title: "Test Artifact 2" },
536
+ hash: "def456",
537
+ size: 2048,
538
+ chunkSize: 1024,
539
+ createdAt: new Date("2023-01-02"),
540
+ updatedAt: new Date("2023-01-02"),
541
+ },
542
+ })
543
+
544
+ // act - search by hash
545
+ const result = await settingsService.queryArtifacts(project.id, {
546
+ sortBy: [{ key: "createdAt", order: "asc" }],
547
+ search: "abc",
548
+ })
549
+
550
+ // assert
551
+ expect(result.items).toHaveLength(1)
552
+ expect(result.items[0].hash).toBe("abc123")
553
+ },
554
+ )
555
+ })
556
+
557
+ describe("queryPages", () => {
558
+ settingsTest(
559
+ "returns pages and searches by name",
560
+ async ({ settingsService, projectDatabase, project, expect }) => {
561
+ // clear pages table
562
+ await projectDatabase.page.deleteMany()
563
+
564
+ // arrange
565
+ await projectDatabase.page.create({
566
+ data: {
567
+ id: createId(),
568
+ name: "home-page",
569
+ meta: { title: "Home Page" },
570
+ content: {},
571
+ createdAt: new Date("2023-01-01"),
572
+ updatedAt: new Date("2023-01-01"),
573
+ },
574
+ })
575
+
576
+ await projectDatabase.page.create({
577
+ data: {
578
+ id: createId(),
579
+ name: "about-page",
580
+ meta: { title: "About Page" },
581
+ content: {},
582
+ createdAt: new Date("2023-01-02"),
583
+ updatedAt: new Date("2023-01-02"),
584
+ },
585
+ })
586
+
587
+ // act
588
+ const result = await settingsService.queryPages(project.id, {
589
+ sortBy: [{ key: "createdAt", order: "asc" }],
590
+ search: "home",
591
+ })
592
+
593
+ // assert
594
+ expect(result.items).toHaveLength(1)
595
+ expect(result.items[0].name).toBe("home-page")
596
+ },
597
+ )
598
+ })
599
+
600
+ describe("querySecrets", () => {
601
+ settingsTest(
602
+ "returns secrets and searches by name",
603
+ async ({ settingsService, projectDatabase, project, createInstanceState, expect }) => {
604
+ // clear secrets and instance state tables
605
+ await projectDatabase.secret.deleteMany()
606
+ await projectDatabase.instanceState.deleteMany()
607
+
608
+ // arrange
609
+ const instance = await createInstanceState(project.id)
610
+
611
+ await projectDatabase.secret.create({
612
+ data: {
613
+ id: createId(),
614
+ name: "api-key",
615
+ meta: { title: "API Key" },
616
+ content: "secret-value",
617
+ stateId: instance.id,
618
+ createdAt: new Date("2023-01-01"),
619
+ updatedAt: new Date("2023-01-01"),
620
+ },
621
+ })
622
+
623
+ await projectDatabase.secret.create({
624
+ data: {
625
+ id: createId(),
626
+ name: "db-password",
627
+ meta: { title: "Database Password" },
628
+ content: "secret-password",
629
+ stateId: instance.id,
630
+ createdAt: new Date("2023-01-02"),
631
+ updatedAt: new Date("2023-01-02"),
632
+ },
633
+ })
634
+
635
+ // act
636
+ const result = await settingsService.querySecrets(project.id, {
637
+ sortBy: [{ key: "createdAt", order: "asc" }],
638
+ search: "api",
639
+ })
640
+
641
+ // assert
642
+ expect(result.items).toHaveLength(1)
643
+ expect(result.items[0].name).toBe("api-key")
644
+ },
645
+ )
646
+ })
647
+
648
+ describe("queryTriggers", () => {
649
+ settingsTest(
650
+ "returns triggers and searches by name",
651
+ async ({ settingsService, projectDatabase, project, createInstanceState, expect }) => {
652
+ // clear triggers and instance state tables
653
+ await projectDatabase.trigger.deleteMany()
654
+ await projectDatabase.instanceState.deleteMany()
655
+
656
+ // arrange
657
+ const instance = await createInstanceState(project.id)
658
+
659
+ await projectDatabase.trigger.create({
660
+ data: {
661
+ id: createId(),
662
+ name: "deploy-trigger",
663
+ meta: { title: "Deploy Trigger" },
664
+ spec: {},
665
+ stateId: instance.id,
666
+ createdAt: new Date("2023-01-01"),
667
+ updatedAt: new Date("2023-01-01"),
668
+ },
669
+ })
670
+
671
+ await projectDatabase.trigger.create({
672
+ data: {
673
+ id: createId(),
674
+ name: "backup-trigger",
675
+ meta: { title: "Backup Trigger" },
676
+ spec: {},
677
+ stateId: instance.id,
678
+ createdAt: new Date("2023-01-02"),
679
+ updatedAt: new Date("2023-01-02"),
680
+ },
681
+ })
682
+
683
+ // act
684
+ const result = await settingsService.queryTriggers(project.id, {
685
+ sortBy: [{ key: "createdAt", order: "asc" }],
686
+ search: "deploy",
687
+ })
688
+
689
+ // assert
690
+ expect(result.items).toHaveLength(1)
691
+ expect(result.items[0].name).toBe("deploy-trigger")
692
+ },
693
+ )
694
+ })
695
+ })