@highstate/backend 0.9.4 → 0.9.6
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-I7NLZ54E.js → chunk-NMGIUI6X.js} +22 -7
- package/dist/chunk-NMGIUI6X.js.map +1 -0
- package/dist/highstate.manifest.json +3 -3
- package/dist/index.js +160 -92
- package/dist/index.js.map +1 -1
- package/dist/library/worker/main.js +19 -15
- package/dist/library/worker/main.js.map +1 -1
- package/dist/shared/index.js +3 -1
- package/package.json +3 -3
- package/src/library/local.ts +10 -2
- package/src/library/worker/evaluator.ts +14 -6
- package/src/library/worker/loader.ts +12 -9
- package/src/orchestrator/manager.ts +4 -2
- package/src/orchestrator/operation-workset.ts +132 -63
- package/src/orchestrator/operation.ts +49 -30
- package/src/project/manager.ts +8 -0
- package/src/shared/library.ts +3 -0
- package/src/shared/operation.ts +13 -2
- package/src/shared/resolvers/validation.ts +3 -3
- package/src/shared/state.ts +9 -1
- package/dist/chunk-I7NLZ54E.js.map +0 -1
@@ -2,7 +2,12 @@ import type { LibraryBackend } from "../library"
|
|
2
2
|
import type { ProjectBackend } from "../project"
|
3
3
|
import type { StateBackend, StateManager } from "../state"
|
4
4
|
import type { Logger } from "pino"
|
5
|
-
import {
|
5
|
+
import {
|
6
|
+
isUnitModel,
|
7
|
+
parseInstanceId,
|
8
|
+
type ComponentModel,
|
9
|
+
type InstanceModel,
|
10
|
+
} from "@highstate/contract"
|
6
11
|
import { unique } from "remeda"
|
7
12
|
import {
|
8
13
|
applyPartialInstanceState,
|
@@ -22,8 +27,12 @@ import {
|
|
22
27
|
type ResolvedInstanceInput,
|
23
28
|
} from "../shared"
|
24
29
|
|
30
|
+
export type OperationPhase = "update" | "destroy" | "refresh"
|
31
|
+
|
25
32
|
export class OperationWorkset {
|
26
|
-
private readonly
|
33
|
+
private readonly instanceIdsToUpdate = new Set<string>()
|
34
|
+
private readonly instanceIdsToDestroy = new Set<string>()
|
35
|
+
|
27
36
|
private readonly instanceMap = new Map<string, InstanceModel>()
|
28
37
|
private readonly instanceChildrenMap = new Map<string, InstanceModel[]>()
|
29
38
|
|
@@ -67,8 +76,24 @@ export class OperationWorkset {
|
|
67
76
|
return instance
|
68
77
|
}
|
69
78
|
|
70
|
-
public
|
71
|
-
|
79
|
+
public getAffectedInstanceIds(phase: OperationPhase): string[] {
|
80
|
+
if (phase === "destroy") {
|
81
|
+
return Array.from(this.instanceIdsToDestroy)
|
82
|
+
}
|
83
|
+
|
84
|
+
return Array.from(this.instanceIdsToUpdate)
|
85
|
+
}
|
86
|
+
|
87
|
+
public getInstanceOrUndefined(instanceId: string): InstanceModel | undefined {
|
88
|
+
return this.instanceMap.get(instanceId)
|
89
|
+
}
|
90
|
+
|
91
|
+
public isAffected(instanceId: string, phase: OperationPhase): boolean {
|
92
|
+
if (phase === "destroy") {
|
93
|
+
return this.instanceIdsToDestroy.has(instanceId)
|
94
|
+
}
|
95
|
+
|
96
|
+
return this.instanceIdsToUpdate.has(instanceId)
|
72
97
|
}
|
73
98
|
|
74
99
|
public updateState(update: InstanceStateUpdate): InstanceState {
|
@@ -76,7 +101,7 @@ export class OperationWorkset {
|
|
76
101
|
this.stateManager.emitStatePatch(this.operation.projectId, createInstanceStatePatch(update))
|
77
102
|
|
78
103
|
if (finalState.parentId) {
|
79
|
-
this.
|
104
|
+
this.recalculateCompositeInstanceState(finalState.parentId)
|
80
105
|
}
|
81
106
|
|
82
107
|
return finalState
|
@@ -128,25 +153,44 @@ export class OperationWorkset {
|
|
128
153
|
|
129
154
|
public emitAffectedInitialStates(): void {
|
130
155
|
for (const state of this.initialStateMap.values()) {
|
131
|
-
if (this.
|
156
|
+
if (this.instanceIdsToUpdate.has(state.id)) {
|
132
157
|
this.stateManager.emitStatePatch(this.operation.projectId, state)
|
133
158
|
}
|
134
159
|
}
|
135
160
|
}
|
136
161
|
|
137
|
-
public getAffectedCompositeChildren(instanceId: string): InstanceModel[] {
|
162
|
+
public getAffectedCompositeChildren(instanceId: string, phase: OperationPhase): InstanceModel[] {
|
138
163
|
const children = this.instanceChildrenMap.get(instanceId)
|
139
164
|
if (!children) {
|
140
165
|
return []
|
141
166
|
}
|
142
167
|
|
143
|
-
|
168
|
+
if (phase === "destroy") {
|
169
|
+
return children.filter(child => this.instanceIdsToDestroy.has(child.id))
|
170
|
+
}
|
171
|
+
|
172
|
+
return children.filter(child => this.instanceIdsToUpdate.has(child.id))
|
144
173
|
}
|
145
174
|
|
146
175
|
public getState(instanceId: string): InstanceState | undefined {
|
147
176
|
return this.stateMap.get(instanceId)
|
148
177
|
}
|
149
178
|
|
179
|
+
public getParentId(instanceId: string, phase: OperationPhase): string | null {
|
180
|
+
if (phase === "destroy") {
|
181
|
+
const state = this.stateMap.get(instanceId)
|
182
|
+
if (!state) {
|
183
|
+
return null
|
184
|
+
}
|
185
|
+
|
186
|
+
return state.parentId
|
187
|
+
}
|
188
|
+
|
189
|
+
const instance = this.getInstance(instanceId)
|
190
|
+
|
191
|
+
return instance.parentId ?? null
|
192
|
+
}
|
193
|
+
|
150
194
|
public getDependentStates(instanceId: string): InstanceState[] {
|
151
195
|
const dependentStateIds = this.dependentStateIdMap.get(instanceId)
|
152
196
|
if (!dependentStateIds) {
|
@@ -158,7 +202,7 @@ export class OperationWorkset {
|
|
158
202
|
.filter((state): state is InstanceState => !!state)
|
159
203
|
}
|
160
204
|
|
161
|
-
private
|
205
|
+
private recalculateCompositeInstanceState(instanceId: string): void {
|
162
206
|
const state = this.stateMap.get(instanceId) ?? createInstanceState(instanceId)
|
163
207
|
let currentResourceCount = 0
|
164
208
|
let totalResourceCount = 0
|
@@ -186,7 +230,7 @@ export class OperationWorkset {
|
|
186
230
|
this.stateManager.emitStatePatch(this.operation.projectId, updatedState)
|
187
231
|
|
188
232
|
if (state.parentId) {
|
189
|
-
this.
|
233
|
+
this.recalculateCompositeInstanceState(state.parentId)
|
190
234
|
}
|
191
235
|
}
|
192
236
|
|
@@ -215,9 +259,9 @@ export class OperationWorkset {
|
|
215
259
|
}
|
216
260
|
}
|
217
261
|
|
218
|
-
private async
|
262
|
+
private async calculateInstanceIdsToUpdate(): Promise<void> {
|
219
263
|
const traverse = async (instanceId: string) => {
|
220
|
-
if (this.
|
264
|
+
if (this.instanceIdsToUpdate.has(instanceId)) {
|
221
265
|
return
|
222
266
|
}
|
223
267
|
|
@@ -238,51 +282,62 @@ export class OperationWorkset {
|
|
238
282
|
const { inputHash: expectedInputHash } = await this.inputHashResolver(instance.id)
|
239
283
|
|
240
284
|
if (this.operation.options.forceUpdateDependencies) {
|
241
|
-
this.
|
285
|
+
this.instanceIdsToUpdate.add(instanceId)
|
242
286
|
return
|
243
287
|
}
|
244
288
|
|
245
289
|
if (state?.status !== "created" || state.inputHash !== expectedInputHash) {
|
246
|
-
this.
|
290
|
+
this.instanceIdsToUpdate.add(instanceId)
|
247
291
|
}
|
248
292
|
}
|
249
293
|
|
250
294
|
// 1. extend affected instance IDs with their not-created or not-up-to-date dependencies (only for "update" operations)
|
251
|
-
for (const instanceId of this.operation.
|
295
|
+
for (const instanceId of this.operation.requestedInstanceIds) {
|
252
296
|
if (this.operation.type === "update") {
|
253
297
|
await traverse(instanceId)
|
254
298
|
}
|
255
299
|
|
256
|
-
this.
|
300
|
+
this.instanceIdsToUpdate.add(instanceId)
|
257
301
|
}
|
258
302
|
|
259
303
|
// 2. extend affected instance IDs with the children of the affected composite instances
|
260
|
-
const compositeInstanceQueue = Array.from(this.
|
304
|
+
const compositeInstanceQueue = Array.from(this.instanceIdsToUpdate)
|
261
305
|
while (compositeInstanceQueue.length > 0) {
|
262
306
|
const childId = compositeInstanceQueue.pop()!
|
263
307
|
const children = this.instanceChildrenMap.get(childId) ?? []
|
264
308
|
|
265
309
|
for (const child of children) {
|
266
310
|
compositeInstanceQueue.push(child.id)
|
267
|
-
|
311
|
+
|
312
|
+
if (this.operation.options.forceUpdateChildren) {
|
313
|
+
this.instanceIdsToUpdate.add(child.id)
|
314
|
+
continue
|
315
|
+
}
|
316
|
+
|
317
|
+
const state = this.stateMap.get(child.id)
|
318
|
+
const { inputHash: expectedInputHash } = await this.inputHashResolver(child.id)
|
319
|
+
|
320
|
+
if (state?.status !== "created" || state.inputHash !== expectedInputHash) {
|
321
|
+
this.instanceIdsToUpdate.add(child.id)
|
322
|
+
}
|
268
323
|
}
|
269
324
|
}
|
270
325
|
|
271
326
|
// 3. detect composite instance children and include their parents (recursively)
|
272
|
-
for (const instanceId of this.
|
327
|
+
for (const instanceId of this.instanceIdsToUpdate) {
|
273
328
|
let instance = this.instanceMap.get(instanceId)
|
274
329
|
while (instance?.parentId) {
|
275
|
-
this.
|
330
|
+
this.instanceIdsToUpdate.add(instance.parentId)
|
276
331
|
instance = this.instanceMap.get(instance.parentId)
|
277
332
|
}
|
278
333
|
}
|
279
334
|
|
280
|
-
this.operation.
|
335
|
+
this.operation.instanceIdsToUpdate = Array.from(this.instanceIdsToUpdate)
|
281
336
|
}
|
282
337
|
|
283
|
-
private
|
338
|
+
private calculateInstanceIdsToDestroy() {
|
284
339
|
const traverse = (instanceKey: string) => {
|
285
|
-
if (this.
|
340
|
+
if (this.instanceIdsToDestroy.has(instanceKey)) {
|
286
341
|
return
|
287
342
|
}
|
288
343
|
|
@@ -295,46 +350,74 @@ export class OperationWorkset {
|
|
295
350
|
|
296
351
|
for (const dependentId of dependentIds) {
|
297
352
|
traverse(dependentId)
|
298
|
-
this.
|
353
|
+
this.instanceIdsToDestroy.add(dependentId)
|
299
354
|
}
|
300
355
|
}
|
301
356
|
|
302
|
-
|
303
|
-
|
304
|
-
const
|
305
|
-
|
306
|
-
|
307
|
-
|
357
|
+
if (this.operation.type === "destroy" || this.operation.type === "recreate") {
|
358
|
+
// 1.a. extend affected instance IDs with their created dependents (if not forbidden by the operation options)
|
359
|
+
for (const instanceId of this.operation.requestedInstanceIds) {
|
360
|
+
const instance = this.instanceMap.get(instanceId)
|
361
|
+
if (!instance) {
|
362
|
+
throw new Error(`Instance not found: ${instanceId}`)
|
363
|
+
}
|
308
364
|
|
309
|
-
|
310
|
-
|
365
|
+
if (this.operation.options.destroyDependentInstances) {
|
366
|
+
traverse(instance.id)
|
367
|
+
}
|
368
|
+
|
369
|
+
this.instanceIdsToDestroy.add(instanceId)
|
311
370
|
}
|
371
|
+
} else if (this.operation.type === "update") {
|
372
|
+
// 1.b. find all children instances of the affected to-update instances which are in the state map, but not in the instance map
|
373
|
+
// in other words, this code cleans up child instances which are not produced by composite instances anymore
|
374
|
+
|
375
|
+
for (const instanceId of this.operation.instanceIdsToUpdate) {
|
376
|
+
const [type] = parseInstanceId(instanceId)
|
377
|
+
const component = this.library.components[type]
|
312
378
|
|
313
|
-
|
379
|
+
if (!component || isUnitModel(component)) {
|
380
|
+
// ignore non-composite instances
|
381
|
+
continue
|
382
|
+
}
|
383
|
+
|
384
|
+
const childrenQueue = [...(this.stateChildIdMap.get(instanceId) ?? [])]
|
385
|
+
|
386
|
+
while (childrenQueue.length > 0) {
|
387
|
+
const childId = childrenQueue.pop()!
|
388
|
+
if (!this.instanceMap.has(childId)) {
|
389
|
+
this.instanceIdsToDestroy.add(childId)
|
390
|
+
}
|
391
|
+
|
392
|
+
childrenQueue.push(...(this.stateChildIdMap.get(childId) ?? []))
|
393
|
+
}
|
394
|
+
}
|
395
|
+
} else {
|
396
|
+
return
|
314
397
|
}
|
315
398
|
|
316
399
|
// 2. extend affected instance IDs with the children of the affected composite instances
|
317
|
-
const compositeInstanceQueue = Array.from(this.
|
400
|
+
const compositeInstanceQueue = Array.from(this.instanceIdsToDestroy)
|
318
401
|
while (compositeInstanceQueue.length > 0) {
|
319
402
|
const childId = compositeInstanceQueue.pop()!
|
320
403
|
const children = this.stateChildIdMap.get(childId) ?? []
|
321
404
|
|
322
405
|
for (const child of children) {
|
323
406
|
compositeInstanceQueue.push(child)
|
324
|
-
this.
|
407
|
+
this.instanceIdsToDestroy.add(child)
|
325
408
|
}
|
326
409
|
}
|
327
410
|
|
328
411
|
// 3. detect composite instance children and include their parents (recursively)
|
329
|
-
for (const instanceId of this.
|
412
|
+
for (const instanceId of this.instanceIdsToDestroy) {
|
330
413
|
let state = this.stateMap.get(instanceId)
|
331
414
|
while (state?.parentId) {
|
332
|
-
this.
|
415
|
+
this.instanceIdsToDestroy.add(state.parentId)
|
333
416
|
state = this.stateMap.get(state.parentId)
|
334
417
|
}
|
335
418
|
}
|
336
419
|
|
337
|
-
this.operation.
|
420
|
+
this.operation.instanceIdsToDestroy = Array.from(this.instanceIdsToDestroy)
|
338
421
|
}
|
339
422
|
|
340
423
|
private getSourceHashIfApplicable(
|
@@ -366,18 +449,7 @@ export class OperationWorkset {
|
|
366
449
|
}
|
367
450
|
|
368
451
|
public getLockInstanceIds(): string[] {
|
369
|
-
|
370
|
-
|
371
|
-
// enrich with the parent IDs of the affected instances in order to lock and update parent states
|
372
|
-
for (const instanceId of this.operation.affectedInstanceIds) {
|
373
|
-
const instance = this.getInstance(instanceId)
|
374
|
-
|
375
|
-
if (instance.parentId) {
|
376
|
-
instanceIds.add(instance.parentId)
|
377
|
-
}
|
378
|
-
}
|
379
|
-
|
380
|
-
return Array.from(instanceIds)
|
452
|
+
return Array.from(this.instanceIdsToUpdate.union(this.instanceIdsToDestroy))
|
381
453
|
}
|
382
454
|
|
383
455
|
public static async load(
|
@@ -422,8 +494,13 @@ export class OperationWorkset {
|
|
422
494
|
if (worksetInstance) {
|
423
495
|
worksetInstance.outputs = instance.instance.outputs
|
424
496
|
worksetInstance.resolvedOutputs = instance.instance.resolvedOutputs
|
425
|
-
} else {
|
497
|
+
} else if (instance.instance.parentId) {
|
426
498
|
workset.addInstance(instance.instance)
|
499
|
+
} else {
|
500
|
+
workset.logger.warn(
|
501
|
+
`ignoring instance "${instance.instance.id}" from composite instance state because it is not in the project or is not a part of known composite instance`,
|
502
|
+
)
|
503
|
+
continue
|
427
504
|
}
|
428
505
|
|
429
506
|
for (const child of instance.children) {
|
@@ -492,20 +569,12 @@ export class OperationWorkset {
|
|
492
569
|
{ promiseCache: workset.inputHashResolverPromiseCache },
|
493
570
|
)
|
494
571
|
|
495
|
-
|
496
|
-
|
497
|
-
case "preview":
|
498
|
-
case "refresh":
|
499
|
-
await workset.extendForUpdate()
|
500
|
-
break
|
501
|
-
case "recreate":
|
502
|
-
workset.extendForDestroy()
|
503
|
-
break
|
504
|
-
case "destroy":
|
505
|
-
workset.extendForDestroy()
|
506
|
-
break
|
572
|
+
if (operation.type !== "destroy") {
|
573
|
+
await workset.calculateInstanceIdsToUpdate()
|
507
574
|
}
|
508
575
|
|
576
|
+
workset.calculateInstanceIdsToDestroy()
|
577
|
+
|
509
578
|
return workset
|
510
579
|
}
|
511
580
|
}
|
@@ -24,9 +24,7 @@ import {
|
|
24
24
|
tryWrapAbortErrorLike,
|
25
25
|
valueToString,
|
26
26
|
} from "../common"
|
27
|
-
import { OperationWorkset } from "./operation-workset"
|
28
|
-
|
29
|
-
type OperationPhase = "update" | "destroy" | "refresh"
|
27
|
+
import { OperationWorkset, type OperationPhase } from "./operation-workset"
|
30
28
|
|
31
29
|
export class RuntimeOperation {
|
32
30
|
private readonly abortController = new AbortController()
|
@@ -117,10 +115,14 @@ export class RuntimeOperation {
|
|
117
115
|
}
|
118
116
|
|
119
117
|
private async processOperation(): Promise<void> {
|
120
|
-
this.operation.
|
118
|
+
this.operation.instanceIdsToUpdate = this.workset.operation.instanceIdsToUpdate
|
119
|
+
this.operation.instanceIdsToDestroy = this.workset.operation.instanceIdsToDestroy
|
121
120
|
|
122
121
|
this.logger.info(
|
123
|
-
{
|
122
|
+
{
|
123
|
+
instanceIdsToUpdate: this.operation.instanceIdsToUpdate,
|
124
|
+
instanceIdsToDestroy: this.operation.instanceIdsToDestroy,
|
125
|
+
},
|
124
126
|
"operation started",
|
125
127
|
)
|
126
128
|
|
@@ -130,9 +132,9 @@ export class RuntimeOperation {
|
|
130
132
|
this.currentPhase = phase
|
131
133
|
|
132
134
|
const promises: Promise<void>[] = []
|
133
|
-
for (const instanceId of this.
|
134
|
-
const
|
135
|
-
if (
|
135
|
+
for (const instanceId of this.workset.getAffectedInstanceIds(phase)) {
|
136
|
+
const parentId = this.workset.getParentId(instanceId, phase)
|
137
|
+
if (parentId && this.workset.isAffected(parentId, phase)) {
|
136
138
|
// do not call the operation for child instances of affected composites,
|
137
139
|
// they will be called by their parent instance
|
138
140
|
continue
|
@@ -163,19 +165,25 @@ export class RuntimeOperation {
|
|
163
165
|
}
|
164
166
|
|
165
167
|
private getInstancePromiseForOperation(instanceId: string): Promise<void> {
|
166
|
-
const
|
167
|
-
const component = this.workset.library.components[
|
168
|
+
const [instanceType] = parseInstanceId(instanceId)
|
169
|
+
const component = this.workset.library.components[instanceType]
|
168
170
|
|
169
171
|
if (isUnitModel(component)) {
|
170
|
-
return this.getUnitPromise(
|
172
|
+
return this.getUnitPromise(instanceId)
|
171
173
|
}
|
172
174
|
|
173
|
-
return this.getCompositePromise(
|
175
|
+
return this.getCompositePromise(instanceId)
|
174
176
|
}
|
175
177
|
|
176
178
|
private getOperationPhases(): OperationPhase[] {
|
177
179
|
switch (this.operation.type) {
|
178
|
-
case "update":
|
180
|
+
case "update": {
|
181
|
+
if (this.operation.instanceIdsToDestroy.length > 0) {
|
182
|
+
return ["destroy", "update"]
|
183
|
+
}
|
184
|
+
|
185
|
+
return ["update"]
|
186
|
+
}
|
179
187
|
case "preview":
|
180
188
|
return ["update"]
|
181
189
|
case "recreate":
|
@@ -187,35 +195,36 @@ export class RuntimeOperation {
|
|
187
195
|
}
|
188
196
|
}
|
189
197
|
|
190
|
-
private async getUnitPromise(
|
198
|
+
private async getUnitPromise(instanceId: string): Promise<void> {
|
191
199
|
switch (this.currentPhase) {
|
192
200
|
case "update": {
|
193
|
-
return this.updateUnit(
|
201
|
+
return this.updateUnit(instanceId)
|
194
202
|
}
|
195
203
|
case "destroy": {
|
196
|
-
return this.destroyUnit(
|
204
|
+
return this.destroyUnit(instanceId)
|
197
205
|
}
|
198
206
|
case "refresh": {
|
199
|
-
return this.refreshUnit(
|
207
|
+
return this.refreshUnit(instanceId)
|
200
208
|
}
|
201
209
|
}
|
202
210
|
}
|
203
211
|
|
204
|
-
private async getCompositePromise(
|
205
|
-
const logger = this.logger.child({ instanceId
|
212
|
+
private async getCompositePromise(instanceId: string): Promise<void> {
|
213
|
+
const logger = this.logger.child({ instanceId })
|
206
214
|
|
207
|
-
return this.getInstancePromise(
|
208
|
-
const state = this.workset.getState(
|
215
|
+
return this.getInstancePromise(instanceId, async () => {
|
216
|
+
const state = this.workset.getState(instanceId) ?? createInstanceState(instanceId)
|
217
|
+
const instance = this.workset.getInstanceOrUndefined(instanceId)
|
209
218
|
|
210
219
|
this.updateInstanceState({
|
211
220
|
...state,
|
212
|
-
parentId: instance
|
221
|
+
parentId: instance?.parentId,
|
213
222
|
latestOperationId: this.operation.id,
|
214
223
|
status: this.getStatusByOperationType(),
|
215
224
|
error: null,
|
216
225
|
})
|
217
226
|
|
218
|
-
const children = this.workset.getAffectedCompositeChildren(
|
227
|
+
const children = this.workset.getAffectedCompositeChildren(instanceId, this.currentPhase)
|
219
228
|
const childPromises: Promise<void>[] = []
|
220
229
|
|
221
230
|
if (children.length) {
|
@@ -225,6 +234,14 @@ export class RuntimeOperation {
|
|
225
234
|
}
|
226
235
|
|
227
236
|
for (const child of children) {
|
237
|
+
if (
|
238
|
+
!this.operation.options.forceUpdateChildren &&
|
239
|
+
!this.workset.isAffected(child.id, this.currentPhase)
|
240
|
+
) {
|
241
|
+
// skip children that are not affected by the operation
|
242
|
+
continue
|
243
|
+
}
|
244
|
+
|
228
245
|
logger.debug(`waiting for child: "%s"`, child.id)
|
229
246
|
childPromises.push(this.getInstancePromiseForOperation(child.id))
|
230
247
|
}
|
@@ -237,18 +254,18 @@ export class RuntimeOperation {
|
|
237
254
|
}
|
238
255
|
|
239
256
|
this.updateInstanceState({
|
240
|
-
id:
|
257
|
+
id: instanceId,
|
241
258
|
status: this.operation.type === "destroy" ? "not_created" : "created",
|
242
|
-
inputHash: await this.workset.getUpToDateInputHash(instance),
|
259
|
+
inputHash: instance ? await this.workset.getUpToDateInputHash(instance) : undefined,
|
243
260
|
})
|
244
261
|
} catch (error) {
|
245
262
|
if (isAbortErrorLike(error)) {
|
246
|
-
this.workset.restoreInitialStatus(
|
263
|
+
this.workset.restoreInitialStatus(instanceId)
|
247
264
|
return
|
248
265
|
}
|
249
266
|
|
250
267
|
this.updateInstanceState({
|
251
|
-
id:
|
268
|
+
id: instanceId,
|
252
269
|
status: "error",
|
253
270
|
error: errorToString(error),
|
254
271
|
})
|
@@ -256,8 +273,10 @@ export class RuntimeOperation {
|
|
256
273
|
})
|
257
274
|
}
|
258
275
|
|
259
|
-
private updateUnit(
|
260
|
-
return this.getInstancePromise(
|
276
|
+
private updateUnit(instanceId: string): Promise<void> {
|
277
|
+
return this.getInstancePromise(instanceId, async logger => {
|
278
|
+
const instance = this.workset.getInstance(instanceId)
|
279
|
+
|
261
280
|
this.updateInstanceState({
|
262
281
|
id: instance.id,
|
263
282
|
parentId: instance.parentId,
|
@@ -323,7 +342,7 @@ export class RuntimeOperation {
|
|
323
342
|
const dependencyPromises: Promise<void>[] = []
|
324
343
|
|
325
344
|
for (const dependency of dependencies) {
|
326
|
-
if (!this.operation.
|
345
|
+
if (!this.operation.instanceIdsToUpdate.includes(dependency.id)) {
|
327
346
|
// skip dependencies that are not affected by the operation
|
328
347
|
continue
|
329
348
|
}
|
package/src/project/manager.ts
CHANGED
@@ -19,6 +19,10 @@ import {
|
|
19
19
|
} from "../shared"
|
20
20
|
|
21
21
|
type CompositeInstanceEvent =
|
22
|
+
| {
|
23
|
+
type: "evaluation-started"
|
24
|
+
instanceId: string
|
25
|
+
}
|
22
26
|
| {
|
23
27
|
type: "updated"
|
24
28
|
instance: CompositeInstance
|
@@ -157,6 +161,10 @@ export class ProjectManager {
|
|
157
161
|
await this.projectLockManager.getLock(projectId).lockInstances(instanceIds, async () => {
|
158
162
|
this.logger.debug({ instanceIds }, "evaluating composite instances")
|
159
163
|
|
164
|
+
for (const instanceId of instanceIds) {
|
165
|
+
this.compositeInstanceEE.emit(projectId, { type: "evaluation-started", instanceId })
|
166
|
+
}
|
167
|
+
|
160
168
|
const [
|
161
169
|
{ instances, resolvedInputs, stateMap, resolveInputHash },
|
162
170
|
topLevelCompositeChildrenIds,
|
package/src/shared/library.ts
CHANGED
package/src/shared/operation.ts
CHANGED
@@ -19,6 +19,14 @@ export const operationOptionsSchema = z.object({
|
|
19
19
|
*/
|
20
20
|
forceUpdateDependencies: z.boolean().default(false),
|
21
21
|
|
22
|
+
/**
|
23
|
+
* Whether to force update all children of the composite instances even if they are not changed.
|
24
|
+
*
|
25
|
+
* Only applicable for `update`, `preview`, `recreate`, and `refresh` operations.
|
26
|
+
* By default, `false`.
|
27
|
+
*/
|
28
|
+
forceUpdateChildren: z.boolean().default(false),
|
29
|
+
|
22
30
|
/**
|
23
31
|
* Whether to destroy all dependents of the instances when destroying them.
|
24
32
|
*
|
@@ -72,8 +80,11 @@ export const projectOperationSchema = z.object({
|
|
72
80
|
|
73
81
|
projectId: z.string(),
|
74
82
|
type: operationTypeSchema,
|
75
|
-
|
76
|
-
|
83
|
+
|
84
|
+
requestedInstanceIds: z.array(z.string()),
|
85
|
+
|
86
|
+
instanceIdsToUpdate: z.array(z.string()).default(() => []),
|
87
|
+
instanceIdsToDestroy: z.array(z.string()).default(() => []),
|
77
88
|
|
78
89
|
options: operationOptionsSchema.default(() => ({})),
|
79
90
|
|
@@ -59,7 +59,7 @@ export const createValidationResolver = defineGraphResolver<
|
|
59
59
|
|
60
60
|
return {
|
61
61
|
status: "invalid-args",
|
62
|
-
errorText: ajv.errorsText()
|
62
|
+
errorText: `invalid argument "${name}": ${ajv.errorsText()}`,
|
63
63
|
}
|
64
64
|
}
|
65
65
|
}
|
@@ -70,14 +70,14 @@ export const createValidationResolver = defineGraphResolver<
|
|
70
70
|
if (inputInstance?.status !== "ok") {
|
71
71
|
return {
|
72
72
|
status: "invalid-inputs",
|
73
|
-
errorText: `instance "${input.input.instanceId}"
|
73
|
+
errorText: `instance "${input.input.instanceId}" has errors`,
|
74
74
|
}
|
75
75
|
}
|
76
76
|
}
|
77
77
|
}
|
78
78
|
|
79
79
|
for (const [name, input] of Object.entries(component.inputs)) {
|
80
|
-
if (!input.required
|
80
|
+
if (!input.required) {
|
81
81
|
continue
|
82
82
|
}
|
83
83
|
|
package/src/shared/state.ts
CHANGED
@@ -13,9 +13,16 @@ export const instanceStatusSchema = z.enum([
|
|
13
13
|
"unknown",
|
14
14
|
])
|
15
15
|
|
16
|
+
export const instanceStatusFieldValueSchema = z.union([
|
17
|
+
z.string(),
|
18
|
+
z.number(),
|
19
|
+
z.boolean(),
|
20
|
+
z.array(z.string()),
|
21
|
+
])
|
22
|
+
|
16
23
|
export const instanceStatusFieldSchema = z.object({
|
17
24
|
name: z.string(),
|
18
|
-
value:
|
25
|
+
value: instanceStatusFieldValueSchema.optional(),
|
19
26
|
displayName: z.string().optional(),
|
20
27
|
sensitive: z.boolean().optional(),
|
21
28
|
url: z.string().optional(),
|
@@ -138,6 +145,7 @@ export const instanceStateUpdateSchema = z
|
|
138
145
|
})
|
139
146
|
.partial()
|
140
147
|
|
148
|
+
export type InstanceStatusFieldValue = z.infer<typeof instanceStatusFieldValueSchema>
|
141
149
|
export type InstanceStatusField = z.infer<typeof instanceStatusFieldSchema>
|
142
150
|
export type InstanceTerminal = z.infer<typeof instanceTerminalSchema>
|
143
151
|
|