@highstate/backend 0.9.16 → 0.9.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-5WVU2AK4.js +1535 -0
- package/dist/chunk-5WVU2AK4.js.map +1 -0
- package/dist/chunk-I7BWSAN6.js +49 -0
- package/dist/chunk-I7BWSAN6.js.map +1 -0
- package/dist/{chunk-RCB4AFGD.js → chunk-VB4YL327.js} +51 -71
- package/dist/chunk-VB4YL327.js.map +1 -0
- package/dist/database/local/prisma.config.js +26 -0
- package/dist/database/local/prisma.config.js.map +1 -0
- package/dist/highstate.manifest.json +5 -4
- package/dist/index.js +7676 -6634
- package/dist/index.js.map +1 -1
- package/dist/library/package-resolution-worker.js +8 -6
- package/dist/library/package-resolution-worker.js.map +1 -1
- package/dist/library/worker/main.js +63 -58
- package/dist/library/worker/main.js.map +1 -1
- package/dist/shared/index.js +3 -216
- package/dist/shared/index.js.map +1 -1
- package/package.json +23 -11
- package/prisma/backend/_schema/layout.prisma +7 -0
- package/prisma/backend/_schema/library.prisma +17 -0
- package/prisma/backend/_schema/project.prisma +101 -0
- package/prisma/backend/_schema/pulumi.prisma +17 -0
- package/prisma/backend/postgresql/main.prisma +17 -0
- package/prisma/backend/sqlite/main.prisma +17 -0
- package/prisma/backend/sqlite/migrations/20250817070609_initiial/migration.sql +34 -0
- package/prisma/backend/sqlite/migrations/20250817104948_add_fields/migration.sql +59 -0
- package/prisma/backend/sqlite/migrations/20250818082732_add_models/migration.sql +41 -0
- package/prisma/backend/sqlite/migrations/20250818083106_a/migration.sql +19 -0
- package/prisma/backend/sqlite/migrations/20250818101945_hi/migration.sql +1 -0
- package/prisma/backend/sqlite/migrations/20250819082315_a/migration.sql +5 -0
- package/prisma/backend/sqlite/migrations/migration_lock.toml +3 -0
- package/prisma/project/api-key.prisma +27 -0
- package/prisma/project/artifact.prisma +52 -0
- package/prisma/project/custom-status.prisma +46 -0
- package/prisma/project/evaluation.prisma +35 -0
- package/prisma/project/instance.prisma +160 -0
- package/prisma/project/layout.prisma +23 -0
- package/prisma/project/lock.prisma +18 -0
- package/prisma/project/main.prisma +17 -0
- package/prisma/project/migrations/20250816081310_initial/migration.sql +300 -0
- package/prisma/project/migrations/20250816082523_test/migration.sql +72 -0
- package/prisma/project/migrations/20250818065643_update/migration.sql +42 -0
- package/prisma/project/migrations/20250818070758_a/migration.sql +8 -0
- package/prisma/project/migrations/20250818070913_a/migration.sql +8 -0
- package/prisma/project/migrations/20250818082720_add_motels/migration.sql +11 -0
- package/prisma/project/migrations/20250818112523_hello/migration.sql +35 -0
- package/prisma/project/migrations/20250819082305_a/migration.sql +14 -0
- package/prisma/project/migrations/20250819165004_add_missing_fields/migration.sql +216 -0
- package/prisma/project/migrations/20250819171309_a/migration.sql +22 -0
- package/prisma/project/migrations/20250820113949_a/migration.sql +66 -0
- package/prisma/project/migrations/20250820144256_b/migration.sql +31 -0
- package/prisma/project/migrations/20250820145547_a/migration.sql +24 -0
- package/prisma/project/migrations/20250820182517_b/migration.sql +2 -0
- package/prisma/project/migrations/20250821172324_a/migration.sql +2 -0
- package/prisma/project/migrations/20250822081339_a/migration.sql +219 -0
- package/prisma/project/migrations/20250822083742_b/migration.sql +1 -0
- package/prisma/project/migrations/20250822105134_boom/migration.sql +1 -0
- package/prisma/project/migrations/20250822141028_b/migration.sql +1 -0
- package/prisma/project/migrations/20250822142342_b/migration.sql +16 -0
- package/prisma/project/migrations/20250824072720_a/migration.sql +1 -0
- package/prisma/project/migrations/20250824093656_b/migration.sql +21 -0
- package/prisma/project/migrations/20250825082518_a/migration.sql +1 -0
- package/prisma/project/migrations/20250825085343_b/migration.sql +1 -0
- package/prisma/project/migrations/20250825091312_a/migration.sql +1 -0
- package/prisma/project/migrations/20250903095431_hi/migration.sql +44 -0
- package/prisma/project/migrations/20250903174255_a/migration.sql +24 -0
- package/prisma/project/migrations/20250908095205_hi/migration.sql +18 -0
- package/prisma/project/migrations/20250909155857_hi/migration.sql +15 -0
- package/prisma/project/migrations/migration_lock.toml +3 -0
- package/prisma/project/model.prisma +37 -0
- package/prisma/project/operation.prisma +148 -0
- package/prisma/project/page.prisma +41 -0
- package/prisma/project/secret.prisma +42 -0
- package/prisma/project/service-account.prisma +36 -0
- package/prisma/project/terminal.prisma +90 -0
- package/prisma/project/trigger.prisma +31 -0
- package/prisma/project/unlock-method.prisma +32 -0
- package/prisma/project/worker.prisma +138 -0
- package/src/artifact/abstractions.ts +13 -13
- package/src/artifact/encryption.ts +31 -15
- package/src/artifact/factory.ts +7 -10
- package/src/artifact/local.ts +33 -50
- package/src/business/api-key.ts +24 -36
- package/src/business/artifact.test.ts +978 -0
- package/src/business/artifact.ts +136 -215
- package/src/business/evaluation.ts +328 -0
- package/src/business/index.ts +5 -1
- package/src/business/instance-lock.test.ts +1060 -0
- package/src/business/instance-lock.ts +387 -77
- package/src/business/instance-state.test.ts +735 -0
- package/src/business/instance-state.ts +604 -217
- package/src/business/operation.test.ts +439 -0
- package/src/business/operation.ts +174 -208
- package/src/business/project-model.ts +258 -0
- package/src/business/project-unlock.ts +172 -112
- package/src/business/project.ts +407 -0
- package/src/business/secret.test.ts +513 -0
- package/src/business/secret.ts +194 -131
- package/src/business/settings.test.ts +695 -0
- package/src/business/settings.ts +855 -0
- package/src/business/terminal-session.ts +90 -0
- package/src/business/unit-extra.test.ts +539 -0
- package/src/business/unit-extra.ts +160 -0
- package/src/business/worker.test.ts +391 -0
- package/src/business/worker.ts +250 -114
- package/src/common/codebase.ts +65 -0
- package/src/common/index.ts +3 -2
- package/src/common/logger.ts +5 -0
- package/src/common/utils.ts +4 -3
- package/src/config.ts +15 -12
- package/src/database/_generated/backend/postgresql/client.ts +72 -0
- package/src/database/_generated/backend/postgresql/commonInputTypes.ts +350 -0
- package/src/database/_generated/backend/postgresql/enums.ts +13 -0
- package/src/database/_generated/backend/postgresql/internal/class.ts +320 -0
- package/src/database/_generated/backend/postgresql/internal/prismaNamespace.ts +1238 -0
- package/src/database/_generated/backend/postgresql/models/Library.ts +1263 -0
- package/src/database/_generated/backend/postgresql/models/Project.ts +2175 -0
- package/src/database/_generated/backend/postgresql/models/ProjectModelStorage.ts +1263 -0
- package/src/database/_generated/backend/postgresql/models/ProjectSpace.ts +1602 -0
- package/src/database/_generated/backend/postgresql/models/PulumiBackend.ts +1263 -0
- package/src/database/_generated/backend/postgresql/models/UserWorkspaseLayout.ts +1065 -0
- package/src/database/_generated/backend/postgresql/models.ts +16 -0
- package/src/database/_generated/backend/postgresql/pjtg.ts +182 -0
- package/src/database/_generated/backend/sqlite/client.ts +72 -0
- package/src/database/_generated/backend/sqlite/commonInputTypes.ts +331 -0
- package/src/database/_generated/backend/sqlite/enums.ts +13 -0
- package/src/database/_generated/backend/sqlite/internal/class.ts +318 -0
- package/src/database/_generated/backend/sqlite/internal/prismaNamespace.ts +1207 -0
- package/src/database/_generated/backend/sqlite/models/Library.ts +1261 -0
- package/src/database/_generated/backend/sqlite/models/Project.ts +2169 -0
- package/src/database/_generated/backend/sqlite/models/ProjectModelStorage.ts +1261 -0
- package/src/database/_generated/backend/sqlite/models/ProjectSpace.ts +1599 -0
- package/src/database/_generated/backend/sqlite/models/PulumiBackend.ts +1261 -0
- package/src/database/_generated/backend/sqlite/models/UserWorkspaseLayout.ts +1063 -0
- package/src/database/_generated/backend/sqlite/models.ts +16 -0
- package/src/database/_generated/backend/sqlite/pjtg.ts +182 -0
- package/src/database/_generated/project/client.ts +204 -0
- package/src/database/_generated/project/commonInputTypes.ts +827 -0
- package/src/database/_generated/project/enums.ts +104 -0
- package/src/database/_generated/project/internal/class.ts +479 -0
- package/src/database/_generated/project/internal/prismaNamespace.ts +2974 -0
- package/src/database/_generated/project/models/ApiKey.ts +1506 -0
- package/src/database/_generated/project/models/Artifact.ts +2051 -0
- package/src/database/_generated/project/models/HubModel.ts +1125 -0
- package/src/database/_generated/project/models/InstanceCustomStatus.ts +1713 -0
- package/src/database/_generated/project/models/InstanceEvaluationState.ts +1312 -0
- package/src/database/_generated/project/models/InstanceLock.ts +1268 -0
- package/src/database/_generated/project/models/InstanceModel.ts +1125 -0
- package/src/database/_generated/project/models/InstanceOperationState.ts +1707 -0
- package/src/database/_generated/project/models/InstanceState.ts +4613 -0
- package/src/database/_generated/project/models/Operation.ts +1647 -0
- package/src/database/_generated/project/models/OperationLog.ts +1455 -0
- package/src/database/_generated/project/models/Page.ts +1838 -0
- package/src/database/_generated/project/models/Secret.ts +1692 -0
- package/src/database/_generated/project/models/ServiceAccount.ts +2165 -0
- package/src/database/_generated/project/models/Terminal.ts +2038 -0
- package/src/database/_generated/project/models/TerminalSession.ts +1454 -0
- package/src/database/_generated/project/models/TerminalSessionLog.ts +1280 -0
- package/src/database/_generated/project/models/Trigger.ts +1430 -0
- package/src/database/_generated/project/models/UnlockMethod.ts +1220 -0
- package/src/database/_generated/project/models/UserCompositeViewport.ts +1280 -0
- package/src/database/_generated/project/models/UserProjectViewport.ts +1059 -0
- package/src/database/_generated/project/models/Worker.ts +1459 -0
- package/src/database/_generated/project/models/WorkerUnitRegistration.ts +1524 -0
- package/src/database/_generated/project/models/WorkerVersion.ts +1974 -0
- package/src/database/_generated/project/models/WorkerVersionLog.ts +1318 -0
- package/src/database/_generated/project/models.ts +35 -0
- package/src/database/_generated/project/pjtg.ts +182 -0
- package/src/database/abstractions.ts +19 -0
- package/src/database/factory.ts +37 -0
- package/src/database/index.ts +6 -0
- package/src/database/local/backend.ts +134 -0
- package/src/database/local/index.ts +3 -0
- package/src/database/local/meta.ts +46 -0
- package/src/database/local/prisma.config.ts +25 -0
- package/src/database/local/project.ts +39 -0
- package/src/database/manager.ts +181 -0
- package/src/database/migrate.ts +35 -0
- package/src/database/prisma.ts +56 -0
- package/src/database/well-known.ts +38 -0
- package/src/index.ts +4 -4
- package/src/library/abstractions.ts +21 -14
- package/src/library/factory.ts +1 -1
- package/src/library/local.ts +86 -38
- package/src/library/package-resolution-worker.ts +1 -1
- package/src/library/worker/evaluator.ts +61 -48
- package/src/library/worker/loader.lite.ts +14 -1
- package/src/library/worker/main.ts +9 -16
- package/src/library/worker/protocol.ts +0 -12
- package/src/lock/manager.ts +12 -7
- package/src/orchestrator/manager.ts +198 -131
- package/src/orchestrator/operation-context.ts +357 -0
- package/src/orchestrator/operation-plan.destroy.test.md +357 -0
- package/src/orchestrator/operation-plan.destroy.test.ts +775 -0
- package/src/orchestrator/operation-plan.fixtures.ts +213 -0
- package/src/orchestrator/operation-plan.md +198 -0
- package/src/orchestrator/operation-plan.refresh.test.md +199 -0
- package/src/orchestrator/operation-plan.refresh.test.ts +367 -0
- package/src/orchestrator/operation-plan.ts +709 -0
- package/src/orchestrator/operation-plan.update.test.md +485 -0
- package/src/orchestrator/operation-plan.update.test.ts +1066 -0
- package/src/orchestrator/operation-workset.ts +235 -583
- package/src/orchestrator/operation.ts +446 -904
- package/src/orchestrator/plan-test-builder.ts +267 -0
- package/src/project-model/abstractions.ts +118 -0
- package/src/project-model/backends/codebase.ts +365 -0
- package/src/project-model/backends/database.ts +440 -0
- package/src/project-model/errors.ts +81 -0
- package/src/project-model/factory.ts +24 -0
- package/src/project-model/index.ts +4 -0
- package/src/project-model/utils.test.ts +544 -0
- package/src/project-model/utils.ts +242 -0
- package/src/pubsub/abstractions.ts +10 -1
- package/src/pubsub/factory.ts +4 -4
- package/src/pubsub/index.ts +1 -0
- package/src/pubsub/manager.ts +49 -25
- package/src/pubsub/memory.ts +31 -0
- package/src/runner/abstractions.ts +38 -26
- package/src/runner/artifact-env.ts +17 -6
- package/src/runner/factory.ts +6 -6
- package/src/runner/force-abort.ts +3 -6
- package/src/runner/local.ts +79 -72
- package/src/runner/pulumi.ts +26 -63
- package/src/services.ts +214 -103
- package/src/shared/models/backend/index.ts +3 -1
- package/src/shared/models/backend/library.ts +12 -4
- package/src/shared/models/backend/project.ts +43 -23
- package/src/shared/models/backend/pulumi.ts +14 -0
- package/src/shared/models/backend/unlock-method.ts +1 -1
- package/src/shared/models/backend/well-known.ts +58 -0
- package/src/shared/models/base.ts +40 -109
- package/src/shared/models/errors.ts +82 -1
- package/src/shared/models/index.ts +3 -2
- package/src/shared/models/prisma.ts +36 -0
- package/src/shared/models/project/api-key.ts +37 -56
- package/src/shared/models/project/artifact.ts +15 -105
- package/src/shared/models/project/custom-status.ts +12 -0
- package/src/shared/models/project/index.ts +9 -9
- package/src/shared/models/project/lock.ts +10 -78
- package/src/shared/models/project/model.ts +32 -0
- package/src/shared/models/project/operation.ts +222 -99
- package/src/shared/models/project/page.ts +37 -48
- package/src/shared/models/project/secret.ts +29 -103
- package/src/shared/models/project/service-account.ts +12 -17
- package/src/shared/models/project/state.ts +100 -390
- package/src/shared/models/project/terminal.ts +75 -89
- package/src/shared/models/project/trigger.ts +13 -49
- package/src/shared/models/project/unlock-method.ts +21 -20
- package/src/shared/models/project/worker.ts +89 -88
- package/src/shared/resolvers/graph-resolver.ts +62 -26
- package/src/shared/resolvers/index.ts +1 -1
- package/src/shared/resolvers/input-hash.ts +24 -14
- package/src/shared/resolvers/input.ts +48 -6
- package/src/shared/resolvers/registry.ts +5 -4
- package/src/shared/resolvers/state.ts +12 -1
- package/src/shared/resolvers/validation.ts +29 -9
- package/src/shared/utils/index.ts +1 -1
- package/src/shared/utils/promise-tracker.ts +30 -3
- package/src/terminal/abstractions.ts +1 -1
- package/src/terminal/docker.ts +3 -3
- package/src/terminal/manager.ts +102 -118
- package/src/test-utils/database.ts +119 -0
- package/src/test-utils/index.ts +2 -0
- package/src/test-utils/services.ts +134 -0
- package/src/unlock/abstractions.ts +31 -0
- package/src/unlock/index.ts +2 -0
- package/src/unlock/memory.ts +27 -0
- package/src/worker/abstractions.ts +7 -4
- package/src/worker/docker.ts +14 -19
- package/src/worker/manager.ts +376 -79
- package/dist/chunk-RCB4AFGD.js.map +0 -1
- package/dist/chunk-WHALQHEZ.js +0 -2017
- package/dist/chunk-WHALQHEZ.js.map +0 -1
- package/src/business/backend-unlock.ts +0 -10
- package/src/common/performance.ts +0 -44
- package/src/hotstate/abstractions.ts +0 -48
- package/src/hotstate/factory.ts +0 -17
- package/src/hotstate/index.ts +0 -3
- package/src/hotstate/manager.ts +0 -192
- package/src/hotstate/memory.ts +0 -100
- package/src/hotstate/validation.ts +0 -101
- package/src/project/abstractions.ts +0 -102
- package/src/project/factory.ts +0 -11
- package/src/project/index.ts +0 -3
- package/src/project/local.ts +0 -469
- package/src/project/manager.ts +0 -574
- package/src/pubsub/local.ts +0 -36
- package/src/pubsub/validation.ts +0 -33
- package/src/shared/models/project/component.ts +0 -45
- package/src/shared/models/project/instance.ts +0 -74
- package/src/state/abstractions.ts +0 -450
- package/src/state/encryption.ts +0 -59
- package/src/state/factory.ts +0 -20
- package/src/state/index.ts +0 -6
- package/src/state/local/backend.ts +0 -299
- package/src/state/local/collection.ts +0 -342
- package/src/state/local/index.ts +0 -2
- package/src/state/manager.ts +0 -819
- package/src/state/repository/index.ts +0 -2
- package/src/state/repository/repository.index.ts +0 -193
- package/src/state/repository/repository.ts +0 -458
- /package/src/{state → database/local}/keyring.ts +0 -0
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import type { UnitPage, UnitTerminal, UnitTrigger } from "@highstate/contract"
|
|
2
|
+
import type { DatabaseManager, ProjectTransaction } from "../database"
|
|
3
|
+
|
|
4
|
+
export class UnitExtraService {
|
|
5
|
+
constructor(private readonly database: DatabaseManager) {}
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Processes unit terminals within an existing transaction.
|
|
9
|
+
*
|
|
10
|
+
* @param tx The database transaction to use.
|
|
11
|
+
* @param stateId The ID of the instance state.
|
|
12
|
+
* @param unitTerminals The unit terminals to process.
|
|
13
|
+
* @returns Array of terminal IDs that are active for this instance.
|
|
14
|
+
*/
|
|
15
|
+
async processUnitTerminals(
|
|
16
|
+
tx: ProjectTransaction,
|
|
17
|
+
stateId: string,
|
|
18
|
+
unitTerminals: UnitTerminal[],
|
|
19
|
+
): Promise<string[]> {
|
|
20
|
+
const terminalIds: string[] = []
|
|
21
|
+
|
|
22
|
+
// upsert terminals
|
|
23
|
+
for (const unit of unitTerminals) {
|
|
24
|
+
const terminal = await tx.terminal.upsert({
|
|
25
|
+
where: { stateId_name: { stateId, name: unit.name } },
|
|
26
|
+
create: {
|
|
27
|
+
stateId,
|
|
28
|
+
name: unit.name,
|
|
29
|
+
meta: unit.meta ?? {},
|
|
30
|
+
spec: unit.spec,
|
|
31
|
+
status: "active",
|
|
32
|
+
},
|
|
33
|
+
update: {
|
|
34
|
+
meta: unit.meta ?? {},
|
|
35
|
+
spec: unit.spec,
|
|
36
|
+
status: "active",
|
|
37
|
+
},
|
|
38
|
+
select: { id: true },
|
|
39
|
+
})
|
|
40
|
+
terminalIds.push(terminal.id)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// mark dangling terminals as unavailable
|
|
44
|
+
const unitNames = unitTerminals.map(u => u.name)
|
|
45
|
+
await tx.terminal.updateMany({
|
|
46
|
+
where: {
|
|
47
|
+
stateId,
|
|
48
|
+
name: { notIn: unitNames },
|
|
49
|
+
status: "active",
|
|
50
|
+
},
|
|
51
|
+
data: { status: "unavailable" },
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
return terminalIds
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Processes unit pages within an existing transaction.
|
|
59
|
+
*
|
|
60
|
+
* @param tx The database transaction to use.
|
|
61
|
+
* @param stateId The ID of the instance state.
|
|
62
|
+
* @param unitPages The unit pages to process.
|
|
63
|
+
* @returns Array of page IDs that exist for this instance.
|
|
64
|
+
*/
|
|
65
|
+
async processUnitPages(
|
|
66
|
+
tx: ProjectTransaction,
|
|
67
|
+
stateId: string,
|
|
68
|
+
unitPages: UnitPage[],
|
|
69
|
+
): Promise<string[]> {
|
|
70
|
+
const pageIds: string[] = []
|
|
71
|
+
|
|
72
|
+
// upsert pages
|
|
73
|
+
for (const unit of unitPages) {
|
|
74
|
+
const page = await tx.page.upsert({
|
|
75
|
+
where: { stateId_name: { stateId, name: unit.name } },
|
|
76
|
+
create: {
|
|
77
|
+
stateId,
|
|
78
|
+
name: unit.name,
|
|
79
|
+
meta: unit.meta ?? {},
|
|
80
|
+
content: unit.content,
|
|
81
|
+
},
|
|
82
|
+
update: {
|
|
83
|
+
meta: unit.meta ?? {},
|
|
84
|
+
content: unit.content,
|
|
85
|
+
},
|
|
86
|
+
select: { id: true },
|
|
87
|
+
})
|
|
88
|
+
pageIds.push(page.id)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// delete dangling pages
|
|
92
|
+
const unitNames = unitPages.map(u => u.name)
|
|
93
|
+
await tx.page.deleteMany({
|
|
94
|
+
where: {
|
|
95
|
+
stateId,
|
|
96
|
+
name: { notIn: unitNames },
|
|
97
|
+
},
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
return pageIds
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Processes unit triggers within an existing transaction.
|
|
105
|
+
*
|
|
106
|
+
* @param tx The database transaction to use.
|
|
107
|
+
* @param stateId The ID of the instance state.
|
|
108
|
+
* @param unitTriggers The unit triggers to process.
|
|
109
|
+
* @returns Array of trigger IDs that exist for this instance.
|
|
110
|
+
*/
|
|
111
|
+
async processUnitTriggers(
|
|
112
|
+
tx: ProjectTransaction,
|
|
113
|
+
stateId: string,
|
|
114
|
+
unitTriggers: UnitTrigger[],
|
|
115
|
+
): Promise<string[]> {
|
|
116
|
+
const triggerIds: string[] = []
|
|
117
|
+
|
|
118
|
+
// upsert triggers
|
|
119
|
+
for (const unit of unitTriggers) {
|
|
120
|
+
const trigger = await tx.trigger.upsert({
|
|
121
|
+
where: { stateId_name: { stateId, name: unit.name } },
|
|
122
|
+
create: {
|
|
123
|
+
stateId,
|
|
124
|
+
name: unit.name,
|
|
125
|
+
meta: unit.meta ?? {},
|
|
126
|
+
spec: unit.spec,
|
|
127
|
+
},
|
|
128
|
+
update: {
|
|
129
|
+
meta: unit.meta ?? {},
|
|
130
|
+
spec: unit.spec,
|
|
131
|
+
},
|
|
132
|
+
select: { id: true },
|
|
133
|
+
})
|
|
134
|
+
triggerIds.push(trigger.id)
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// delete dangling triggers
|
|
138
|
+
const unitNames = unitTriggers.map(u => u.name)
|
|
139
|
+
await tx.trigger.deleteMany({
|
|
140
|
+
where: {
|
|
141
|
+
stateId,
|
|
142
|
+
name: { notIn: unitNames },
|
|
143
|
+
},
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
return triggerIds
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Gets all triggers for a specific instance.
|
|
151
|
+
*
|
|
152
|
+
* @param projectId The project ID.
|
|
153
|
+
* @param stateId The ID of the instance state to get triggers for.
|
|
154
|
+
*/
|
|
155
|
+
async getInstanceTriggers(projectId: string, stateId: string) {
|
|
156
|
+
const database = await this.database.forProject(projectId)
|
|
157
|
+
|
|
158
|
+
return database.trigger.findMany({ where: { stateId } })
|
|
159
|
+
}
|
|
160
|
+
}
|
|
@@ -0,0 +1,391 @@
|
|
|
1
|
+
import type { UnitWorker } from "@highstate/contract"
|
|
2
|
+
import type { Worker, WorkerVersion } from "../database"
|
|
3
|
+
import type { PubSubManager } from "../pubsub"
|
|
4
|
+
import type { WorkerManager } from "../worker"
|
|
5
|
+
import { randomBytes } from "node:crypto"
|
|
6
|
+
import { createId } from "@paralleldrive/cuid2"
|
|
7
|
+
import { describe, vi } from "vitest"
|
|
8
|
+
import { extractDigestFromImage } from "../shared"
|
|
9
|
+
import { test } from "../test-utils"
|
|
10
|
+
import { WorkerService } from "./worker"
|
|
11
|
+
|
|
12
|
+
const workerTest = test.extend<{
|
|
13
|
+
workerService: WorkerService
|
|
14
|
+
|
|
15
|
+
createWorker(): Promise<Worker>
|
|
16
|
+
|
|
17
|
+
createWorkerVersion: (
|
|
18
|
+
worker: Worker,
|
|
19
|
+
overrides?: Pick<Partial<WorkerVersion>, "digest">,
|
|
20
|
+
) => Promise<WorkerVersion>
|
|
21
|
+
|
|
22
|
+
createMockUnitWorker: (overrides?: Partial<UnitWorker>) => UnitWorker
|
|
23
|
+
}>({
|
|
24
|
+
workerService: async ({ database, logger }, use) => {
|
|
25
|
+
const workerManager = vi.mockObject({ syncWorkers: vi.fn() } as unknown as WorkerManager)
|
|
26
|
+
|
|
27
|
+
const workerService = new WorkerService(
|
|
28
|
+
database,
|
|
29
|
+
workerManager,
|
|
30
|
+
vi.mockObject({ subscribe: vi.fn(), publish: vi.fn() } as unknown as PubSubManager),
|
|
31
|
+
logger.child({ service: "WorkerService" }),
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
await use(workerService)
|
|
35
|
+
},
|
|
36
|
+
|
|
37
|
+
createWorker: async ({ projectDatabase }, use) => {
|
|
38
|
+
const createWorker = async () => {
|
|
39
|
+
return await projectDatabase.worker.create({
|
|
40
|
+
data: {
|
|
41
|
+
identity: `ghcr.io/org/${createId()}`,
|
|
42
|
+
serviceAccount: {
|
|
43
|
+
create: {
|
|
44
|
+
meta: {
|
|
45
|
+
title: "Test Worker Service Account",
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
})
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
await use(createWorker)
|
|
54
|
+
},
|
|
55
|
+
|
|
56
|
+
createWorkerVersion: async ({ projectDatabase }, use) => {
|
|
57
|
+
const createWorkerVersion = async (
|
|
58
|
+
worker: Worker,
|
|
59
|
+
overrides: Pick<Partial<WorkerVersion>, "digest"> = {},
|
|
60
|
+
) => {
|
|
61
|
+
return await projectDatabase.workerVersion.create({
|
|
62
|
+
data: {
|
|
63
|
+
worker: { connect: worker },
|
|
64
|
+
digest: createId(),
|
|
65
|
+
meta: {
|
|
66
|
+
title: "Test Worker Version",
|
|
67
|
+
description: "Test worker version for testing purposes",
|
|
68
|
+
},
|
|
69
|
+
apiKey: {
|
|
70
|
+
create: {
|
|
71
|
+
meta: {
|
|
72
|
+
title: "Test Worker API Key",
|
|
73
|
+
},
|
|
74
|
+
serviceAccountId: worker.serviceAccountId,
|
|
75
|
+
token: createId(),
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
...overrides,
|
|
79
|
+
},
|
|
80
|
+
})
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
await use(createWorkerVersion)
|
|
84
|
+
},
|
|
85
|
+
|
|
86
|
+
createMockUnitWorker: async ({}, use) => {
|
|
87
|
+
const createMockUnitWorker = (overrides: Partial<UnitWorker> = {}): UnitWorker => ({
|
|
88
|
+
name: "test-worker",
|
|
89
|
+
image: `ghcr.io/org/${createId()}@sha256:${randomBytes(32).toString("hex")}`,
|
|
90
|
+
params: { key: "value" },
|
|
91
|
+
...overrides,
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
await use(createMockUnitWorker)
|
|
95
|
+
},
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
describe("updateUnitRegistrations", () => {
|
|
99
|
+
workerTest(
|
|
100
|
+
"creates new worker registrations for unit workers",
|
|
101
|
+
async ({
|
|
102
|
+
workerService,
|
|
103
|
+
project,
|
|
104
|
+
projectDatabase,
|
|
105
|
+
createInstanceState,
|
|
106
|
+
createMockUnitWorker,
|
|
107
|
+
expect,
|
|
108
|
+
}) => {
|
|
109
|
+
// arrange
|
|
110
|
+
const unitWorker = createMockUnitWorker()
|
|
111
|
+
const instance = await createInstanceState(project.id)
|
|
112
|
+
|
|
113
|
+
// act
|
|
114
|
+
await projectDatabase.$transaction(async tx => {
|
|
115
|
+
await workerService.updateUnitRegistrations(tx, project.id, instance.id, [unitWorker])
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
// assert
|
|
119
|
+
const registrations = await projectDatabase.workerUnitRegistration.findMany({
|
|
120
|
+
where: { stateId: instance.id },
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
// check that the registration was created with the correct data
|
|
124
|
+
expect(registrations).toHaveLength(1)
|
|
125
|
+
expect(registrations[0].name).toBe(unitWorker.name)
|
|
126
|
+
expect(registrations[0].params).toEqual(unitWorker.params)
|
|
127
|
+
expect(registrations[0].workerVersionId).toBeDefined()
|
|
128
|
+
expect(registrations[0].stateId).toBe(instance.id)
|
|
129
|
+
},
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
workerTest(
|
|
133
|
+
"updates existing worker registrations with new params",
|
|
134
|
+
async ({
|
|
135
|
+
workerService,
|
|
136
|
+
project,
|
|
137
|
+
projectDatabase,
|
|
138
|
+
createInstanceState,
|
|
139
|
+
createWorker,
|
|
140
|
+
createWorkerVersion,
|
|
141
|
+
createMockUnitWorker,
|
|
142
|
+
expect,
|
|
143
|
+
}) => {
|
|
144
|
+
// arrange
|
|
145
|
+
const unitWorker = createMockUnitWorker({ params: { key: "newValue" } })
|
|
146
|
+
const instance = await createInstanceState(project.id)
|
|
147
|
+
|
|
148
|
+
// create initial worker and version
|
|
149
|
+
const worker = await createWorker()
|
|
150
|
+
const workerVersion = await createWorkerVersion(worker, {
|
|
151
|
+
digest: extractDigestFromImage(unitWorker.image),
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
// create an initial registration
|
|
155
|
+
await projectDatabase.workerUnitRegistration.create({
|
|
156
|
+
data: {
|
|
157
|
+
stateId: instance.id,
|
|
158
|
+
name: unitWorker.name,
|
|
159
|
+
params: { key: "oldValue" },
|
|
160
|
+
workerVersionId: workerVersion.id,
|
|
161
|
+
},
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
// act
|
|
165
|
+
await projectDatabase.$transaction(async tx => {
|
|
166
|
+
await workerService.updateUnitRegistrations(tx, project.id, instance.id, [unitWorker])
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
// assert
|
|
170
|
+
const updatedRegistration = await projectDatabase.workerUnitRegistration.findFirst({
|
|
171
|
+
where: { stateId: instance.id, name: unitWorker.name },
|
|
172
|
+
})
|
|
173
|
+
|
|
174
|
+
expect(updatedRegistration).toBeDefined()
|
|
175
|
+
expect(updatedRegistration?.params).toEqual(unitWorker.params)
|
|
176
|
+
expect(updatedRegistration?.workerVersionId).toBe(workerVersion.id)
|
|
177
|
+
},
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
workerTest(
|
|
181
|
+
"creates new worker version while keeping existing one when both are in use",
|
|
182
|
+
async ({
|
|
183
|
+
workerService,
|
|
184
|
+
project,
|
|
185
|
+
projectDatabase,
|
|
186
|
+
createInstanceState,
|
|
187
|
+
createWorker,
|
|
188
|
+
createWorkerVersion,
|
|
189
|
+
createMockUnitWorker,
|
|
190
|
+
expect,
|
|
191
|
+
}) => {
|
|
192
|
+
// arrange
|
|
193
|
+
const oldDigest = randomBytes(32).toString("hex")
|
|
194
|
+
const newDigest = randomBytes(32).toString("hex")
|
|
195
|
+
|
|
196
|
+
const worker = await createWorker()
|
|
197
|
+
const oldWorkerVersion = await createWorkerVersion(worker, { digest: oldDigest })
|
|
198
|
+
|
|
199
|
+
// create two instances, both using the same worker version initially
|
|
200
|
+
const instance1 = await createInstanceState(project.id)
|
|
201
|
+
const instance2 = await createInstanceState(project.id)
|
|
202
|
+
|
|
203
|
+
// create registrations for both instances using the old version
|
|
204
|
+
await projectDatabase.workerUnitRegistration.create({
|
|
205
|
+
data: {
|
|
206
|
+
stateId: instance1.id,
|
|
207
|
+
name: "test-worker",
|
|
208
|
+
params: { key: "value1" },
|
|
209
|
+
workerVersionId: oldWorkerVersion.id,
|
|
210
|
+
},
|
|
211
|
+
})
|
|
212
|
+
|
|
213
|
+
await projectDatabase.workerUnitRegistration.create({
|
|
214
|
+
data: {
|
|
215
|
+
stateId: instance2.id,
|
|
216
|
+
name: "test-worker",
|
|
217
|
+
params: { key: "value2" },
|
|
218
|
+
workerVersionId: oldWorkerVersion.id,
|
|
219
|
+
},
|
|
220
|
+
})
|
|
221
|
+
|
|
222
|
+
// act - update only instance1 to use new worker version
|
|
223
|
+
const newUnitWorker = createMockUnitWorker({
|
|
224
|
+
image: `${worker.identity}@sha256:${newDigest}`,
|
|
225
|
+
params: { key: "newValue" },
|
|
226
|
+
})
|
|
227
|
+
|
|
228
|
+
await projectDatabase.$transaction(async tx => {
|
|
229
|
+
await workerService.updateUnitRegistrations(tx, project.id, instance1.id, [newUnitWorker])
|
|
230
|
+
})
|
|
231
|
+
|
|
232
|
+
// assert
|
|
233
|
+
const instance1Registration = await projectDatabase.workerUnitRegistration.findFirst({
|
|
234
|
+
where: { stateId: instance1.id, name: "test-worker" },
|
|
235
|
+
include: { workerVersion: true },
|
|
236
|
+
})
|
|
237
|
+
|
|
238
|
+
const instance2Registration = await projectDatabase.workerUnitRegistration.findFirst({
|
|
239
|
+
where: { stateId: instance2.id, name: "test-worker" },
|
|
240
|
+
include: { workerVersion: true },
|
|
241
|
+
})
|
|
242
|
+
|
|
243
|
+
// verify instance1 now uses new version
|
|
244
|
+
expect(instance1Registration?.workerVersion.digest).toBe(newDigest)
|
|
245
|
+
expect(instance1Registration?.params).toEqual({ key: "newValue" })
|
|
246
|
+
|
|
247
|
+
// verify instance2 still uses old version
|
|
248
|
+
expect(instance2Registration?.workerVersion.digest).toBe(oldDigest)
|
|
249
|
+
expect(instance2Registration?.params).toEqual({ key: "value2" })
|
|
250
|
+
|
|
251
|
+
// verify both worker versions still exist (no cleanup since both are in use)
|
|
252
|
+
const allVersions = await projectDatabase.workerVersion.findMany({
|
|
253
|
+
where: { workerId: worker.id },
|
|
254
|
+
})
|
|
255
|
+
expect(allVersions).toHaveLength(2)
|
|
256
|
+
expect(allVersions.map(v => v.digest).sort()).toEqual([oldDigest, newDigest].sort())
|
|
257
|
+
},
|
|
258
|
+
)
|
|
259
|
+
|
|
260
|
+
workerTest(
|
|
261
|
+
"creates new worker version and deletes old unused one",
|
|
262
|
+
async ({
|
|
263
|
+
workerService,
|
|
264
|
+
project,
|
|
265
|
+
projectDatabase,
|
|
266
|
+
createInstanceState,
|
|
267
|
+
createWorker,
|
|
268
|
+
createWorkerVersion,
|
|
269
|
+
createMockUnitWorker,
|
|
270
|
+
expect,
|
|
271
|
+
}) => {
|
|
272
|
+
// arrange
|
|
273
|
+
const oldDigest = randomBytes(32).toString("hex")
|
|
274
|
+
const newDigest = randomBytes(32).toString("hex")
|
|
275
|
+
|
|
276
|
+
const worker = await createWorker()
|
|
277
|
+
const oldWorkerVersion = await createWorkerVersion(worker, { digest: oldDigest })
|
|
278
|
+
|
|
279
|
+
const instance = await createInstanceState(project.id)
|
|
280
|
+
|
|
281
|
+
// create registration using old version
|
|
282
|
+
await projectDatabase.workerUnitRegistration.create({
|
|
283
|
+
data: {
|
|
284
|
+
stateId: instance.id,
|
|
285
|
+
name: "test-worker",
|
|
286
|
+
params: { key: "oldValue" },
|
|
287
|
+
workerVersionId: oldWorkerVersion.id,
|
|
288
|
+
},
|
|
289
|
+
})
|
|
290
|
+
|
|
291
|
+
// act - update to use new worker version
|
|
292
|
+
const newUnitWorker = createMockUnitWorker({
|
|
293
|
+
image: `${worker.identity}@sha256:${newDigest}`,
|
|
294
|
+
params: { key: "newValue" },
|
|
295
|
+
})
|
|
296
|
+
|
|
297
|
+
await projectDatabase.$transaction(async tx => {
|
|
298
|
+
await workerService.updateUnitRegistrations(tx, project.id, instance.id, [newUnitWorker])
|
|
299
|
+
})
|
|
300
|
+
|
|
301
|
+
// assert
|
|
302
|
+
const registration = await projectDatabase.workerUnitRegistration.findFirst({
|
|
303
|
+
where: { stateId: instance.id, name: "test-worker" },
|
|
304
|
+
include: { workerVersion: true },
|
|
305
|
+
})
|
|
306
|
+
|
|
307
|
+
// verify registration now uses new version
|
|
308
|
+
expect(registration?.workerVersion.digest).toBe(newDigest)
|
|
309
|
+
expect(registration?.params).toEqual({ key: "newValue" })
|
|
310
|
+
|
|
311
|
+
// verify old version was cleaned up
|
|
312
|
+
const oldVersion = await projectDatabase.workerVersion.findFirst({
|
|
313
|
+
where: { digest: oldDigest, workerId: worker.id },
|
|
314
|
+
})
|
|
315
|
+
expect(oldVersion).toBeNull()
|
|
316
|
+
|
|
317
|
+
// verify new version exists
|
|
318
|
+
const newVersion = await projectDatabase.workerVersion.findFirst({
|
|
319
|
+
where: { digest: newDigest, workerId: worker.id },
|
|
320
|
+
})
|
|
321
|
+
expect(newVersion).toBeDefined()
|
|
322
|
+
},
|
|
323
|
+
)
|
|
324
|
+
|
|
325
|
+
workerTest(
|
|
326
|
+
"removes all registrations and cleans up unused worker versions when unit workers list is empty",
|
|
327
|
+
async ({
|
|
328
|
+
workerService,
|
|
329
|
+
project,
|
|
330
|
+
projectDatabase,
|
|
331
|
+
createInstanceState,
|
|
332
|
+
createWorker,
|
|
333
|
+
createWorkerVersion,
|
|
334
|
+
expect,
|
|
335
|
+
}) => {
|
|
336
|
+
// arrange
|
|
337
|
+
const worker1 = await createWorker()
|
|
338
|
+
const worker2 = await createWorker()
|
|
339
|
+
|
|
340
|
+
const digest1 = randomBytes(32).toString("hex")
|
|
341
|
+
const digest2 = randomBytes(32).toString("hex")
|
|
342
|
+
|
|
343
|
+
const version1 = await createWorkerVersion(worker1, { digest: digest1 })
|
|
344
|
+
const version2 = await createWorkerVersion(worker2, { digest: digest2 })
|
|
345
|
+
|
|
346
|
+
const instance = await createInstanceState(project.id)
|
|
347
|
+
|
|
348
|
+
console.log(instance.id, version1.id, version2.id)
|
|
349
|
+
|
|
350
|
+
// create multiple registrations
|
|
351
|
+
await projectDatabase.workerUnitRegistration.createMany({
|
|
352
|
+
data: [
|
|
353
|
+
{
|
|
354
|
+
stateId: instance.id,
|
|
355
|
+
name: "worker1",
|
|
356
|
+
params: { key: "value1" },
|
|
357
|
+
workerVersionId: version1.id,
|
|
358
|
+
},
|
|
359
|
+
{
|
|
360
|
+
stateId: instance.id,
|
|
361
|
+
name: "worker2",
|
|
362
|
+
params: { key: "value2" },
|
|
363
|
+
workerVersionId: version2.id,
|
|
364
|
+
},
|
|
365
|
+
],
|
|
366
|
+
})
|
|
367
|
+
|
|
368
|
+
// act - update with empty workers list
|
|
369
|
+
await projectDatabase.$transaction(async tx => {
|
|
370
|
+
await workerService.updateUnitRegistrations(tx, project.id, instance.id, [])
|
|
371
|
+
})
|
|
372
|
+
|
|
373
|
+
// assert
|
|
374
|
+
const remainingRegistrations = await projectDatabase.workerUnitRegistration.findMany({
|
|
375
|
+
where: { stateId: instance.id },
|
|
376
|
+
})
|
|
377
|
+
expect(remainingRegistrations).toHaveLength(0)
|
|
378
|
+
|
|
379
|
+
// verify unused worker versions were cleaned up
|
|
380
|
+
const remainingVersions = await projectDatabase.workerVersion.findMany({
|
|
381
|
+
where: {
|
|
382
|
+
OR: [
|
|
383
|
+
{ digest: digest1, workerId: worker1.id },
|
|
384
|
+
{ digest: digest2, workerId: worker2.id },
|
|
385
|
+
],
|
|
386
|
+
},
|
|
387
|
+
})
|
|
388
|
+
expect(remainingVersions).toHaveLength(0)
|
|
389
|
+
},
|
|
390
|
+
)
|
|
391
|
+
})
|