@highstate/backend 0.9.18 → 0.9.20
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-OU5OQBLB.js → chunk-I7BWSAN6.js} +3 -28
- package/dist/{chunk-OU5OQBLB.js.map → chunk-I7BWSAN6.js.map} +1 -1
- package/dist/chunk-RC6Q3XQQ.js +1547 -0
- package/dist/chunk-RC6Q3XQQ.js.map +1 -0
- package/dist/chunk-VB4YL327.js +139 -0
- 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 +2 -1
- package/dist/index.js +7590 -7289
- package/dist/index.js.map +1 -1
- package/dist/library/package-resolution-worker.js +1 -1
- package/dist/library/package-resolution-worker.js.map +1 -1
- package/dist/library/worker/main.js +35 -29
- package/dist/library/worker/main.js.map +1 -1
- package/dist/shared/index.js +2 -2
- package/package.json +18 -9
- 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 +32 -0
- package/prisma/project/artifact.prisma +52 -0
- package/prisma/project/custom-status.prisma +46 -0
- package/prisma/project/evaluation.prisma +45 -0
- package/prisma/project/instance.prisma +157 -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 +49 -0
- package/prisma/project/secret.prisma +54 -0
- package/prisma/project/service-account.prisma +42 -0
- package/prisma/project/terminal.prisma +107 -0
- package/prisma/project/trigger.prisma +37 -0
- package/prisma/project/unlock-method.prisma +46 -0
- package/prisma/project/worker.prisma +169 -0
- package/src/artifact/abstractions.ts +13 -13
- package/src/artifact/encryption.ts +30 -54
- package/src/artifact/factory.ts +6 -9
- package/src/artifact/local.ts +33 -46
- package/src/business/api-key.ts +24 -36
- package/src/business/artifact.test.ts +978 -0
- package/src/business/artifact.ts +136 -216
- package/src/business/evaluation.ts +328 -0
- package/src/business/index.ts +5 -2
- package/src/business/instance-lock.test.ts +1060 -0
- package/src/business/instance-lock.ts +387 -78
- package/src/business/instance-state.test.ts +735 -0
- package/src/business/instance-state.ts +582 -337
- 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 +168 -126
- package/src/business/project.ts +287 -179
- package/src/business/secret.test.ts +469 -130
- package/src/business/secret.ts +177 -217
- 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 +356 -579
- package/src/business/worker.ts +238 -339
- package/src/common/codebase.ts +65 -0
- package/src/common/index.ts +3 -5
- package/src/common/logger.ts +5 -0
- package/src/common/utils.ts +4 -3
- package/src/config.ts +10 -11
- 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 +3 -5
- package/src/library/factory.ts +1 -1
- package/src/library/local.ts +81 -26
- package/src/library/package-resolution-worker.ts +1 -1
- package/src/library/worker/evaluator.ts +40 -23
- package/src/library/worker/loader.lite.ts +1 -1
- package/src/library/worker/main.ts +3 -10
- package/src/library/worker/protocol.ts +0 -1
- package/src/lock/index.ts +0 -1
- package/src/lock/manager.ts +0 -10
- package/src/orchestrator/manager.ts +190 -104
- 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 +233 -578
- package/src/orchestrator/operation.ts +440 -948
- 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 +29 -13
- package/src/pubsub/memory.ts +31 -0
- package/src/runner/abstractions.ts +40 -41
- package/src/runner/artifact-env.ts +19 -8
- package/src/runner/factory.ts +6 -6
- package/src/runner/force-abort.ts +3 -6
- package/src/runner/local.ts +74 -67
- package/src/runner/pulumi.ts +23 -63
- package/src/services.ts +181 -123
- package/src/shared/models/backend/index.ts +3 -1
- package/src/shared/models/backend/library.ts +9 -1
- package/src/shared/models/backend/project.ts +43 -42
- 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 -26
- 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 -59
- package/src/shared/models/project/artifact.ts +16 -76
- package/src/shared/models/project/custom-status.ts +12 -0
- package/src/shared/models/project/index.ts +8 -7
- package/src/shared/models/project/lock.ts +10 -78
- package/src/shared/models/project/model.ts +19 -1
- package/src/shared/models/project/operation.ts +235 -99
- package/src/shared/models/project/page.ts +37 -48
- package/src/shared/models/project/secret.ts +29 -89
- package/src/shared/models/project/service-account.ts +12 -17
- package/src/shared/models/project/state.ts +100 -407
- package/src/shared/models/project/terminal.ts +75 -88
- package/src/shared/models/project/trigger.ts +13 -49
- package/src/shared/models/project/unlock-method.ts +20 -26
- package/src/shared/models/project/worker.ts +89 -90
- package/src/shared/resolvers/graph-resolver.ts +21 -0
- package/src/shared/resolvers/index.ts +1 -1
- package/src/shared/resolvers/input-hash.ts +24 -14
- package/src/shared/resolvers/input.ts +9 -2
- package/src/shared/resolvers/registry.ts +5 -4
- package/src/shared/resolvers/state.ts +12 -1
- package/src/shared/resolvers/validation.ts +7 -3
- package/src/shared/utils/index.ts +1 -2
- 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 +5 -23
- package/src/unlock/memory.ts +9 -14
- package/src/worker/abstractions.ts +7 -4
- package/src/worker/docker.ts +14 -19
- package/src/worker/manager.ts +366 -97
- package/dist/chunk-NAAIDR4U.js +0 -8499
- package/dist/chunk-NAAIDR4U.js.map +0 -1
- package/dist/chunk-Y7DXREVO.js +0 -1745
- package/dist/chunk-Y7DXREVO.js.map +0 -1
- package/dist/magic-string.es-5ABAC4JN.js +0 -1292
- package/dist/magic-string.es-5ABAC4JN.js.map +0 -1
- package/src/business/__traces__/secret/update-instance-secrets/create-and-delete-secrets-simultaneously.md +0 -356
- package/src/business/__traces__/secret/update-instance-secrets/create-new-secrets-for-instance.md +0 -274
- package/src/business/__traces__/secret/update-instance-secrets/delete-existing-secrets.md +0 -223
- package/src/business/__traces__/secret/update-instance-secrets/no-op-when-no-changes.md +0 -147
- package/src/business/__traces__/secret/update-instance-secrets/update-existing-secrets.md +0 -280
- package/src/business/__traces__/worker/update-unit-registrations/add-new-unit-registration-when-other-exists.md +0 -360
- package/src/business/__traces__/worker/update-unit-registrations/add-new-unit-registration.md +0 -215
- package/src/business/__traces__/worker/update-unit-registrations/create-multiple-workers-with-different-identities.md +0 -427
- package/src/business/__traces__/worker/update-unit-registrations/handle-nonexistent-registration-id-gracefully.md +0 -217
- package/src/business/__traces__/worker/update-unit-registrations/no-op-when-no-changes.md +0 -132
- package/src/business/__traces__/worker/update-unit-registrations/recreate-worker-when-image-changes.md +0 -454
- package/src/business/__traces__/worker/update-unit-registrations/recreate-worker-when-image-version-changes.md +0 -426
- package/src/business/__traces__/worker/update-unit-registrations/recreate-worker-with-same-identity-reuses-service-account.md +0 -372
- package/src/business/__traces__/worker/update-unit-registrations/remove-one-of-multiple-unit-registrations.md +0 -383
- package/src/business/__traces__/worker/update-unit-registrations/remove-unit-registration.md +0 -245
- package/src/business/__traces__/worker/update-unit-registrations/update-existing-unit-registration-when-params-change.md +0 -174
- package/src/business/__traces__/worker/update-unit-registrations/update-params-and-image-simultaneously.md +0 -432
- package/src/business/__traces__/worker/update-unit-registrations/worker-with-multiple-registrations-not-deleted-when-one-removed.md +0 -220
- package/src/business/backend-unlock.ts +0 -10
- package/src/common/clock.ts +0 -18
- package/src/common/performance.ts +0 -44
- package/src/common/random.ts +0 -68
- package/src/common/test/index.ts +0 -2
- package/src/common/test/render.ts +0 -98
- package/src/common/test/tracer.ts +0 -359
- 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 -100
- package/src/lock/test.ts +0 -108
- package/src/project/abstractions.ts +0 -78
- package/src/project/evaluation.ts +0 -248
- package/src/project/factory.ts +0 -11
- package/src/project/index.ts +0 -3
- package/src/project/local.ts +0 -417
- package/src/pubsub/local.ts +0 -36
- package/src/pubsub/validation.ts +0 -33
- package/src/shared/utils/args.ts +0 -25
- package/src/state/abstractions.ts +0 -289
- package/src/state/encryption.ts +0 -98
- package/src/state/factory.ts +0 -20
- package/src/state/index.ts +0 -7
- package/src/state/local/backend.ts +0 -106
- package/src/state/local/collection.ts +0 -361
- package/src/state/local/index.ts +0 -2
- package/src/state/manager.ts +0 -890
- package/src/state/memory/backend.ts +0 -70
- package/src/state/memory/collection.ts +0 -270
- package/src/state/memory/index.ts +0 -2
- package/src/state/repository/index.ts +0 -2
- package/src/state/repository/repository.index.ts +0 -193
- package/src/state/repository/repository.ts +0 -507
- package/src/state/test.ts +0 -457
- /package/src/{state → database/local}/keyring.ts +0 -0
|
@@ -0,0 +1,440 @@
|
|
|
1
|
+
import type { Logger } from "pino"
|
|
2
|
+
import type { DatabaseManager } from "../../database"
|
|
3
|
+
import type { ProjectModel, ProjectModelStorageSpec, ProjectOutput } from "../../shared"
|
|
4
|
+
import type { ProjectModelBackend } from "../abstractions"
|
|
5
|
+
import {
|
|
6
|
+
getInstanceId,
|
|
7
|
+
type HubModel,
|
|
8
|
+
type HubModelPatch,
|
|
9
|
+
hubModelSchema,
|
|
10
|
+
type InstanceModel,
|
|
11
|
+
type InstanceModelPatch,
|
|
12
|
+
instanceModelSchema,
|
|
13
|
+
} from "@highstate/contract"
|
|
14
|
+
import {
|
|
15
|
+
ProjectModelHubAlreadyExistsError,
|
|
16
|
+
ProjectModelHubNotFoundError,
|
|
17
|
+
ProjectModelInstanceAlreadyExistsError,
|
|
18
|
+
ProjectModelInstanceNotFoundError,
|
|
19
|
+
ProjectModelOperationError,
|
|
20
|
+
} from "../errors"
|
|
21
|
+
import {
|
|
22
|
+
applyHubPatch,
|
|
23
|
+
applyInstancePatch,
|
|
24
|
+
cleanupHubReferences,
|
|
25
|
+
cleanupInstanceReferences,
|
|
26
|
+
updateInstanceReferences,
|
|
27
|
+
} from "../utils"
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* A project model backend that stores the project models in the project database.
|
|
31
|
+
*
|
|
32
|
+
* Uses the InstanceModel and HubModel tables in the project database.
|
|
33
|
+
*/
|
|
34
|
+
export class DatabaseProjectModelBackend implements ProjectModelBackend {
|
|
35
|
+
constructor(
|
|
36
|
+
private readonly database: DatabaseManager,
|
|
37
|
+
private readonly logger: Logger,
|
|
38
|
+
) {}
|
|
39
|
+
|
|
40
|
+
async getProjectModel(
|
|
41
|
+
project: ProjectOutput,
|
|
42
|
+
spec: ProjectModelStorageSpec,
|
|
43
|
+
): Promise<ProjectModel> {
|
|
44
|
+
assertDatabaseSpec(spec)
|
|
45
|
+
|
|
46
|
+
try {
|
|
47
|
+
const projectDatabase = await this.database.forProject(project.id)
|
|
48
|
+
|
|
49
|
+
const [instanceRecords, hubRecords] = await Promise.all([
|
|
50
|
+
projectDatabase.instanceModel.findMany({
|
|
51
|
+
select: { model: true },
|
|
52
|
+
}),
|
|
53
|
+
projectDatabase.hubModel.findMany({
|
|
54
|
+
select: { model: true },
|
|
55
|
+
}),
|
|
56
|
+
])
|
|
57
|
+
|
|
58
|
+
const instances = instanceRecords.map(record => instanceModelSchema.parse(record.model))
|
|
59
|
+
const hubs = hubRecords.map(record => hubModelSchema.parse(record.model))
|
|
60
|
+
|
|
61
|
+
return { instances, hubs }
|
|
62
|
+
} catch (error) {
|
|
63
|
+
throw new ProjectModelOperationError("get project model", project.id, error)
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async createProjectModel(project: ProjectOutput, spec: ProjectModelStorageSpec): Promise<void> {
|
|
68
|
+
assertDatabaseSpec(spec)
|
|
69
|
+
|
|
70
|
+
try {
|
|
71
|
+
// for database storage, creating an empty project model means ensuring tables exist
|
|
72
|
+
// the tables are created automatically by Prisma migrations, so nothing to do here
|
|
73
|
+
this.logger.info({ projectId: project.id }, "created project model")
|
|
74
|
+
} catch (error) {
|
|
75
|
+
throw new ProjectModelOperationError("create project model", project.id, error)
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async updateInstance(
|
|
80
|
+
project: ProjectOutput,
|
|
81
|
+
spec: ProjectModelStorageSpec,
|
|
82
|
+
instanceId: string,
|
|
83
|
+
patch: InstanceModelPatch,
|
|
84
|
+
): Promise<InstanceModel> {
|
|
85
|
+
assertDatabaseSpec(spec)
|
|
86
|
+
|
|
87
|
+
try {
|
|
88
|
+
const projectDatabase = await this.database.forProject(project.id)
|
|
89
|
+
|
|
90
|
+
const existingRecord = await projectDatabase.instanceModel.findUnique({
|
|
91
|
+
where: { id: instanceId },
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
if (!existingRecord) {
|
|
95
|
+
throw new ProjectModelInstanceNotFoundError(project.id, instanceId)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const instance = instanceModelSchema.parse(existingRecord.model)
|
|
99
|
+
|
|
100
|
+
// apply patch
|
|
101
|
+
applyInstancePatch(instance, patch)
|
|
102
|
+
|
|
103
|
+
// update in database
|
|
104
|
+
await projectDatabase.instanceModel.update({
|
|
105
|
+
where: { id: instanceId },
|
|
106
|
+
data: { model: instance },
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
this.logger.info({ projectId: project.id, instanceId }, "updated instance in project model")
|
|
110
|
+
|
|
111
|
+
return instance
|
|
112
|
+
} catch (error) {
|
|
113
|
+
throw new ProjectModelOperationError("update instance", project.id, error)
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
async deleteInstance(
|
|
118
|
+
project: ProjectOutput,
|
|
119
|
+
spec: ProjectModelStorageSpec,
|
|
120
|
+
instanceId: string,
|
|
121
|
+
): Promise<void> {
|
|
122
|
+
assertDatabaseSpec(spec)
|
|
123
|
+
|
|
124
|
+
try {
|
|
125
|
+
const projectDatabase = await this.database.forProject(project.id)
|
|
126
|
+
|
|
127
|
+
// this is a multi-node operation, so we need a transaction
|
|
128
|
+
await projectDatabase.$transaction(async tx => {
|
|
129
|
+
const existingRecord = await tx.instanceModel.findUnique({
|
|
130
|
+
where: { id: instanceId },
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
if (!existingRecord) {
|
|
134
|
+
throw new ProjectModelInstanceNotFoundError(project.id, instanceId)
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// delete the instance
|
|
138
|
+
await tx.instanceModel.delete({
|
|
139
|
+
where: { id: instanceId },
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
// get all instances and hubs to clean up references
|
|
143
|
+
const [instanceRecords, hubRecords] = await Promise.all([
|
|
144
|
+
tx.instanceModel.findMany({ select: { id: true, model: true } }),
|
|
145
|
+
tx.hubModel.findMany({ select: { id: true, model: true } }),
|
|
146
|
+
])
|
|
147
|
+
|
|
148
|
+
const instances = instanceRecords.map(record => instanceModelSchema.parse(record.model))
|
|
149
|
+
const hubs = hubRecords.map(record => hubModelSchema.parse(record.model))
|
|
150
|
+
|
|
151
|
+
// clean up references
|
|
152
|
+
cleanupInstanceReferences(instances, hubs, instanceId)
|
|
153
|
+
|
|
154
|
+
// update modified instances and hubs back to database
|
|
155
|
+
await Promise.all([
|
|
156
|
+
...instances.map(instance =>
|
|
157
|
+
tx.instanceModel.update({
|
|
158
|
+
where: { id: instance.id },
|
|
159
|
+
data: { model: instance },
|
|
160
|
+
}),
|
|
161
|
+
),
|
|
162
|
+
...hubs.map(hub =>
|
|
163
|
+
tx.hubModel.update({
|
|
164
|
+
where: { id: hub.id },
|
|
165
|
+
data: { model: hub },
|
|
166
|
+
}),
|
|
167
|
+
),
|
|
168
|
+
])
|
|
169
|
+
})
|
|
170
|
+
|
|
171
|
+
this.logger.info({ projectId: project.id, instanceId }, "deleted instance from project model")
|
|
172
|
+
} catch (error) {
|
|
173
|
+
throw new ProjectModelOperationError("delete instance", project.id, error)
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
async renameInstance(
|
|
178
|
+
project: ProjectOutput,
|
|
179
|
+
spec: ProjectModelStorageSpec,
|
|
180
|
+
instanceId: string,
|
|
181
|
+
newName: string,
|
|
182
|
+
): Promise<InstanceModel> {
|
|
183
|
+
assertDatabaseSpec(spec)
|
|
184
|
+
|
|
185
|
+
try {
|
|
186
|
+
const projectDatabase = await this.database.forProject(project.id)
|
|
187
|
+
|
|
188
|
+
// this is a multi-node operation, so we need a transaction
|
|
189
|
+
const renamedInstance = await projectDatabase.$transaction(async tx => {
|
|
190
|
+
const existingRecord = await tx.instanceModel.findUnique({
|
|
191
|
+
where: { id: instanceId },
|
|
192
|
+
})
|
|
193
|
+
|
|
194
|
+
if (!existingRecord) {
|
|
195
|
+
throw new ProjectModelInstanceNotFoundError(project.id, instanceId)
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const instance = instanceModelSchema.parse(existingRecord.model)
|
|
199
|
+
const newInstanceId = getInstanceId(instance.type, newName)
|
|
200
|
+
|
|
201
|
+
// check if new instance ID already exists
|
|
202
|
+
const conflictRecord = await tx.instanceModel.findUnique({
|
|
203
|
+
where: { id: newInstanceId },
|
|
204
|
+
})
|
|
205
|
+
|
|
206
|
+
if (conflictRecord) {
|
|
207
|
+
throw new ProjectModelInstanceAlreadyExistsError(project.id, newInstanceId)
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// update instance
|
|
211
|
+
instance.id = newInstanceId
|
|
212
|
+
instance.name = newName
|
|
213
|
+
|
|
214
|
+
// delete old record and create new one
|
|
215
|
+
await tx.instanceModel.delete({ where: { id: instanceId } })
|
|
216
|
+
await tx.instanceModel.create({
|
|
217
|
+
data: { id: newInstanceId, model: instance },
|
|
218
|
+
})
|
|
219
|
+
|
|
220
|
+
// get all instances and hubs to update references
|
|
221
|
+
const [instanceRecords, hubRecords] = await Promise.all([
|
|
222
|
+
tx.instanceModel.findMany({ select: { id: true, model: true } }),
|
|
223
|
+
tx.hubModel.findMany({ select: { id: true, model: true } }),
|
|
224
|
+
])
|
|
225
|
+
|
|
226
|
+
const instances = instanceRecords.map(record => instanceModelSchema.parse(record.model))
|
|
227
|
+
const hubs = hubRecords.map(record => hubModelSchema.parse(record.model))
|
|
228
|
+
|
|
229
|
+
// update references
|
|
230
|
+
updateInstanceReferences(instances, hubs, instanceId, newInstanceId)
|
|
231
|
+
|
|
232
|
+
// update modified instances and hubs back to database
|
|
233
|
+
await Promise.all([
|
|
234
|
+
...instances.map(inst =>
|
|
235
|
+
tx.instanceModel.update({
|
|
236
|
+
where: { id: inst.id },
|
|
237
|
+
data: { model: inst },
|
|
238
|
+
}),
|
|
239
|
+
),
|
|
240
|
+
...hubs.map(hub =>
|
|
241
|
+
tx.hubModel.update({
|
|
242
|
+
where: { id: hub.id },
|
|
243
|
+
data: { model: hub },
|
|
244
|
+
}),
|
|
245
|
+
),
|
|
246
|
+
])
|
|
247
|
+
|
|
248
|
+
return instance
|
|
249
|
+
})
|
|
250
|
+
|
|
251
|
+
this.logger.info(
|
|
252
|
+
{ projectId: project.id, oldInstanceId: instanceId, newInstanceId: renamedInstance.id },
|
|
253
|
+
"renamed instance in project model",
|
|
254
|
+
)
|
|
255
|
+
|
|
256
|
+
return renamedInstance
|
|
257
|
+
} catch (error) {
|
|
258
|
+
throw new ProjectModelOperationError("rename instance", project.id, error)
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
async updateHub(
|
|
263
|
+
project: ProjectOutput,
|
|
264
|
+
spec: ProjectModelStorageSpec,
|
|
265
|
+
hubId: string,
|
|
266
|
+
patch: HubModelPatch,
|
|
267
|
+
): Promise<HubModel> {
|
|
268
|
+
assertDatabaseSpec(spec)
|
|
269
|
+
|
|
270
|
+
try {
|
|
271
|
+
const projectDatabase = await this.database.forProject(project.id)
|
|
272
|
+
|
|
273
|
+
const existingRecord = await projectDatabase.hubModel.findUnique({
|
|
274
|
+
where: { id: hubId },
|
|
275
|
+
})
|
|
276
|
+
|
|
277
|
+
if (!existingRecord) {
|
|
278
|
+
throw new ProjectModelHubNotFoundError(project.id, hubId)
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
const hub = hubModelSchema.parse(existingRecord.model)
|
|
282
|
+
|
|
283
|
+
// apply patch
|
|
284
|
+
applyHubPatch(hub, patch)
|
|
285
|
+
|
|
286
|
+
// update in database
|
|
287
|
+
await projectDatabase.hubModel.update({
|
|
288
|
+
where: { id: hubId },
|
|
289
|
+
data: { model: hub },
|
|
290
|
+
})
|
|
291
|
+
|
|
292
|
+
this.logger.info({ projectId: project.id, hubId }, "updated hub in project model")
|
|
293
|
+
|
|
294
|
+
return hub
|
|
295
|
+
} catch (error) {
|
|
296
|
+
throw new ProjectModelOperationError("update hub", project.id, error)
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
async deleteHub(
|
|
301
|
+
project: ProjectOutput,
|
|
302
|
+
spec: ProjectModelStorageSpec,
|
|
303
|
+
hubId: string,
|
|
304
|
+
): Promise<void> {
|
|
305
|
+
assertDatabaseSpec(spec)
|
|
306
|
+
|
|
307
|
+
try {
|
|
308
|
+
const projectDatabase = await this.database.forProject(project.id)
|
|
309
|
+
|
|
310
|
+
// this is a multi-node operation, so we need a transaction
|
|
311
|
+
await projectDatabase.$transaction(async tx => {
|
|
312
|
+
const existingRecord = await tx.hubModel.findUnique({
|
|
313
|
+
where: { id: hubId },
|
|
314
|
+
})
|
|
315
|
+
|
|
316
|
+
if (!existingRecord) {
|
|
317
|
+
throw new ProjectModelHubNotFoundError(project.id, hubId)
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// delete the hub
|
|
321
|
+
await tx.hubModel.delete({
|
|
322
|
+
where: { id: hubId },
|
|
323
|
+
})
|
|
324
|
+
|
|
325
|
+
// get all instances and hubs to clean up references
|
|
326
|
+
const [instanceRecords, hubRecords] = await Promise.all([
|
|
327
|
+
tx.instanceModel.findMany({ select: { id: true, model: true } }),
|
|
328
|
+
tx.hubModel.findMany({ select: { id: true, model: true } }),
|
|
329
|
+
])
|
|
330
|
+
|
|
331
|
+
const instances = instanceRecords.map(record => instanceModelSchema.parse(record.model))
|
|
332
|
+
const hubs = hubRecords.map(record => hubModelSchema.parse(record.model))
|
|
333
|
+
|
|
334
|
+
// clean up references
|
|
335
|
+
cleanupHubReferences(instances, hubs, hubId)
|
|
336
|
+
|
|
337
|
+
// update modified instances and hubs back to database
|
|
338
|
+
await Promise.all([
|
|
339
|
+
...instances.map(instance =>
|
|
340
|
+
tx.instanceModel.update({
|
|
341
|
+
where: { id: instance.id },
|
|
342
|
+
data: { model: instance },
|
|
343
|
+
}),
|
|
344
|
+
),
|
|
345
|
+
...hubs.map(hub =>
|
|
346
|
+
tx.hubModel.update({
|
|
347
|
+
where: { id: hub.id },
|
|
348
|
+
data: { model: hub },
|
|
349
|
+
}),
|
|
350
|
+
),
|
|
351
|
+
])
|
|
352
|
+
})
|
|
353
|
+
|
|
354
|
+
this.logger.info({ projectId: project.id, hubId }, "deleted hub from project model")
|
|
355
|
+
} catch (error) {
|
|
356
|
+
throw new ProjectModelOperationError("delete hub", project.id, error)
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
async createNodes(
|
|
361
|
+
project: ProjectOutput,
|
|
362
|
+
spec: ProjectModelStorageSpec,
|
|
363
|
+
instances: InstanceModel[],
|
|
364
|
+
hubs: HubModel[],
|
|
365
|
+
): Promise<void> {
|
|
366
|
+
assertDatabaseSpec(spec)
|
|
367
|
+
|
|
368
|
+
try {
|
|
369
|
+
const projectDatabase = await this.database.forProject(project.id)
|
|
370
|
+
|
|
371
|
+
// this is a multi-node operation, so we need a transaction
|
|
372
|
+
await projectDatabase.$transaction(async tx => {
|
|
373
|
+
// check for conflicts
|
|
374
|
+
const [conflictingInstance, conflictingHub] = await Promise.all([
|
|
375
|
+
instances.length > 0
|
|
376
|
+
? tx.instanceModel.findFirst({
|
|
377
|
+
where: { id: { in: instances.map(i => i.id) } },
|
|
378
|
+
select: { id: true },
|
|
379
|
+
})
|
|
380
|
+
: null,
|
|
381
|
+
hubs.length > 0
|
|
382
|
+
? tx.hubModel.findFirst({
|
|
383
|
+
where: { id: { in: hubs.map(h => h.id) } },
|
|
384
|
+
select: { id: true },
|
|
385
|
+
})
|
|
386
|
+
: null,
|
|
387
|
+
])
|
|
388
|
+
|
|
389
|
+
if (conflictingInstance) {
|
|
390
|
+
throw new ProjectModelInstanceAlreadyExistsError(project.id, conflictingInstance.id)
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
if (conflictingHub) {
|
|
394
|
+
throw new ProjectModelHubAlreadyExistsError(project.id, conflictingHub.id)
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
// create instances and hubs using createMany
|
|
398
|
+
await Promise.all([
|
|
399
|
+
instances.length > 0
|
|
400
|
+
? tx.instanceModel.createMany({
|
|
401
|
+
data: instances.map(instance => ({
|
|
402
|
+
id: instance.id,
|
|
403
|
+
model: instance,
|
|
404
|
+
})),
|
|
405
|
+
})
|
|
406
|
+
: Promise.resolve(),
|
|
407
|
+
hubs.length > 0
|
|
408
|
+
? tx.hubModel.createMany({
|
|
409
|
+
data: hubs.map(hub => ({
|
|
410
|
+
id: hub.id,
|
|
411
|
+
model: hub,
|
|
412
|
+
})),
|
|
413
|
+
})
|
|
414
|
+
: Promise.resolve(),
|
|
415
|
+
])
|
|
416
|
+
})
|
|
417
|
+
|
|
418
|
+
this.logger.info(
|
|
419
|
+
{ projectId: project.id, instanceCount: instances.length, hubCount: hubs.length },
|
|
420
|
+
"created nodes in project model",
|
|
421
|
+
)
|
|
422
|
+
} catch (error) {
|
|
423
|
+
throw new ProjectModelOperationError("create nodes", project.id, error)
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
/**
|
|
429
|
+
* Type guard and casting helper for database storage spec.
|
|
430
|
+
*
|
|
431
|
+
* @param spec The project model storage specification.
|
|
432
|
+
* @throws Error if spec is not database type.
|
|
433
|
+
*/
|
|
434
|
+
function assertDatabaseSpec(
|
|
435
|
+
spec: ProjectModelStorageSpec,
|
|
436
|
+
): asserts spec is Extract<ProjectModelStorageSpec, { type: "database" }> {
|
|
437
|
+
if (spec.type !== "database") {
|
|
438
|
+
throw new Error(`Expected database spec, got ${spec.type}`)
|
|
439
|
+
}
|
|
440
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { BackendError } from "../shared"
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Base error class for all project model related errors.
|
|
5
|
+
*/
|
|
6
|
+
export class ProjectModelError extends BackendError {
|
|
7
|
+
constructor(message: string, cause?: unknown) {
|
|
8
|
+
super(message, cause)
|
|
9
|
+
this.name = "ProjectModelError"
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Error thrown when a project model backend is not found for the given type.
|
|
15
|
+
*/
|
|
16
|
+
export class ProjectModelBackendNotFoundError extends ProjectModelError {
|
|
17
|
+
constructor(backendType: string) {
|
|
18
|
+
super(`Project model backend "${backendType}" not found`)
|
|
19
|
+
this.name = "ProjectModelBackendNotFoundError"
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Error thrown when a project model is not found.
|
|
25
|
+
*/
|
|
26
|
+
export class ProjectModelNotFoundError extends ProjectModelError {
|
|
27
|
+
constructor(projectId: string) {
|
|
28
|
+
super(`Project model not found for project "${projectId}"`)
|
|
29
|
+
this.name = "ProjectModelNotFoundError"
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Error thrown when an instance is not found in the project model.
|
|
35
|
+
*/
|
|
36
|
+
export class ProjectModelInstanceNotFoundError extends ProjectModelError {
|
|
37
|
+
constructor(projectId: string, instanceId: string) {
|
|
38
|
+
super(`Instance "${instanceId}" not found in project "${projectId}"`)
|
|
39
|
+
this.name = "ProjectModelInstanceNotFoundError"
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Error thrown when a hub is not found in the project model.
|
|
45
|
+
*/
|
|
46
|
+
export class ProjectModelHubNotFoundError extends ProjectModelError {
|
|
47
|
+
constructor(projectId: string, hubId: string) {
|
|
48
|
+
super(`Hub "${hubId}" not found in project "${projectId}"`)
|
|
49
|
+
this.name = "ProjectModelHubNotFoundError"
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Error thrown when attempting to create an instance that already exists.
|
|
55
|
+
*/
|
|
56
|
+
export class ProjectModelInstanceAlreadyExistsError extends ProjectModelError {
|
|
57
|
+
constructor(projectId: string, instanceId: string) {
|
|
58
|
+
super(`Instance "${instanceId}" already exists in project "${projectId}"`)
|
|
59
|
+
this.name = "ProjectModelInstanceAlreadyExistsError"
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Error thrown when attempting to create a hub that already exists.
|
|
65
|
+
*/
|
|
66
|
+
export class ProjectModelHubAlreadyExistsError extends ProjectModelError {
|
|
67
|
+
constructor(projectId: string, hubId: string) {
|
|
68
|
+
super(`Hub "${hubId}" already exists in project "${projectId}"`)
|
|
69
|
+
this.name = "ProjectModelHubAlreadyExistsError"
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Error thrown when a project model operation fails.
|
|
75
|
+
*/
|
|
76
|
+
export class ProjectModelOperationError extends ProjectModelError {
|
|
77
|
+
constructor(operation: string, projectId: string, cause?: unknown) {
|
|
78
|
+
super(`Failed to ${operation} for project "${projectId}"`, cause)
|
|
79
|
+
this.name = "ProjectModelOperationError"
|
|
80
|
+
}
|
|
81
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { Logger } from "pino"
|
|
2
|
+
import type { DatabaseManager } from "../database"
|
|
3
|
+
import type { ProjectModelBackend } from "./abstractions"
|
|
4
|
+
import { CodebaseProjectModelBackend } from "./backends/codebase"
|
|
5
|
+
import { DatabaseProjectModelBackend } from "./backends/database"
|
|
6
|
+
|
|
7
|
+
export async function createProjectModelBackends(
|
|
8
|
+
database: DatabaseManager,
|
|
9
|
+
logger: Logger,
|
|
10
|
+
): Promise<Record<string, ProjectModelBackend>> {
|
|
11
|
+
const codebaseBackend = await CodebaseProjectModelBackend.create(
|
|
12
|
+
logger.child({ backend: "CodebaseProjectModelBackend" }),
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
const databaseBackend = new DatabaseProjectModelBackend(
|
|
16
|
+
database,
|
|
17
|
+
logger.child({ backend: "DatabaseProjectModelBackend" }),
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
return {
|
|
21
|
+
codebase: codebaseBackend,
|
|
22
|
+
database: databaseBackend,
|
|
23
|
+
}
|
|
24
|
+
}
|