@highstate/backend 0.19.1 → 0.20.0
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-V2NILDHS.js → chunk-52MY2TCE.js} +347 -19
- package/dist/chunk-52MY2TCE.js.map +1 -0
- package/dist/{chunk-I7BWSAN6.js → chunk-UAWBPTDW.js} +3 -3
- package/dist/{chunk-I7BWSAN6.js.map → chunk-UAWBPTDW.js.map} +1 -1
- package/dist/highstate.manifest.json +4 -4
- package/dist/index.js +4159 -785
- package/dist/index.js.map +1 -1
- package/dist/library/worker/main.js +5 -2
- package/dist/library/worker/main.js.map +1 -1
- package/dist/shared/index.js +2 -2
- package/package.json +7 -7
- package/prisma/backend/_schema/object.prisma +12 -0
- package/prisma/backend/sqlite/migrations/20260222113554_add_object_tracking/migration.sql +7 -0
- package/prisma/project/artifact.prisma +3 -0
- package/prisma/project/entity.prisma +125 -0
- package/prisma/project/instance.prisma +6 -0
- package/prisma/project/migrations/20260301210131_add_entity_tracking/migration.sql +70 -0
- package/prisma/project/migrations/20260302212734_add_resource_hooks_flag/migration.sql +1 -0
- package/prisma/project/operation.prisma +3 -0
- package/src/business/artifact.test.ts +22 -2
- package/src/business/artifact.ts +7 -1
- package/src/business/entity-snapshot.test.ts +684 -0
- package/src/business/entity-snapshot.ts +904 -0
- package/src/business/evaluation.test.ts +56 -0
- package/src/business/evaluation.ts +102 -22
- package/src/business/global-search.test.ts +344 -0
- package/src/business/global-search.ts +902 -0
- package/src/business/index.ts +4 -0
- package/src/business/instance-lock.ts +58 -74
- package/src/business/instance-state.test.ts +15 -1
- package/src/business/instance-state.ts +37 -14
- package/src/business/object-ref-index.test.ts +140 -0
- package/src/business/object-ref-index.ts +193 -0
- package/src/business/operation.test.ts +15 -1
- package/src/business/operation.ts +4 -0
- package/src/business/project-model.ts +154 -13
- package/src/business/project-unlock.ts +25 -2
- package/src/business/project.ts +9 -0
- package/src/business/secret.test.ts +35 -2
- package/src/business/secret.ts +32 -9
- package/src/business/settings.ts +761 -0
- package/src/business/unit-output.test.ts +477 -0
- package/src/business/unit-output.ts +461 -0
- package/src/business/worker.ts +55 -4
- package/src/database/_generated/backend/postgresql/browser.ts +6 -0
- package/src/database/_generated/backend/postgresql/client.ts +6 -0
- package/src/database/_generated/backend/postgresql/internal/class.ts +23 -5
- package/src/database/_generated/backend/postgresql/internal/prismaNamespace.ts +89 -5
- package/src/database/_generated/backend/postgresql/internal/prismaNamespaceBrowser.ts +9 -0
- package/src/database/_generated/backend/postgresql/models/Object.ts +1076 -0
- package/src/database/_generated/backend/postgresql/models.ts +1 -0
- package/src/database/_generated/backend/sqlite/browser.ts +6 -0
- package/src/database/_generated/backend/sqlite/client.ts +6 -0
- package/src/database/_generated/backend/sqlite/internal/class.ts +23 -5
- package/src/database/_generated/backend/sqlite/internal/prismaNamespace.ts +89 -5
- package/src/database/_generated/backend/sqlite/internal/prismaNamespaceBrowser.ts +9 -0
- package/src/database/_generated/backend/sqlite/models/Object.ts +1074 -0
- package/src/database/_generated/backend/sqlite/models.ts +1 -0
- package/src/database/_generated/project/browser.ts +23 -0
- package/src/database/_generated/project/client.ts +23 -0
- package/src/database/_generated/project/commonInputTypes.ts +87 -53
- package/src/database/_generated/project/enums.ts +8 -0
- package/src/database/_generated/project/internal/class.ts +53 -5
- package/src/database/_generated/project/internal/prismaNamespace.ts +367 -13
- package/src/database/_generated/project/internal/prismaNamespaceBrowser.ts +48 -1
- package/src/database/_generated/project/models/Artifact.ts +199 -11
- package/src/database/_generated/project/models/Entity.ts +1274 -0
- package/src/database/_generated/project/models/EntitySnapshot.ts +2389 -0
- package/src/database/_generated/project/models/EntitySnapshotContent.ts +1260 -0
- package/src/database/_generated/project/models/EntitySnapshotReference.ts +1449 -0
- package/src/database/_generated/project/models/InstanceState.ts +361 -1
- package/src/database/_generated/project/models/Operation.ts +148 -3
- package/src/database/_generated/project/models/OperationLog.ts +0 -4
- package/src/database/_generated/project/models.ts +4 -0
- package/src/database/migration.ts +3 -0
- package/src/library/worker/evaluator.ts +7 -1
- package/src/orchestrator/manager.ts +7 -0
- package/src/orchestrator/operation-context.captured-outputs.test.ts +118 -0
- package/src/orchestrator/operation-context.ts +154 -16
- package/src/orchestrator/operation-plan.destroy.test.md +33 -12
- package/src/orchestrator/operation-plan.destroy.test.ts +140 -2
- package/src/orchestrator/operation-plan.fixtures.ts +2 -0
- package/src/orchestrator/operation-plan.md +4 -1
- package/src/orchestrator/operation-plan.ts +286 -92
- package/src/orchestrator/operation-plan.update.test.md +286 -11
- package/src/orchestrator/operation-plan.update.test.ts +656 -5
- package/src/orchestrator/operation-workset.ts +72 -22
- package/src/orchestrator/operation.cancel.test.ts +4 -0
- package/src/orchestrator/operation.composite.test.ts +341 -0
- package/src/orchestrator/operation.destroy.test.ts +4 -0
- package/src/orchestrator/operation.output-validation.failure.test.ts +124 -0
- package/src/orchestrator/operation.preview.test.ts +4 -0
- package/src/orchestrator/operation.refresh.test.ts +4 -0
- package/src/orchestrator/operation.test-utils.ts +52 -13
- package/src/orchestrator/operation.ts +228 -68
- package/src/orchestrator/operation.update.failure.test.ts +4 -0
- package/src/orchestrator/operation.update.skip.test.ts +110 -0
- package/src/orchestrator/operation.update.test.ts +4 -0
- package/src/orchestrator/plan-test-builder.ts +1 -0
- package/src/orchestrator/unit-input-values.test.ts +450 -0
- package/src/orchestrator/unit-input-values.ts +281 -0
- package/src/pubsub/manager.ts +3 -0
- package/src/runner/abstractions.ts +23 -54
- package/src/runner/local.ts +109 -85
- package/src/services.ts +52 -1
- package/src/shared/models/prisma.ts +1 -0
- package/src/shared/models/project/entity.ts +121 -0
- package/src/shared/models/project/index.ts +1 -0
- package/src/shared/models/project/operation.ts +61 -3
- package/src/shared/models/project/state.ts +10 -0
- package/src/shared/models/project/worker.ts +7 -0
- package/src/shared/resolvers/effective-output-type.test.ts +494 -0
- package/src/shared/resolvers/effective-output-type.ts +162 -0
- package/src/shared/resolvers/index.ts +1 -0
- package/src/shared/resolvers/input.ts +59 -9
- package/src/shared/utils/index.ts +1 -0
- package/src/shared/utils/stable-json.ts +41 -0
- package/src/terminal/manager.ts +6 -0
- package/src/worker/manager.ts +97 -1
- package/dist/chunk-V2NILDHS.js.map +0 -1
package/src/runner/local.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/** biome-ignore-all lint/complexity/useLiteralKeys: не ори на меня */
|
|
2
2
|
|
|
3
|
-
import type { ConfigMap,
|
|
3
|
+
import type { ConfigMap, Stack } from "@pulumi/pulumi/automation/index.js"
|
|
4
4
|
import type { Logger } from "pino"
|
|
5
5
|
import type { ArtifactBackend, ArtifactService } from "../artifact"
|
|
6
6
|
import type { LibraryBackend, ResolvedUnitSource } from "../library"
|
|
@@ -15,26 +15,17 @@ import type {
|
|
|
15
15
|
} from "./abstractions"
|
|
16
16
|
import type { DualAbortSignal } from "./force-abort"
|
|
17
17
|
import { EventEmitter, on } from "node:events"
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
18
|
+
import { mkdir, rm } from "node:fs/promises"
|
|
19
|
+
import { cpus } from "node:os"
|
|
20
20
|
import { join, resolve } from "node:path"
|
|
21
|
-
import { crc32 } from "node:zlib"
|
|
22
21
|
import {
|
|
23
22
|
getInstanceId,
|
|
24
23
|
HighstateConfigKey,
|
|
25
24
|
type InstanceId,
|
|
26
|
-
instanceStatusFieldSchema,
|
|
27
|
-
unitArtifactId,
|
|
28
25
|
unitArtifactSchema,
|
|
29
|
-
unitPageSchema,
|
|
30
|
-
unitTerminalSchema,
|
|
31
|
-
unitTriggerSchema,
|
|
32
|
-
unitWorkerSchema,
|
|
33
26
|
} from "@highstate/contract"
|
|
34
|
-
import { encode } from "@msgpack/msgpack"
|
|
35
|
-
import { sha256 } from "@noble/hashes/sha2"
|
|
36
27
|
import { ensureDependencyInstalled } from "nypm"
|
|
37
|
-
import
|
|
28
|
+
import PQueue from "p-queue"
|
|
38
29
|
import { z } from "zod"
|
|
39
30
|
import { runWithRetryOnError } from "../common"
|
|
40
31
|
import {
|
|
@@ -51,20 +42,33 @@ type Events = {
|
|
|
51
42
|
export const localRunnerBackendConfig = z.object({
|
|
52
43
|
HIGHSTATE_RUNNER_BACKEND_LOCAL_PRINT_OUTPUT: z.coerce.boolean().default(true),
|
|
53
44
|
HIGHSTATE_RUNNER_BACKEND_LOCAL_CACHE_DIR: z.string().optional(),
|
|
45
|
+
HIGHSTATE_RUNNER_BACKEND_LOCAL_CONCURRENCY: z.coerce
|
|
46
|
+
.number()
|
|
47
|
+
.int()
|
|
48
|
+
.min(1)
|
|
49
|
+
.default(Math.max(1, Math.floor(cpus().length / 4))),
|
|
54
50
|
})
|
|
55
51
|
|
|
56
52
|
export class LocalRunnerBackend implements RunnerBackend {
|
|
57
53
|
private readonly events = new EventEmitter<Events>()
|
|
54
|
+
private readonly queue: PQueue
|
|
55
|
+
|
|
56
|
+
private static getUnitTempPath(stateId: string): string {
|
|
57
|
+
return join("/tmp", "highstate", stateId)
|
|
58
|
+
}
|
|
58
59
|
|
|
59
60
|
constructor(
|
|
60
61
|
private readonly printOutput: boolean,
|
|
61
62
|
private readonly cacheDir: string,
|
|
63
|
+
concurrency: number,
|
|
62
64
|
private readonly pulumiProjectHost: LocalPulumiHost,
|
|
63
65
|
private readonly libraryBackend: LibraryBackend,
|
|
64
66
|
private readonly arttifactManager: ArtifactService,
|
|
65
67
|
private readonly artifactBackend: ArtifactBackend,
|
|
66
68
|
private readonly logger: Logger,
|
|
67
|
-
) {
|
|
69
|
+
) {
|
|
70
|
+
this.queue = new PQueue({ concurrency })
|
|
71
|
+
}
|
|
68
72
|
|
|
69
73
|
async *watch(options: UnitOptions): AsyncIterable<UnitStateUpdate> {
|
|
70
74
|
const stream = on(
|
|
@@ -84,21 +88,69 @@ export class LocalRunnerBackend implements RunnerBackend {
|
|
|
84
88
|
}
|
|
85
89
|
|
|
86
90
|
update(options: UnitUpdateOptions): Promise<void> {
|
|
87
|
-
void this.
|
|
91
|
+
void this.queue
|
|
92
|
+
.add(async () => {
|
|
93
|
+
options.signal?.throwIfAborted()
|
|
94
|
+
|
|
95
|
+
await this.updateWorker(options, false)
|
|
96
|
+
})
|
|
97
|
+
.catch(error => this.handleQueuedTaskError("update", options, error))
|
|
88
98
|
|
|
89
99
|
return Promise.resolve()
|
|
90
100
|
}
|
|
91
101
|
|
|
92
102
|
preview(options: UnitUpdateOptions): Promise<void> {
|
|
93
|
-
void this.
|
|
103
|
+
void this.queue
|
|
104
|
+
.add(async () => {
|
|
105
|
+
options.signal?.throwIfAborted()
|
|
106
|
+
|
|
107
|
+
await this.updateWorker(options, true)
|
|
108
|
+
})
|
|
109
|
+
.catch(error => this.handleQueuedTaskError("preview", options, error))
|
|
110
|
+
|
|
111
|
+
return Promise.resolve()
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
destroy(options: UnitDestroyOptions): Promise<void> {
|
|
115
|
+
void this.queue
|
|
116
|
+
.add(async () => {
|
|
117
|
+
options.signal?.throwIfAborted()
|
|
118
|
+
|
|
119
|
+
await this.destroyWorker(options)
|
|
120
|
+
})
|
|
121
|
+
.catch(error => this.handleQueuedTaskError("destroy", options, error))
|
|
94
122
|
|
|
95
123
|
return Promise.resolve()
|
|
96
124
|
}
|
|
97
125
|
|
|
126
|
+
refresh(options: UnitOptions): Promise<void> {
|
|
127
|
+
void this.queue
|
|
128
|
+
.add(async () => {
|
|
129
|
+
options.signal?.throwIfAborted()
|
|
130
|
+
|
|
131
|
+
await this.refreshWorker(options)
|
|
132
|
+
})
|
|
133
|
+
.catch(error => this.handleQueuedTaskError("refresh", options, error))
|
|
134
|
+
|
|
135
|
+
return Promise.resolve()
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
private handleQueuedTaskError(operation: string, options: UnitOptions, error: unknown): void {
|
|
139
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
140
|
+
return
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
this.logger.warn({
|
|
144
|
+
msg: "failed to execute unit runner task",
|
|
145
|
+
operation,
|
|
146
|
+
unitId: LocalRunnerBackend.getInstanceId(options),
|
|
147
|
+
error,
|
|
148
|
+
})
|
|
149
|
+
}
|
|
150
|
+
|
|
98
151
|
private async updateWorker(options: UnitUpdateOptions, preview: boolean): Promise<void> {
|
|
99
152
|
const configMap: ConfigMap = {
|
|
100
|
-
[HighstateConfigKey.Config]: { value: JSON.stringify(options.config) },
|
|
101
|
-
[HighstateConfigKey.Secrets]: { value: JSON.stringify(options.secrets), secret: true },
|
|
153
|
+
[HighstateConfigKey.Config]: { value: JSON.stringify(options.config), secret: true },
|
|
102
154
|
}
|
|
103
155
|
|
|
104
156
|
const unitId = LocalRunnerBackend.getInstanceId(options)
|
|
@@ -109,9 +161,13 @@ export class LocalRunnerBackend implements RunnerBackend {
|
|
|
109
161
|
let artifactEnv: ArtifactEnvironment | null = null
|
|
110
162
|
|
|
111
163
|
try {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
164
|
+
unitTempPath = LocalRunnerBackend.getUnitTempPath(options.stateId)
|
|
165
|
+
|
|
166
|
+
// ensure stable temp directory is empty before starting
|
|
167
|
+
await rm(unitTempPath, { recursive: true, force: true })
|
|
168
|
+
await mkdir(unitTempPath, { recursive: true })
|
|
169
|
+
|
|
170
|
+
childLogger.debug({ msg: "prepared unit temp directory", unitTempPath })
|
|
115
171
|
options.signal?.throwIfAborted()
|
|
116
172
|
|
|
117
173
|
artifactEnv = await setupArtifactEnvironment(
|
|
@@ -128,8 +184,8 @@ export class LocalRunnerBackend implements RunnerBackend {
|
|
|
128
184
|
|
|
129
185
|
const envVars: Record<string, string> = {
|
|
130
186
|
HIGHSTATE_CACHE_DIR: this.cacheDir,
|
|
131
|
-
HIGHSTATE_TEMP_PATH: unitTempPath,
|
|
132
187
|
PULUMI_K8S_DELETE_UNREACHABLE: options.deleteUnreachable ? "true" : "",
|
|
188
|
+
HIGHSTATE_PULUMI_COMMAND: preview ? "preview" : "update",
|
|
133
189
|
...options.envVars,
|
|
134
190
|
}
|
|
135
191
|
|
|
@@ -208,7 +264,12 @@ export class LocalRunnerBackend implements RunnerBackend {
|
|
|
208
264
|
})
|
|
209
265
|
|
|
210
266
|
const outputs = await stack.outputs()
|
|
211
|
-
const completionUpdate
|
|
267
|
+
const completionUpdate: TypedUnitStateUpdate<"completion"> = {
|
|
268
|
+
unitId,
|
|
269
|
+
type: "completion",
|
|
270
|
+
operationType: "update",
|
|
271
|
+
rawOutputs: outputs,
|
|
272
|
+
}
|
|
212
273
|
|
|
213
274
|
if (!preview && outputs["$artifacts"]) {
|
|
214
275
|
const artifacts = z
|
|
@@ -224,17 +285,9 @@ export class LocalRunnerBackend implements RunnerBackend {
|
|
|
224
285
|
childLogger,
|
|
225
286
|
)
|
|
226
287
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
return artifact[unitArtifactId]
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
throw new Error(
|
|
234
|
-
`Failed to determine artifact ID for artifact with hash ${artifact.hash}`,
|
|
235
|
-
)
|
|
236
|
-
})
|
|
237
|
-
})
|
|
288
|
+
// zod parse returns cloned objects; write back mutated artifacts
|
|
289
|
+
// so symbol-based unitArtifactId values are visible downstream
|
|
290
|
+
outputs["$artifacts"].value = artifacts
|
|
238
291
|
} else if (preview && outputs["$artifacts"]) {
|
|
239
292
|
childLogger.debug({ msg: "skipping artifact persistence for preview" })
|
|
240
293
|
}
|
|
@@ -268,12 +321,6 @@ export class LocalRunnerBackend implements RunnerBackend {
|
|
|
268
321
|
}
|
|
269
322
|
}
|
|
270
323
|
|
|
271
|
-
async destroy(options: UnitDestroyOptions): Promise<void> {
|
|
272
|
-
void this.destroyWorker(options)
|
|
273
|
-
|
|
274
|
-
return Promise.resolve()
|
|
275
|
-
}
|
|
276
|
-
|
|
277
324
|
async deleteState(options: UnitOptions): Promise<void> {
|
|
278
325
|
await this.pulumiProjectHost.runEmpty(
|
|
279
326
|
{
|
|
@@ -298,7 +345,18 @@ export class LocalRunnerBackend implements RunnerBackend {
|
|
|
298
345
|
private async destroyWorker(options: UnitDestroyOptions): Promise<void> {
|
|
299
346
|
const unitId = LocalRunnerBackend.getInstanceId(options)
|
|
300
347
|
|
|
348
|
+
const childLogger = this.logger.child({ unitId })
|
|
349
|
+
let unitTempPath: string | null = null
|
|
350
|
+
|
|
301
351
|
try {
|
|
352
|
+
unitTempPath = LocalRunnerBackend.getUnitTempPath(options.stateId)
|
|
353
|
+
|
|
354
|
+
// ensure stable temp directory is empty before starting
|
|
355
|
+
await rm(unitTempPath, { recursive: true, force: true })
|
|
356
|
+
await mkdir(unitTempPath, { recursive: true })
|
|
357
|
+
|
|
358
|
+
childLogger.debug({ msg: "prepared unit temp directory", unitTempPath })
|
|
359
|
+
|
|
302
360
|
const resolvedSource = await this.getResolvedUnitSource(options)
|
|
303
361
|
if (!resolvedSource) {
|
|
304
362
|
throw new Error(`Resolved unit source not found for ${options.instanceType}`)
|
|
@@ -315,6 +373,7 @@ export class LocalRunnerBackend implements RunnerBackend {
|
|
|
315
373
|
envVars: {
|
|
316
374
|
HIGHSTATE_CACHE_DIR: this.cacheDir,
|
|
317
375
|
PULUMI_K8S_DELETE_UNREACHABLE: options.deleteUnreachable ? "true" : "",
|
|
376
|
+
HIGHSTATE_PULUMI_COMMAND: "destroy",
|
|
318
377
|
...(options.debug && { TF_LOG: "DEBUG" }),
|
|
319
378
|
},
|
|
320
379
|
},
|
|
@@ -343,6 +402,7 @@ export class LocalRunnerBackend implements RunnerBackend {
|
|
|
343
402
|
color: "always",
|
|
344
403
|
refresh: options.refresh,
|
|
345
404
|
remove: true,
|
|
405
|
+
runProgram: options.hasResourceHooks ?? true,
|
|
346
406
|
signal,
|
|
347
407
|
debug: options.debug,
|
|
348
408
|
|
|
@@ -399,15 +459,11 @@ export class LocalRunnerBackend implements RunnerBackend {
|
|
|
399
459
|
unitId: unitId,
|
|
400
460
|
message: await pulumiErrorToString(error),
|
|
401
461
|
})
|
|
462
|
+
} finally {
|
|
463
|
+
await this.cleanupTempPath(unitTempPath, unitId, "destroy", this.logger)
|
|
402
464
|
}
|
|
403
465
|
}
|
|
404
466
|
|
|
405
|
-
refresh(options: UnitOptions): Promise<void> {
|
|
406
|
-
void this.refreshWorker(options)
|
|
407
|
-
|
|
408
|
-
return Promise.resolve()
|
|
409
|
-
}
|
|
410
|
-
|
|
411
467
|
private async refreshWorker(options: UnitOptions): Promise<void> {
|
|
412
468
|
const unitId = LocalRunnerBackend.getInstanceId(options)
|
|
413
469
|
|
|
@@ -496,51 +552,18 @@ export class LocalRunnerBackend implements RunnerBackend {
|
|
|
496
552
|
}
|
|
497
553
|
}
|
|
498
554
|
|
|
499
|
-
private createCompletionStateUpdate(
|
|
500
|
-
opType: "update" | "destroy" | "refresh",
|
|
501
|
-
unitId: InstanceId,
|
|
502
|
-
outputs: OutputMap,
|
|
503
|
-
): TypedUnitStateUpdate<"completion"> {
|
|
504
|
-
const unitOutputs = omitBy(outputs, (_, key) => key.startsWith("$"))
|
|
505
|
-
|
|
506
|
-
return {
|
|
507
|
-
unitId,
|
|
508
|
-
type: "completion",
|
|
509
|
-
operationType: opType,
|
|
510
|
-
|
|
511
|
-
outputHash: crc32(sha256(encode(unitOutputs))),
|
|
512
|
-
|
|
513
|
-
statusFields: outputs["$statusFields"]
|
|
514
|
-
? z.array(instanceStatusFieldSchema).parse(outputs["$statusFields"].value)
|
|
515
|
-
: null,
|
|
516
|
-
|
|
517
|
-
terminals: outputs["$terminals"]
|
|
518
|
-
? z.array(unitTerminalSchema).parse(outputs["$terminals"].value)
|
|
519
|
-
: null,
|
|
520
|
-
|
|
521
|
-
pages: outputs["$pages"] ? z.array(unitPageSchema).parse(outputs["$pages"].value) : null,
|
|
522
|
-
|
|
523
|
-
triggers: outputs["$triggers"]
|
|
524
|
-
? z.array(unitTriggerSchema).parse(outputs["$triggers"].value)
|
|
525
|
-
: null,
|
|
526
|
-
|
|
527
|
-
workers: outputs["$workers"]
|
|
528
|
-
? z.array(unitWorkerSchema).parse(outputs["$workers"].value)
|
|
529
|
-
: null,
|
|
530
|
-
|
|
531
|
-
secrets: outputs["$secrets"]
|
|
532
|
-
? z.record(z.string(), z.unknown()).parse(outputs["$secrets"].value)
|
|
533
|
-
: null,
|
|
534
|
-
}
|
|
535
|
-
}
|
|
536
|
-
|
|
537
555
|
private async emitCompletionStateUpdate(
|
|
538
556
|
opType: OperationType,
|
|
539
557
|
unitId: InstanceId,
|
|
540
558
|
stack: Stack,
|
|
541
559
|
): Promise<TypedUnitStateUpdate<"completion">> {
|
|
542
560
|
const output = await stack.outputs()
|
|
543
|
-
const update =
|
|
561
|
+
const update: TypedUnitStateUpdate<"completion"> = {
|
|
562
|
+
unitId,
|
|
563
|
+
type: "completion",
|
|
564
|
+
operationType: opType,
|
|
565
|
+
rawOutputs: output,
|
|
566
|
+
}
|
|
544
567
|
|
|
545
568
|
return this.emitStateUpdate(update)
|
|
546
569
|
}
|
|
@@ -649,6 +672,7 @@ export class LocalRunnerBackend implements RunnerBackend {
|
|
|
649
672
|
return new LocalRunnerBackend(
|
|
650
673
|
config.HIGHSTATE_RUNNER_BACKEND_LOCAL_PRINT_OUTPUT,
|
|
651
674
|
cacheDir,
|
|
675
|
+
config.HIGHSTATE_RUNNER_BACKEND_LOCAL_CONCURRENCY,
|
|
652
676
|
pulumiProjectHost,
|
|
653
677
|
libraryBackend,
|
|
654
678
|
artifactManager,
|
package/src/services.ts
CHANGED
|
@@ -5,8 +5,11 @@ import { type ArtifactBackend, ArtifactService, createArtifactBackend } from "./
|
|
|
5
5
|
import {
|
|
6
6
|
ApiKeyService,
|
|
7
7
|
BackendUnlockService,
|
|
8
|
+
EntitySnapshotService,
|
|
9
|
+
GlobalSearchService,
|
|
8
10
|
InstanceLockService,
|
|
9
11
|
InstanceStateService,
|
|
12
|
+
ObjectRefIndexService,
|
|
10
13
|
OperationService,
|
|
11
14
|
ProjectModelService,
|
|
12
15
|
ProjectService,
|
|
@@ -15,6 +18,7 @@ import {
|
|
|
15
18
|
SettingsService,
|
|
16
19
|
TerminalSessionService,
|
|
17
20
|
UnitExtraService,
|
|
21
|
+
UnitOutputService,
|
|
18
22
|
WorkerService,
|
|
19
23
|
} from "./business"
|
|
20
24
|
import { ProjectEvaluationSubsystem } from "./business/evaluation"
|
|
@@ -74,7 +78,9 @@ export type Services = {
|
|
|
74
78
|
|
|
75
79
|
// business services
|
|
76
80
|
readonly backendUnlockService: BackendUnlockService
|
|
81
|
+
readonly globalSearchService: GlobalSearchService
|
|
77
82
|
readonly instanceLockService: InstanceLockService
|
|
83
|
+
readonly objectRefIndexService: ObjectRefIndexService
|
|
78
84
|
readonly projectUnlockService: ProjectUnlockService
|
|
79
85
|
readonly operationService: OperationService
|
|
80
86
|
readonly instanceStateService: InstanceStateService
|
|
@@ -86,6 +92,8 @@ export type Services = {
|
|
|
86
92
|
readonly artifactService: ArtifactService
|
|
87
93
|
readonly settingsService: SettingsService
|
|
88
94
|
readonly unitExtraService: UnitExtraService
|
|
95
|
+
readonly entitySnapshotService: EntitySnapshotService
|
|
96
|
+
readonly unitOutputService: UnitOutputService
|
|
89
97
|
}
|
|
90
98
|
|
|
91
99
|
export interface CreateServicesOptions {
|
|
@@ -134,7 +142,9 @@ export async function createServices({
|
|
|
134
142
|
|
|
135
143
|
// business services
|
|
136
144
|
backendUnlockService,
|
|
145
|
+
globalSearchService,
|
|
137
146
|
instanceLockService,
|
|
147
|
+
objectRefIndexService,
|
|
138
148
|
projectUnlockService,
|
|
139
149
|
operationService,
|
|
140
150
|
secretService,
|
|
@@ -146,6 +156,8 @@ export async function createServices({
|
|
|
146
156
|
projectModelService,
|
|
147
157
|
settingsService,
|
|
148
158
|
unitExtraService,
|
|
159
|
+
entitySnapshotService,
|
|
160
|
+
unitOutputService,
|
|
149
161
|
} = {},
|
|
150
162
|
}: CreateServicesOptions = {}): Promise<Services> {
|
|
151
163
|
runtimeId ??= createId()
|
|
@@ -166,6 +178,11 @@ export async function createServices({
|
|
|
166
178
|
logger,
|
|
167
179
|
)
|
|
168
180
|
|
|
181
|
+
objectRefIndexService ??= new ObjectRefIndexService(
|
|
182
|
+
database,
|
|
183
|
+
logger.child({ service: "ObjectRefIndexService" }),
|
|
184
|
+
)
|
|
185
|
+
|
|
169
186
|
pubsubBackend ??= createPubSubBackend(config, logger)
|
|
170
187
|
pubsubManager ??= new PubSubManager(pubsubBackend, logger)
|
|
171
188
|
|
|
@@ -175,17 +192,24 @@ export async function createServices({
|
|
|
175
192
|
libraryBackend ??= await createLibraryBackend(config, logger)
|
|
176
193
|
|
|
177
194
|
artifactBackend ??= await createArtifactBackend(config, database, logger)
|
|
178
|
-
artifactService ??= new ArtifactService(database, artifactBackend, logger)
|
|
195
|
+
artifactService ??= new ArtifactService(database, artifactBackend, objectRefIndexService, logger)
|
|
179
196
|
|
|
180
197
|
backendUnlockService ??= new BackendUnlockService(
|
|
181
198
|
database,
|
|
182
199
|
logger.child({ service: "BackendUnlockService" }),
|
|
183
200
|
)
|
|
184
201
|
|
|
202
|
+
globalSearchService ??= new GlobalSearchService(
|
|
203
|
+
database,
|
|
204
|
+
projectUnlockBackend,
|
|
205
|
+
logger.child({ service: "GlobalSearchService" }),
|
|
206
|
+
)
|
|
207
|
+
|
|
185
208
|
secretService ??= new SecretService(
|
|
186
209
|
database,
|
|
187
210
|
pubsubManager,
|
|
188
211
|
libraryBackend,
|
|
212
|
+
objectRefIndexService,
|
|
189
213
|
logger.child({ service: "SecretService" }),
|
|
190
214
|
)
|
|
191
215
|
sessionService ??= new TerminalSessionService(database)
|
|
@@ -199,6 +223,11 @@ export async function createServices({
|
|
|
199
223
|
logger,
|
|
200
224
|
)
|
|
201
225
|
|
|
226
|
+
unitOutputService ??= new UnitOutputService(
|
|
227
|
+
libraryBackend,
|
|
228
|
+
logger.child({ service: "UnitOutputService" }),
|
|
229
|
+
)
|
|
230
|
+
|
|
202
231
|
projectModelBackends ??= await createProjectModelBackends(database, logger)
|
|
203
232
|
|
|
204
233
|
instanceLockService ??= new InstanceLockService(
|
|
@@ -211,16 +240,28 @@ export async function createServices({
|
|
|
211
240
|
database,
|
|
212
241
|
pubsubManager,
|
|
213
242
|
projectUnlockBackend,
|
|
243
|
+
objectRefIndexService,
|
|
214
244
|
config,
|
|
215
245
|
logger.child({ service: "StateUnlockService" }),
|
|
216
246
|
)
|
|
217
247
|
|
|
248
|
+
projectUnlockService.registerUnlockTask("sync-object-refs", async projectId => {
|
|
249
|
+
await objectRefIndexService.syncProject(projectId)
|
|
250
|
+
})
|
|
251
|
+
|
|
218
252
|
operationService ??= new OperationService(
|
|
219
253
|
database,
|
|
220
254
|
pubsubManager,
|
|
255
|
+
objectRefIndexService,
|
|
221
256
|
logger.child({ service: "OperationService" }),
|
|
222
257
|
)
|
|
223
258
|
|
|
259
|
+
entitySnapshotService ??= new EntitySnapshotService(
|
|
260
|
+
database,
|
|
261
|
+
objectRefIndexService,
|
|
262
|
+
logger.child({ service: "EntitySnapshotService" }),
|
|
263
|
+
)
|
|
264
|
+
|
|
224
265
|
apiKeyService ??= new ApiKeyService(database, logger.child({ service: "ApiKeyService" }))
|
|
225
266
|
|
|
226
267
|
terminalBackend ??= createTerminalBackend(config, logger)
|
|
@@ -229,6 +270,7 @@ export async function createServices({
|
|
|
229
270
|
database,
|
|
230
271
|
pubsubManager,
|
|
231
272
|
projectUnlockService,
|
|
273
|
+
objectRefIndexService,
|
|
232
274
|
logger,
|
|
233
275
|
)
|
|
234
276
|
|
|
@@ -262,6 +304,7 @@ export async function createServices({
|
|
|
262
304
|
artifactService,
|
|
263
305
|
unitExtraService,
|
|
264
306
|
secretService,
|
|
307
|
+
objectRefIndexService,
|
|
265
308
|
logger.child({ service: "InstanceService" }),
|
|
266
309
|
)
|
|
267
310
|
|
|
@@ -280,6 +323,7 @@ export async function createServices({
|
|
|
280
323
|
projectModelService,
|
|
281
324
|
pubsubManager,
|
|
282
325
|
projectUnlockService,
|
|
326
|
+
objectRefIndexService,
|
|
283
327
|
logger,
|
|
284
328
|
)
|
|
285
329
|
|
|
@@ -291,6 +335,7 @@ export async function createServices({
|
|
|
291
335
|
projectModelBackends,
|
|
292
336
|
libraryBackend,
|
|
293
337
|
pubsubManager,
|
|
338
|
+
objectRefIndexService,
|
|
294
339
|
logger.child({ service: "ProjectService" }),
|
|
295
340
|
)
|
|
296
341
|
|
|
@@ -305,6 +350,8 @@ export async function createServices({
|
|
|
305
350
|
instanceStateService,
|
|
306
351
|
projectModelService,
|
|
307
352
|
unitExtraService,
|
|
353
|
+
entitySnapshotService,
|
|
354
|
+
unitOutputService,
|
|
308
355
|
database,
|
|
309
356
|
logger,
|
|
310
357
|
)
|
|
@@ -344,7 +391,9 @@ export async function createServices({
|
|
|
344
391
|
|
|
345
392
|
// business services
|
|
346
393
|
backendUnlockService,
|
|
394
|
+
globalSearchService,
|
|
347
395
|
instanceLockService,
|
|
396
|
+
objectRefIndexService,
|
|
348
397
|
projectUnlockService,
|
|
349
398
|
operationService,
|
|
350
399
|
instanceStateService,
|
|
@@ -356,6 +405,8 @@ export async function createServices({
|
|
|
356
405
|
projectModelService,
|
|
357
406
|
settingsService,
|
|
358
407
|
unitExtraService,
|
|
408
|
+
entitySnapshotService,
|
|
409
|
+
unitOutputService,
|
|
359
410
|
}
|
|
360
411
|
}
|
|
361
412
|
|
|
@@ -22,6 +22,7 @@ declare global {
|
|
|
22
22
|
type NullableInstanceId = contract.InstanceId | null
|
|
23
23
|
type InstanceIds = contract.InstanceId[]
|
|
24
24
|
type InstanceModel = contract.InstanceModel
|
|
25
|
+
type EntityMeta = Partial<contract.CommonObjectMeta>
|
|
25
26
|
|
|
26
27
|
type InstanceArgs = Record<string, unknown>
|
|
27
28
|
type InstanceResolvedInputs = Record<string, shared.StableInstanceInput[]>
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { commonObjectMetaSchema } from "@highstate/contract"
|
|
2
|
+
import { z } from "zod"
|
|
3
|
+
import { collectionQuerySchema } from "../base"
|
|
4
|
+
|
|
5
|
+
const rawEntityMetaSchema = z
|
|
6
|
+
.object({
|
|
7
|
+
identity: z.string().optional(),
|
|
8
|
+
title: z.string().optional(),
|
|
9
|
+
description: z.string().optional(),
|
|
10
|
+
icon: z.string().optional(),
|
|
11
|
+
iconColor: z.string().optional(),
|
|
12
|
+
})
|
|
13
|
+
.passthrough()
|
|
14
|
+
|
|
15
|
+
export const entityOutputSchema = z.object({
|
|
16
|
+
id: z.string(),
|
|
17
|
+
type: z.string(),
|
|
18
|
+
identity: z.string(),
|
|
19
|
+
meta: commonObjectMetaSchema,
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* The ID of the last known entity snapshot.
|
|
23
|
+
*/
|
|
24
|
+
snapshotId: z.string().optional(),
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* The timestamp of the last known entity snapshot.
|
|
28
|
+
*/
|
|
29
|
+
createdAt: z.date().optional(),
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
export type EntityOutput = z.infer<typeof entityOutputSchema>
|
|
33
|
+
|
|
34
|
+
export const entityQuerySchema = collectionQuerySchema.extend({
|
|
35
|
+
type: z.string().optional(),
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
export type EntityQuery = z.infer<typeof entityQuerySchema>
|
|
39
|
+
|
|
40
|
+
export const entitySnapshotOutputSchema = z.object({
|
|
41
|
+
id: z.string(),
|
|
42
|
+
meta: commonObjectMetaSchema,
|
|
43
|
+
content: z.unknown(),
|
|
44
|
+
operationId: z.string(),
|
|
45
|
+
stateId: z.string(),
|
|
46
|
+
referencedInOutputs: z.string().array(),
|
|
47
|
+
exportedInOutputs: z.string().array(),
|
|
48
|
+
createdAt: z.date(),
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
export type EntitySnapshotOutput = z.infer<typeof entitySnapshotOutputSchema>
|
|
52
|
+
|
|
53
|
+
export const entitySnapshotListItemOutputSchema = z.object({
|
|
54
|
+
id: z.string(),
|
|
55
|
+
meta: commonObjectMetaSchema,
|
|
56
|
+
operationId: z.string(),
|
|
57
|
+
stateId: z.string(),
|
|
58
|
+
createdAt: z.date(),
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
export type EntitySnapshotListItemOutput = z.infer<typeof entitySnapshotListItemOutputSchema>
|
|
62
|
+
|
|
63
|
+
export const entitySnapshotDetailsOutputSchema = z.object({
|
|
64
|
+
entity: z.object({
|
|
65
|
+
id: z.string(),
|
|
66
|
+
type: z.string(),
|
|
67
|
+
identity: z.string(),
|
|
68
|
+
}),
|
|
69
|
+
snapshot: entitySnapshotOutputSchema,
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
export type EntitySnapshotDetailsOutput = z.infer<typeof entitySnapshotDetailsOutputSchema>
|
|
73
|
+
|
|
74
|
+
export const entityDetailsOutputSchema = z.object({
|
|
75
|
+
...entityOutputSchema.shape,
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* The last known entity snapshot.
|
|
79
|
+
*/
|
|
80
|
+
lastSnapshot: entitySnapshotOutputSchema.nullable(),
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
export type EntityDetailsOutput = z.infer<typeof entityDetailsOutputSchema>
|
|
84
|
+
|
|
85
|
+
export const entityReferenceOutputSchema = z.object({
|
|
86
|
+
id: z.string(),
|
|
87
|
+
meta: commonObjectMetaSchema,
|
|
88
|
+
group: z.string(),
|
|
89
|
+
|
|
90
|
+
fromSnapshotId: z.string(),
|
|
91
|
+
fromEntityId: z.string(),
|
|
92
|
+
fromEntityType: z.string(),
|
|
93
|
+
fromEntityIdentity: z.string(),
|
|
94
|
+
fromEntityMeta: commonObjectMetaSchema,
|
|
95
|
+
|
|
96
|
+
toSnapshotId: z.string(),
|
|
97
|
+
toEntityId: z.string(),
|
|
98
|
+
toEntityType: z.string(),
|
|
99
|
+
toEntityIdentity: z.string(),
|
|
100
|
+
toEntityMeta: commonObjectMetaSchema,
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
export type EntityReferenceOutput = z.infer<typeof entityReferenceOutputSchema>
|
|
104
|
+
|
|
105
|
+
export function toCommonEntityMeta(
|
|
106
|
+
meta: unknown | null | undefined,
|
|
107
|
+
): z.infer<typeof commonObjectMetaSchema> {
|
|
108
|
+
const parsedRawMeta = rawEntityMetaSchema.safeParse(meta)
|
|
109
|
+
if (parsedRawMeta.success) {
|
|
110
|
+
return {
|
|
111
|
+
title: parsedRawMeta.data.title ?? parsedRawMeta.data.identity ?? "Unknown",
|
|
112
|
+
description: parsedRawMeta.data.description,
|
|
113
|
+
icon: parsedRawMeta.data.icon,
|
|
114
|
+
iconColor: parsedRawMeta.data.iconColor,
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return {
|
|
119
|
+
title: "Unknown",
|
|
120
|
+
}
|
|
121
|
+
}
|