@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,138 @@
|
|
|
1
|
+
model Worker {
|
|
2
|
+
/// The CUIDv2 of the worker.
|
|
3
|
+
id String @id @default(cuid(2))
|
|
4
|
+
|
|
5
|
+
/// The ID of the worker.
|
|
6
|
+
///
|
|
7
|
+
/// This is the fully qualified image name without the tag or digest.
|
|
8
|
+
/// The format is `{<registry>/}[<namespace>/]<name>`.
|
|
9
|
+
///
|
|
10
|
+
/// For example: `ghcr.io/highstate/worker` or `docker.io/library/ubuntu`.
|
|
11
|
+
identity String @unique
|
|
12
|
+
|
|
13
|
+
/// The ID of the service account this worker uses.
|
|
14
|
+
serviceAccountId String @unique
|
|
15
|
+
|
|
16
|
+
/// The time this worker first appeared in the system.
|
|
17
|
+
createdAt DateTime @default(now())
|
|
18
|
+
|
|
19
|
+
/// The service account impersonating this worker.
|
|
20
|
+
serviceAccount ServiceAccount @relation(fields: [serviceAccountId], references: [id])
|
|
21
|
+
|
|
22
|
+
/// The versions of this worker.
|
|
23
|
+
versions WorkerVersion[]
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
enum WorkerVersionStatus {
|
|
27
|
+
/// The status is unknown.
|
|
28
|
+
unknown
|
|
29
|
+
|
|
30
|
+
/// The worker is being started by one of the runtimes.
|
|
31
|
+
starting
|
|
32
|
+
|
|
33
|
+
/// The worker is running and serving registrations.
|
|
34
|
+
running
|
|
35
|
+
|
|
36
|
+
/// The worker is being stopping (after was starting/running and was disabled).
|
|
37
|
+
stopping
|
|
38
|
+
|
|
39
|
+
/// The worker is stopped and not serving registrations.
|
|
40
|
+
stopped
|
|
41
|
+
|
|
42
|
+
/// The worker failed to start/crashed more than the allowed number of times.
|
|
43
|
+
error
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
model WorkerVersion {
|
|
47
|
+
/// The CUIDv2 of the worker version.
|
|
48
|
+
id String @id @default(cuid(2))
|
|
49
|
+
|
|
50
|
+
/// The metadata of the worker version managed by the backend.
|
|
51
|
+
///
|
|
52
|
+
/// [CommonObjectMeta]
|
|
53
|
+
meta Json
|
|
54
|
+
|
|
55
|
+
/// The current status of the worker version reported by the runtime.
|
|
56
|
+
status WorkerVersionStatus @default(unknown)
|
|
57
|
+
|
|
58
|
+
/// Whether this worker version is enabled and will be launched when project is unclocked.
|
|
59
|
+
enabled Boolean @default(true)
|
|
60
|
+
|
|
61
|
+
/// The ID of the runtime where this worker version currently runs.
|
|
62
|
+
runtimeId String?
|
|
63
|
+
|
|
64
|
+
/// The ID of the worker this version belongs to.
|
|
65
|
+
workerId String
|
|
66
|
+
|
|
67
|
+
/// The digest of the worker version used to identify it.
|
|
68
|
+
/// The format is raw SHA256 digest without the `sha256:` prefix.
|
|
69
|
+
digest String @unique
|
|
70
|
+
|
|
71
|
+
/// The ID of the API key this worker version uses.
|
|
72
|
+
apiKeyId String @unique
|
|
73
|
+
|
|
74
|
+
/// The time this worker version was created.
|
|
75
|
+
createdAt DateTime @default(now())
|
|
76
|
+
|
|
77
|
+
/// The time this worker version was last updated.
|
|
78
|
+
updatedAt DateTime @updatedAt
|
|
79
|
+
|
|
80
|
+
/// The worker this version belongs to.
|
|
81
|
+
worker Worker @relation(fields: [workerId], references: [id])
|
|
82
|
+
|
|
83
|
+
/// The API key this worker version uses.
|
|
84
|
+
apiKey ApiKey @relation(fields: [apiKeyId], references: [id])
|
|
85
|
+
|
|
86
|
+
/// The unit registrations for this worker version.
|
|
87
|
+
unitRegistrations WorkerUnitRegistration[]
|
|
88
|
+
|
|
89
|
+
/// The logs produced by this worker version.
|
|
90
|
+
logs WorkerVersionLog[]
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
model WorkerUnitRegistration {
|
|
94
|
+
/// The ID of the state of the unit instance requesting the registration.
|
|
95
|
+
stateId String
|
|
96
|
+
|
|
97
|
+
/// The name of the workor within the instance.
|
|
98
|
+
name String
|
|
99
|
+
|
|
100
|
+
/// The parameters of the registration passed by the unit.
|
|
101
|
+
///
|
|
102
|
+
/// [WorkerUnitRegistrationParams]
|
|
103
|
+
params Json
|
|
104
|
+
|
|
105
|
+
/// The ID of the worker version this registration currently uses.
|
|
106
|
+
workerVersionId String
|
|
107
|
+
|
|
108
|
+
/// The time this registration was created.
|
|
109
|
+
createdAt DateTime @default(now())
|
|
110
|
+
|
|
111
|
+
/// The time this registration was last updated.
|
|
112
|
+
updatedAt DateTime @updatedAt
|
|
113
|
+
|
|
114
|
+
/// The unit instance requesting the registration.
|
|
115
|
+
state InstanceState @relation(fields: [stateId], references: [id])
|
|
116
|
+
|
|
117
|
+
/// The worker version this registration currently uses.
|
|
118
|
+
workerVersion WorkerVersion @relation(fields: [workerVersionId], references: [id])
|
|
119
|
+
|
|
120
|
+
@@id([stateId, name]) // the registration is identified by the instance and name
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
model WorkerVersionLog {
|
|
124
|
+
/// The ULID of the worker log. Also used to extract the timestamp.
|
|
125
|
+
id String @id @default(ulid())
|
|
126
|
+
|
|
127
|
+
/// The ID of the worker version that produced this log.
|
|
128
|
+
workerVersionId String
|
|
129
|
+
|
|
130
|
+
/// The log content.
|
|
131
|
+
content String
|
|
132
|
+
|
|
133
|
+
/// Whether this log is a system/runtime message (vs worker output).
|
|
134
|
+
isSystem Boolean @default(false)
|
|
135
|
+
|
|
136
|
+
/// The worker version that produced this log.
|
|
137
|
+
workerVersion WorkerVersion @relation(fields: [workerVersionId], references: [id], onDelete: Cascade)
|
|
138
|
+
}
|
|
@@ -1,46 +1,46 @@
|
|
|
1
1
|
export interface ArtifactBackend {
|
|
2
2
|
/**
|
|
3
|
-
* Stores content
|
|
4
|
-
* If content with the same
|
|
3
|
+
* Stores content in the backend.
|
|
4
|
+
* If content with the same id already exists, does nothing.
|
|
5
5
|
*
|
|
6
6
|
* @param projectId The project ID to which the content belongs.
|
|
7
|
-
* @param
|
|
7
|
+
* @param artifactId The ID of the artifact to store.
|
|
8
8
|
* @param chunkSize The size of each chunk to store. Only the last chunk may be smaller.
|
|
9
9
|
* @param content The async iterable of content chunks.
|
|
10
10
|
*/
|
|
11
11
|
store(
|
|
12
12
|
projectId: string,
|
|
13
|
-
|
|
13
|
+
artifactId: string,
|
|
14
14
|
chunkSize: number,
|
|
15
15
|
content: AsyncIterable<Uint8Array>,
|
|
16
16
|
): Promise<void>
|
|
17
17
|
|
|
18
18
|
/**
|
|
19
|
-
* Retrieves content by
|
|
19
|
+
* Retrieves content by artifact ID.
|
|
20
20
|
*
|
|
21
21
|
* @param projectId The project ID to which the content belongs.
|
|
22
|
-
* @param
|
|
22
|
+
* @param artifactId The ID of the artifact to retrieve.
|
|
23
23
|
* @param chunkSize The size of each chunk to retrieve. Only the last chunk may be smaller.
|
|
24
24
|
*/
|
|
25
25
|
retrieve(
|
|
26
26
|
projectId: string,
|
|
27
|
-
|
|
27
|
+
artifactId: string,
|
|
28
28
|
chunkSize: number,
|
|
29
29
|
): Promise<AsyncIterable<Uint8Array> | null>
|
|
30
30
|
|
|
31
31
|
/**
|
|
32
|
-
* Deletes content by
|
|
32
|
+
* Deletes content by artifact ID.
|
|
33
33
|
*
|
|
34
34
|
* @param projectId The project ID to which the content belongs.
|
|
35
|
-
* @param
|
|
35
|
+
* @param artifactId The ID of the artifact to delete.
|
|
36
36
|
*/
|
|
37
|
-
delete(projectId: string,
|
|
37
|
+
delete(projectId: string, artifactId: string): Promise<void>
|
|
38
38
|
|
|
39
39
|
/**
|
|
40
|
-
* Checks if content exists by
|
|
40
|
+
* Checks if content exists by artifact ID.
|
|
41
41
|
*
|
|
42
42
|
* @param projectId The project ID to which the content belongs.
|
|
43
|
-
* @param
|
|
43
|
+
* @param artifactId The ID of the artifact to check.
|
|
44
44
|
*/
|
|
45
|
-
exists(projectId: string,
|
|
45
|
+
exists(projectId: string, artifactId: string): Promise<boolean>
|
|
46
46
|
}
|
|
@@ -1,45 +1,54 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { DatabaseManager } from "../database"
|
|
2
2
|
import type { ArtifactBackend } from "./abstractions"
|
|
3
|
+
import { xchacha20poly1305 } from "@noble/ciphers/chacha"
|
|
4
|
+
import { managedNonce } from "@noble/ciphers/webcrypto"
|
|
3
5
|
|
|
4
6
|
const nonceSize = 24
|
|
5
7
|
|
|
8
|
+
/**
|
|
9
|
+
* The ArtifactBackend decorator that adds encryption and key obfuscation to the artifact storage.
|
|
10
|
+
*/
|
|
6
11
|
export class EncryptionArtifactBackend implements ArtifactBackend {
|
|
7
12
|
constructor(
|
|
8
13
|
private readonly artifactBackend: ArtifactBackend,
|
|
9
|
-
private readonly
|
|
14
|
+
private readonly database: DatabaseManager,
|
|
10
15
|
) {}
|
|
11
16
|
|
|
12
17
|
async store(
|
|
13
18
|
projectId: string,
|
|
14
|
-
|
|
19
|
+
artifactId: string,
|
|
15
20
|
chunkSize: number,
|
|
16
21
|
content: AsyncIterable<Uint8Array>,
|
|
17
22
|
): Promise<void> {
|
|
18
|
-
const
|
|
19
|
-
const encryptedContent = this.getEncryptedContent(encryptionBackend, content)
|
|
23
|
+
const encryptedContent = this.getEncryptedContent(projectId, content)
|
|
20
24
|
|
|
21
|
-
await this.artifactBackend.store(projectId,
|
|
25
|
+
await this.artifactBackend.store(projectId, artifactId, chunkSize + nonceSize, encryptedContent)
|
|
22
26
|
}
|
|
23
27
|
|
|
24
28
|
private async *getEncryptedContent(
|
|
25
|
-
|
|
29
|
+
projectId: string,
|
|
26
30
|
content: AsyncIterable<Uint8Array>,
|
|
27
31
|
): AsyncIterable<Uint8Array> {
|
|
32
|
+
const masterKey = await this.database.getProjectMasterKey(projectId)
|
|
33
|
+
if (!masterKey) {
|
|
34
|
+
throw new Error(`No master key found for project ${projectId}`)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const xchacha = managedNonce(xchacha20poly1305)(masterKey)
|
|
38
|
+
|
|
28
39
|
for await (const chunk of content) {
|
|
29
|
-
yield
|
|
40
|
+
yield xchacha.encrypt(chunk)
|
|
30
41
|
}
|
|
31
42
|
}
|
|
32
43
|
|
|
33
44
|
async retrieve(
|
|
34
45
|
projectId: string,
|
|
35
|
-
|
|
46
|
+
artifactId: string,
|
|
36
47
|
chunkSize: number,
|
|
37
48
|
): Promise<AsyncIterable<Uint8Array> | null> {
|
|
38
|
-
const encryptionBackend = this.stateManager.getEncryptionBackend(projectId)
|
|
39
|
-
|
|
40
49
|
const encryptedContent = await this.artifactBackend.retrieve(
|
|
41
50
|
projectId,
|
|
42
|
-
|
|
51
|
+
artifactId,
|
|
43
52
|
chunkSize + nonceSize,
|
|
44
53
|
)
|
|
45
54
|
|
|
@@ -47,15 +56,22 @@ export class EncryptionArtifactBackend implements ArtifactBackend {
|
|
|
47
56
|
return null
|
|
48
57
|
}
|
|
49
58
|
|
|
50
|
-
return this.getDecryptedContent(
|
|
59
|
+
return this.getDecryptedContent(projectId, encryptedContent)
|
|
51
60
|
}
|
|
52
61
|
|
|
53
62
|
private async *getDecryptedContent(
|
|
54
|
-
|
|
63
|
+
projectId: string,
|
|
55
64
|
encryptedContent: AsyncIterable<Uint8Array>,
|
|
56
65
|
): AsyncIterable<Uint8Array> {
|
|
66
|
+
const masterKey = await this.database.getProjectMasterKey(projectId)
|
|
67
|
+
if (!masterKey) {
|
|
68
|
+
throw new Error(`No master key found for project ${projectId}`)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const xchacha = managedNonce(xchacha20poly1305)(masterKey)
|
|
72
|
+
|
|
57
73
|
for await (const chunk of encryptedContent) {
|
|
58
|
-
yield
|
|
74
|
+
yield xchacha.decrypt(chunk)
|
|
59
75
|
}
|
|
60
76
|
}
|
|
61
77
|
|
package/src/artifact/factory.ts
CHANGED
|
@@ -1,19 +1,18 @@
|
|
|
1
|
-
import type { ArtifactBackend } from "./abstractions"
|
|
2
1
|
import type { Logger } from "pino"
|
|
3
|
-
import type {
|
|
2
|
+
import type { DatabaseManager } from "../database"
|
|
3
|
+
import type { ArtifactBackend } from "./abstractions"
|
|
4
4
|
import { z } from "zod"
|
|
5
|
-
import { LocalArtifactBackend, localArtifactBackendConfig } from "./local"
|
|
6
5
|
import { EncryptionArtifactBackend } from "./encryption"
|
|
6
|
+
import { LocalArtifactBackend, localArtifactBackendConfig } from "./local"
|
|
7
7
|
|
|
8
8
|
export const artifactBackendConfig = z.object({
|
|
9
9
|
HIGHSTATE_ARTIFACT_BACKEND_TYPE: z.enum(["local"]).default("local"),
|
|
10
|
-
HIGHSTATE_ARTIFACT_ENABLE_ENCRYPTION: z.coerce.boolean().default(true),
|
|
11
10
|
...localArtifactBackendConfig.shape,
|
|
12
11
|
})
|
|
13
12
|
|
|
14
13
|
export async function createArtifactBackend(
|
|
15
14
|
config: z.infer<typeof artifactBackendConfig> & Record<string, unknown>,
|
|
16
|
-
|
|
15
|
+
database: DatabaseManager,
|
|
17
16
|
logger: Logger,
|
|
18
17
|
): Promise<ArtifactBackend> {
|
|
19
18
|
let backend: ArtifactBackend
|
|
@@ -22,14 +21,12 @@ export async function createArtifactBackend(
|
|
|
22
21
|
|
|
23
22
|
switch (config.HIGHSTATE_ARTIFACT_BACKEND_TYPE) {
|
|
24
23
|
case "local": {
|
|
25
|
-
backend = await LocalArtifactBackend.create(config, fileExtension,
|
|
24
|
+
backend = await LocalArtifactBackend.create(config, fileExtension, logger)
|
|
26
25
|
}
|
|
27
26
|
}
|
|
28
27
|
|
|
29
|
-
if (
|
|
30
|
-
backend = new EncryptionArtifactBackend(backend,
|
|
31
|
-
} else {
|
|
32
|
-
logger.warn("artifact encryption is disabled, this is not recommended")
|
|
28
|
+
if (database.isEncryptionEnabled) {
|
|
29
|
+
backend = new EncryptionArtifactBackend(backend, database)
|
|
33
30
|
}
|
|
34
31
|
|
|
35
32
|
return backend
|
package/src/artifact/local.ts
CHANGED
|
@@ -1,61 +1,54 @@
|
|
|
1
1
|
import type { Logger } from "pino"
|
|
2
|
-
import type
|
|
2
|
+
import type z from "zod"
|
|
3
|
+
import type { ArtifactBackend } from "./abstractions"
|
|
3
4
|
import { createReadStream, createWriteStream } from "node:fs"
|
|
4
|
-
import { access, mkdir, readdir,
|
|
5
|
+
import { access, mkdir, readdir, rm, unlink } from "node:fs/promises"
|
|
5
6
|
import { join, resolve } from "node:path"
|
|
6
|
-
import {
|
|
7
|
-
import { type ArtifactBackend } from "./abstractions"
|
|
7
|
+
import { codebaseConfig, createProjectLogger, getCodebaseHighstatePath } from "../common"
|
|
8
8
|
|
|
9
|
-
export const localArtifactBackendConfig =
|
|
10
|
-
HIGHSTATE_ARTIFACT_BACKEND_LOCAL_DIR: z.string().optional(),
|
|
11
|
-
})
|
|
9
|
+
export const localArtifactBackendConfig = codebaseConfig
|
|
12
10
|
|
|
13
11
|
/**
|
|
14
12
|
* A local artifact backend that stores artifacts in the filesystem.
|
|
15
13
|
*
|
|
16
|
-
* The default artifact location is `~/.highstate/artifacts`.
|
|
17
|
-
*
|
|
18
14
|
* File structure:
|
|
19
|
-
* - `{
|
|
15
|
+
* - `{codebase}/.highstate/projects/{projectId}/artifacts/{id}.{extension}`
|
|
20
16
|
*/
|
|
21
17
|
export class LocalArtifactBackend implements ArtifactBackend {
|
|
22
18
|
constructor(
|
|
23
|
-
private readonly
|
|
24
|
-
private readonly
|
|
25
|
-
private readonly stateManager: StateManager,
|
|
19
|
+
private readonly hsCodebasePath: string,
|
|
20
|
+
private readonly extension: string,
|
|
26
21
|
private readonly logger: Logger,
|
|
27
|
-
) {
|
|
28
|
-
this.logger.debug({ msg: "initialized", baseDir: storageDir })
|
|
29
|
-
}
|
|
22
|
+
) {}
|
|
30
23
|
|
|
31
24
|
async store(
|
|
32
25
|
projectId: string,
|
|
33
|
-
|
|
26
|
+
artifactId: string,
|
|
34
27
|
chunkSize: number,
|
|
35
28
|
content: AsyncIterable<Uint8Array>,
|
|
36
29
|
): Promise<void> {
|
|
37
|
-
const
|
|
30
|
+
const logger = createProjectLogger(this.logger, projectId)
|
|
31
|
+
const [baseDir, fileName] = this.getArtifactPath(projectId, artifactId)
|
|
38
32
|
await mkdir(baseDir, { recursive: true })
|
|
39
33
|
|
|
40
34
|
// check if the artifact already exists
|
|
41
35
|
try {
|
|
42
36
|
await access(fileName)
|
|
43
|
-
|
|
44
|
-
|
|
37
|
+
logger.debug({ artifactId }, "artifact already exists")
|
|
45
38
|
return
|
|
46
39
|
} catch {
|
|
47
40
|
// artifact does not exist, continue with storing
|
|
48
41
|
}
|
|
49
42
|
|
|
50
43
|
const file = createWriteStream(fileName, { highWaterMark: chunkSize })
|
|
51
|
-
|
|
44
|
+
logger.debug({ artifactId, fileName }, "opened file for writing")
|
|
52
45
|
|
|
53
46
|
for await (const chunk of content) {
|
|
54
47
|
file.write(chunk)
|
|
55
48
|
}
|
|
56
49
|
|
|
57
50
|
file.end()
|
|
58
|
-
|
|
51
|
+
logger.info({ artifactId, fileName }, "artifact stored")
|
|
59
52
|
}
|
|
60
53
|
|
|
61
54
|
async retrieve(
|
|
@@ -63,41 +56,41 @@ export class LocalArtifactBackend implements ArtifactBackend {
|
|
|
63
56
|
hash: string,
|
|
64
57
|
chunkSize: number,
|
|
65
58
|
): Promise<AsyncIterable<Uint8Array> | null> {
|
|
59
|
+
const logger = createProjectLogger(this.logger, projectId)
|
|
66
60
|
const [, fileName] = this.getArtifactPath(projectId, hash)
|
|
67
61
|
|
|
68
62
|
try {
|
|
69
63
|
return Promise.resolve(createReadStream(fileName, { highWaterMark: chunkSize }))
|
|
70
64
|
} catch (error) {
|
|
71
|
-
|
|
72
|
-
|
|
65
|
+
logger.debug({ hash, error }, "artifact retrieval failed")
|
|
73
66
|
return null
|
|
74
67
|
}
|
|
75
68
|
}
|
|
76
69
|
|
|
77
70
|
async delete(projectId: string, hash: string): Promise<void> {
|
|
71
|
+
const logger = createProjectLogger(this.logger, projectId)
|
|
78
72
|
const [baseDir, fileName] = this.getArtifactPath(projectId, hash)
|
|
79
73
|
|
|
80
74
|
try {
|
|
81
75
|
await unlink(fileName)
|
|
82
|
-
await this.deleteDirectoryIfEmpty(baseDir)
|
|
83
|
-
|
|
84
|
-
this.logger.info({ msg: "artifact deleted", hash, fileName })
|
|
76
|
+
await this.deleteDirectoryIfEmpty(baseDir, logger)
|
|
77
|
+
logger.info({ hash, fileName }, "artifact deleted")
|
|
85
78
|
} catch (error) {
|
|
86
|
-
|
|
79
|
+
logger.error({ hash, fileName, error }, "artifact deletion failed")
|
|
87
80
|
}
|
|
88
81
|
}
|
|
89
82
|
|
|
90
|
-
async deleteDirectoryIfEmpty(dirPath: string): Promise<void> {
|
|
83
|
+
async deleteDirectoryIfEmpty(dirPath: string, logger: Logger): Promise<void> {
|
|
91
84
|
try {
|
|
92
85
|
const files = await readdir(dirPath)
|
|
93
86
|
if (files.length === 0) {
|
|
94
|
-
await
|
|
95
|
-
|
|
87
|
+
await rm(dirPath)
|
|
88
|
+
logger.info({ dirPath }, "deleted empty directory")
|
|
96
89
|
} else {
|
|
97
|
-
|
|
90
|
+
logger.debug({ dirPath, fileCount: files.length }, "directory not empty, skipping deletion")
|
|
98
91
|
}
|
|
99
92
|
} catch (error) {
|
|
100
|
-
|
|
93
|
+
logger.error({ dirPath, error }, "failed to delete directory")
|
|
101
94
|
}
|
|
102
95
|
}
|
|
103
96
|
|
|
@@ -113,30 +106,20 @@ export class LocalArtifactBackend implements ArtifactBackend {
|
|
|
113
106
|
}
|
|
114
107
|
|
|
115
108
|
private getArtifactPath(projectId: string, hash: string): [baseDir: string, fileName: string] {
|
|
116
|
-
const
|
|
117
|
-
const
|
|
118
|
-
const fileName = join(baseDir, `${hash}${this.fileExtension}`)
|
|
109
|
+
const baseDir = resolve(this.hsCodebasePath, "projects", projectId, "artifacts")
|
|
110
|
+
const fileName = join(baseDir, `${hash}${this.extension}`)
|
|
119
111
|
|
|
120
112
|
return [baseDir, fileName]
|
|
121
113
|
}
|
|
122
114
|
|
|
123
115
|
static async create(
|
|
124
|
-
config: z.infer<typeof
|
|
125
|
-
|
|
126
|
-
stateManager: StateManager,
|
|
116
|
+
config: z.infer<typeof codebaseConfig>,
|
|
117
|
+
extension: string,
|
|
127
118
|
logger: Logger,
|
|
128
119
|
): Promise<LocalArtifactBackend> {
|
|
129
|
-
const
|
|
130
|
-
|
|
131
|
-
service: "LocalArtifactBackend",
|
|
132
|
-
})
|
|
133
|
-
|
|
134
|
-
let location = config.HIGHSTATE_ARTIFACT_BACKEND_LOCAL_DIR
|
|
135
|
-
if (!location) {
|
|
136
|
-
location = resolve(process.env.HOME!, ".highstate", "artifacts")
|
|
137
|
-
}
|
|
120
|
+
const serviceLogger = logger.child({ service: "LocalArtifactBackend" })
|
|
121
|
+
const codebasePath = await getCodebaseHighstatePath(config, logger)
|
|
138
122
|
|
|
139
|
-
|
|
140
|
-
return new LocalArtifactBackend(location, fileExtension, stateManager, childLogger)
|
|
123
|
+
return new LocalArtifactBackend(codebasePath, extension, serviceLogger)
|
|
141
124
|
}
|
|
142
125
|
}
|
package/src/business/api-key.ts
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type {
|
|
1
|
+
import type { Logger } from "pino"
|
|
2
|
+
import type { ApiKey, DatabaseManager } from "../database"
|
|
3
3
|
import { randomBytes } from "node:crypto"
|
|
4
|
-
import {
|
|
4
|
+
import { createProjectLogger } from "../common"
|
|
5
|
+
import { AccessError } from "../shared"
|
|
5
6
|
|
|
6
7
|
export class ApiKeyService {
|
|
7
8
|
constructor(
|
|
8
|
-
private readonly
|
|
9
|
-
private readonly
|
|
9
|
+
private readonly database: DatabaseManager,
|
|
10
|
+
private readonly logger: Logger,
|
|
10
11
|
) {}
|
|
11
12
|
|
|
12
13
|
/**
|
|
@@ -15,35 +16,20 @@ export class ApiKeyService {
|
|
|
15
16
|
* @param projectId The ID of the project containing the API key.
|
|
16
17
|
* @param apiKeyId The ID of the API key to regenerate.
|
|
17
18
|
*/
|
|
18
|
-
async regenerateToken(projectId: string, apiKeyId: string): Promise<
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
await this.stateManager
|
|
29
|
-
.getApiKeyTokenIndexRepository(projectId)
|
|
30
|
-
.indexRepository.delete(apiKey.token, batch)
|
|
31
|
-
|
|
32
|
-
// generate a new token
|
|
33
|
-
apiKey.token = randomBytes(32).toString("hex")
|
|
34
|
-
|
|
35
|
-
// update the API key with the new token and update the index
|
|
36
|
-
await Promise.all([
|
|
37
|
-
this.stateManager.getApiKeyRepository(projectId).putItem(apiKey, batch),
|
|
38
|
-
this.stateManager
|
|
39
|
-
.getApiKeyTokenIndexRepository(projectId)
|
|
40
|
-
.indexRepository.put(apiKey.token, apiKey.id, batch),
|
|
41
|
-
])
|
|
19
|
+
async regenerateToken(projectId: string, apiKeyId: string): Promise<ApiKey> {
|
|
20
|
+
const logger = createProjectLogger(this.logger, projectId)
|
|
21
|
+
const database = await this.database.forProject(projectId)
|
|
22
|
+
|
|
23
|
+
const apiKey = await database.apiKey.update({
|
|
24
|
+
where: { id: apiKeyId },
|
|
25
|
+
data: {
|
|
26
|
+
token: randomBytes(32).toString("hex"),
|
|
27
|
+
},
|
|
28
|
+
})
|
|
42
29
|
|
|
43
|
-
|
|
30
|
+
logger.info(`regenerated API key token with ID "%s",`, apiKeyId)
|
|
44
31
|
|
|
45
|
-
|
|
46
|
-
})
|
|
32
|
+
return apiKey
|
|
47
33
|
}
|
|
48
34
|
|
|
49
35
|
/**
|
|
@@ -54,10 +40,12 @@ export class ApiKeyService {
|
|
|
54
40
|
* @returns The ProjectApiKey object if found.
|
|
55
41
|
* @throws AccessError if the token is not valid for the project.
|
|
56
42
|
*/
|
|
57
|
-
async getApiKeyByToken(projectId: string, token: string): Promise<
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
|
|
43
|
+
async getApiKeyByToken(projectId: string, token: string): Promise<ApiKey> {
|
|
44
|
+
const database = await this.database.forProject(projectId)
|
|
45
|
+
const apiKey = await database.apiKey.findUnique({ where: { token } })
|
|
46
|
+
|
|
47
|
+
if (!apiKey) {
|
|
48
|
+
throw new AccessError(`API key token is not valid for project "${projectId}"`)
|
|
61
49
|
}
|
|
62
50
|
|
|
63
51
|
return apiKey
|