@highstate/backend 0.9.9 → 0.9.10
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-NMGIUI6X.js → chunk-DQDXRDUA.js} +228 -96
- package/dist/chunk-DQDXRDUA.js.map +1 -0
- package/dist/{chunk-EQ4LMS7B.js → chunk-WXDYCRTT.js} +1 -57
- package/dist/chunk-WXDYCRTT.js.map +1 -0
- package/dist/highstate.manifest.json +3 -3
- package/dist/index.js +100 -98
- package/dist/index.js.map +1 -1
- package/dist/library/worker/main.js +1 -1
- package/dist/shared/index.js +11 -13
- package/package.json +3 -3
- package/src/common/utils.ts +0 -74
- package/src/library/abstractions.ts +0 -5
- package/src/orchestrator/operation-workset.ts +47 -55
- package/src/orchestrator/operation.ts +1 -1
- package/src/project/manager.ts +66 -61
- package/src/shared/async-batcher.ts +73 -0
- package/src/shared/index.ts +1 -0
- package/src/shared/resolvers/graph-resolver.ts +146 -79
- package/src/shared/resolvers/input-hash.ts +22 -17
- package/src/shared/resolvers/input.ts +29 -26
- package/src/shared/resolvers/registry.ts +19 -9
- package/src/shared/resolvers/validation.ts +12 -18
- package/src/state/abstractions.ts +2 -3
- package/src/state/local.ts +13 -6
- package/src/terminal/manager.ts +2 -2
- package/dist/chunk-EQ4LMS7B.js.map +0 -1
- package/dist/chunk-NMGIUI6X.js.map +0 -1
package/dist/shared/index.js
CHANGED
@@ -1,16 +1,15 @@
|
|
1
1
|
import {
|
2
|
-
|
2
|
+
GraphResolver,
|
3
|
+
InputHashResolver,
|
4
|
+
InputResolver,
|
5
|
+
ValidationResolver,
|
3
6
|
applyLibraryUpdate,
|
4
7
|
applyPartialInstanceState,
|
5
8
|
buildDependentInstanceStateMap,
|
6
9
|
compositeInstanceSchema,
|
7
|
-
|
8
|
-
createInputHashResolver,
|
9
|
-
createInputResolver,
|
10
|
+
createAsyncBatcher,
|
10
11
|
createInstanceState,
|
11
12
|
createInstanceStatePatch,
|
12
|
-
createValidationResolver,
|
13
|
-
defineGraphResolver,
|
14
13
|
diffLibraries,
|
15
14
|
getAllDependentInstanceIds,
|
16
15
|
getMatchedInjectionInstanceInputs,
|
@@ -48,20 +47,19 @@ import {
|
|
48
47
|
projectOperationSchema,
|
49
48
|
resolverFactories,
|
50
49
|
terminalSessionSchema
|
51
|
-
} from "../chunk-
|
50
|
+
} from "../chunk-DQDXRDUA.js";
|
52
51
|
export {
|
53
|
-
|
52
|
+
GraphResolver,
|
53
|
+
InputHashResolver,
|
54
|
+
InputResolver,
|
55
|
+
ValidationResolver,
|
54
56
|
applyLibraryUpdate,
|
55
57
|
applyPartialInstanceState,
|
56
58
|
buildDependentInstanceStateMap,
|
57
59
|
compositeInstanceSchema,
|
58
|
-
|
59
|
-
createInputHashResolver,
|
60
|
-
createInputResolver,
|
60
|
+
createAsyncBatcher,
|
61
61
|
createInstanceState,
|
62
62
|
createInstanceStatePatch,
|
63
|
-
createValidationResolver,
|
64
|
-
defineGraphResolver,
|
65
63
|
diffLibraries,
|
66
64
|
getAllDependentInstanceIds,
|
67
65
|
getMatchedInjectionInstanceInputs,
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@highstate/backend",
|
3
|
-
"version": "0.9.
|
3
|
+
"version": "0.9.10",
|
4
4
|
"type": "module",
|
5
5
|
"files": [
|
6
6
|
"dist",
|
@@ -26,7 +26,7 @@
|
|
26
26
|
"build": "highstate build"
|
27
27
|
},
|
28
28
|
"dependencies": {
|
29
|
-
"@highstate/contract": "^0.9.
|
29
|
+
"@highstate/contract": "^0.9.10",
|
30
30
|
"@types/node": "^22.10.1",
|
31
31
|
"ajv": "^8.17.1",
|
32
32
|
"better-lock": "^3.2.0",
|
@@ -65,5 +65,5 @@
|
|
65
65
|
"rollup": "^4.28.1",
|
66
66
|
"typescript": "^5.7.2"
|
67
67
|
},
|
68
|
-
"gitHead": "
|
68
|
+
"gitHead": "aacf8837fdf40f2bb2f83d4c11b35a358e26ec33"
|
69
69
|
}
|
package/src/common/utils.ts
CHANGED
@@ -61,77 +61,3 @@ export function errorToString(error: unknown): string {
|
|
61
61
|
|
62
62
|
return JSON.stringify(error)
|
63
63
|
}
|
64
|
-
|
65
|
-
export type AsyncBatcherOptions = {
|
66
|
-
waitMs?: number
|
67
|
-
maxWaitTimeMs?: number
|
68
|
-
}
|
69
|
-
|
70
|
-
export function createAsyncBatcher<T>(
|
71
|
-
fn: (items: T[]) => Promise<void>,
|
72
|
-
{ waitMs = 100, maxWaitTimeMs = 1000 }: AsyncBatcherOptions = {},
|
73
|
-
) {
|
74
|
-
let batch: T[] = []
|
75
|
-
let activeTimeout: NodeJS.Timeout | null = null
|
76
|
-
let maxWaitTimeout: NodeJS.Timeout | null = null
|
77
|
-
let firstCallTimestamp: number | null = null
|
78
|
-
|
79
|
-
async function processBatch() {
|
80
|
-
if (batch.length === 0) return
|
81
|
-
|
82
|
-
const currentBatch = batch
|
83
|
-
batch = [] // Reset batch before async call
|
84
|
-
|
85
|
-
await fn(currentBatch)
|
86
|
-
|
87
|
-
// Clear max wait timer since batch has been processed
|
88
|
-
if (maxWaitTimeout) {
|
89
|
-
clearTimeout(maxWaitTimeout)
|
90
|
-
maxWaitTimeout = null
|
91
|
-
}
|
92
|
-
firstCallTimestamp = null
|
93
|
-
}
|
94
|
-
|
95
|
-
function schedule() {
|
96
|
-
if (activeTimeout) clearTimeout(activeTimeout)
|
97
|
-
activeTimeout = setTimeout(() => {
|
98
|
-
activeTimeout = null
|
99
|
-
void processBatch()
|
100
|
-
}, waitMs)
|
101
|
-
|
102
|
-
// Ensure batch is executed within maxWaitTimeMs
|
103
|
-
if (!firstCallTimestamp) {
|
104
|
-
firstCallTimestamp = Date.now()
|
105
|
-
maxWaitTimeout = setTimeout(() => {
|
106
|
-
if (activeTimeout) clearTimeout(activeTimeout)
|
107
|
-
activeTimeout = null
|
108
|
-
void processBatch()
|
109
|
-
}, maxWaitTimeMs)
|
110
|
-
}
|
111
|
-
}
|
112
|
-
|
113
|
-
return {
|
114
|
-
/**
|
115
|
-
* Add an item to the batch.
|
116
|
-
*/
|
117
|
-
call(item: T): void {
|
118
|
-
batch.push(item)
|
119
|
-
schedule()
|
120
|
-
},
|
121
|
-
|
122
|
-
/**
|
123
|
-
* Immediately flush the pending batch (if any).
|
124
|
-
*/
|
125
|
-
async flush(): Promise<void> {
|
126
|
-
if (activeTimeout) {
|
127
|
-
clearTimeout(activeTimeout)
|
128
|
-
activeTimeout = null
|
129
|
-
}
|
130
|
-
if (maxWaitTimeout) {
|
131
|
-
clearTimeout(maxWaitTimeout)
|
132
|
-
maxWaitTimeout = null
|
133
|
-
}
|
134
|
-
await processBatch()
|
135
|
-
},
|
136
|
-
}
|
137
|
-
}
|
@@ -42,11 +42,6 @@ export interface LibraryBackend {
|
|
42
42
|
*/
|
43
43
|
watchLibrary(signal?: AbortSignal): AsyncIterable<LibraryUpdate[]>
|
44
44
|
|
45
|
-
/**
|
46
|
-
* Gets the currently loaded resolved unit sources.
|
47
|
-
*/
|
48
|
-
getLoadedResolvedUnitSources(): Promise<ResolvedUnitSource[]>
|
49
|
-
|
50
45
|
/**
|
51
46
|
* Gets the resolved unit sources for the given unit types.
|
52
47
|
*
|
@@ -9,17 +9,15 @@ import {
|
|
9
9
|
type InstanceModel,
|
10
10
|
} from "@highstate/contract"
|
11
11
|
import { unique } from "remeda"
|
12
|
+
import { BetterLock } from "better-lock"
|
12
13
|
import {
|
13
14
|
applyPartialInstanceState,
|
14
|
-
createInputHashResolver,
|
15
|
-
createInputResolver,
|
16
15
|
createInstanceState,
|
17
16
|
createInstanceStatePatch,
|
18
|
-
|
19
|
-
|
20
|
-
type
|
21
|
-
type
|
22
|
-
type InputResolverOutput,
|
17
|
+
InputHashResolver,
|
18
|
+
InputResolver,
|
19
|
+
type InputHashNode,
|
20
|
+
type InputResolverNode,
|
23
21
|
type InstanceState,
|
24
22
|
type InstanceStateUpdate,
|
25
23
|
type InstanceStatus,
|
@@ -44,17 +42,12 @@ export class OperationWorkset {
|
|
44
42
|
|
45
43
|
private readonly unitSourceHashMap = new Map<string, string>()
|
46
44
|
|
47
|
-
private inputResolver!:
|
48
|
-
private readonly
|
49
|
-
private readonly inputResolverPromiseCache = new Map<string, Promise<InputResolverOutput>>()
|
45
|
+
private inputResolver!: InputResolver
|
46
|
+
private readonly inputResolverNodes = new Map<string, InputResolverNode>()
|
50
47
|
|
51
|
-
private inputHashResolver!:
|
52
|
-
private readonly
|
53
|
-
|
54
|
-
private readonly inputHashResolverPromiseCache = new Map<
|
55
|
-
string,
|
56
|
-
Promise<InputHashResolverOutput>
|
57
|
-
>()
|
48
|
+
private inputHashResolver!: InputHashResolver
|
49
|
+
private readonly inputHashNodes = new Map<string, InputHashNode>()
|
50
|
+
private readonly inputHashResolverLock = new BetterLock()
|
58
51
|
|
59
52
|
public readonly resolvedInstanceInputs = new Map<
|
60
53
|
string,
|
@@ -281,7 +274,7 @@ export class OperationWorkset {
|
|
281
274
|
}
|
282
275
|
|
283
276
|
const state = this.stateMap.get(instance.id)
|
284
|
-
const { inputHash: expectedInputHash } =
|
277
|
+
const { inputHash: expectedInputHash } = this.inputHashResolver.requireOutput(instance.id)
|
285
278
|
|
286
279
|
if (this.operation.options.forceUpdateDependencies) {
|
287
280
|
this.instanceIdsToUpdate.add(instanceId)
|
@@ -317,7 +310,7 @@ export class OperationWorkset {
|
|
317
310
|
}
|
318
311
|
|
319
312
|
const state = this.stateMap.get(child.id)
|
320
|
-
const { inputHash: expectedInputHash } =
|
313
|
+
const { inputHash: expectedInputHash } = this.inputHashResolver.requireOutput(child.id)
|
321
314
|
|
322
315
|
if (state?.status !== "created" || state.inputHash !== expectedInputHash) {
|
323
316
|
this.instanceIdsToUpdate.add(child.id)
|
@@ -445,20 +438,23 @@ export class OperationWorkset {
|
|
445
438
|
}
|
446
439
|
|
447
440
|
public async getUpToDateInputHash(instance: InstanceModel): Promise<string> {
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
instance,
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
441
|
+
return await this.inputHashResolverLock.acquire(async () => {
|
442
|
+
const component = this.library.components[instance.type]
|
443
|
+
|
444
|
+
this.inputHashNodes.set(instance.id, {
|
445
|
+
instance,
|
446
|
+
component,
|
447
|
+
resolvedInputs: this.resolvedInstanceInputs.get(instance.id)!,
|
448
|
+
state: this.stateMap.get(instance.id),
|
449
|
+
sourceHash: this.getSourceHashIfApplicable(instance, component),
|
450
|
+
})
|
457
451
|
|
458
|
-
|
452
|
+
this.inputHashResolver.invalidate(instance.id)
|
453
|
+
await this.inputHashResolver.process()
|
459
454
|
|
460
|
-
|
461
|
-
|
455
|
+
const { inputHash } = this.inputHashResolver.requireOutput(instance.id)
|
456
|
+
return inputHash
|
457
|
+
})
|
462
458
|
}
|
463
459
|
|
464
460
|
public getLockInstanceIds(): string[] {
|
@@ -481,10 +477,6 @@ export class OperationWorkset {
|
|
481
477
|
stateBackend.getAllInstanceStates(operation.projectId, signal),
|
482
478
|
])
|
483
479
|
|
484
|
-
const unitSources = await libraryBackend.getResolvedUnitSources(
|
485
|
-
unique(project.instances.map(i => i.type)),
|
486
|
-
)
|
487
|
-
|
488
480
|
const workset = new OperationWorkset(
|
489
481
|
operation,
|
490
482
|
library,
|
@@ -492,10 +484,6 @@ export class OperationWorkset {
|
|
492
484
|
logger.child({ operationId: operation.id, service: "OperationWorkset" }),
|
493
485
|
)
|
494
486
|
|
495
|
-
for (const unitSource of unitSources) {
|
496
|
-
workset.unitSourceHashMap.set(unitSource.unitType, unitSource.sourceHash)
|
497
|
-
}
|
498
|
-
|
499
487
|
// prepare instances
|
500
488
|
for (const instance of project.instances) {
|
501
489
|
workset.addInstance(instance)
|
@@ -523,6 +511,14 @@ export class OperationWorkset {
|
|
523
511
|
}
|
524
512
|
}
|
525
513
|
|
514
|
+
const unitSources = await libraryBackend.getResolvedUnitSources(
|
515
|
+
unique(Array.from(workset.instanceMap.values().map(i => i.type))),
|
516
|
+
)
|
517
|
+
|
518
|
+
for (const unitSource of unitSources) {
|
519
|
+
workset.unitSourceHashMap.set(unitSource.unitType, unitSource.sourceHash)
|
520
|
+
}
|
521
|
+
|
526
522
|
for (const state of states) {
|
527
523
|
if (!workset.instanceMap.has(state.id)) {
|
528
524
|
workset.logger.warn(
|
@@ -536,7 +532,7 @@ export class OperationWorkset {
|
|
536
532
|
|
537
533
|
// prepare input resolver
|
538
534
|
for (const instance of workset.instanceMap.values()) {
|
539
|
-
workset.
|
535
|
+
workset.inputResolverNodes.set(`instance:${instance.id}`, {
|
540
536
|
kind: "instance",
|
541
537
|
instance,
|
542
538
|
component: library.components[instance.type],
|
@@ -544,19 +540,17 @@ export class OperationWorkset {
|
|
544
540
|
}
|
545
541
|
|
546
542
|
for (const hub of project.hubs) {
|
547
|
-
workset.
|
543
|
+
workset.inputResolverNodes.set(`hub:${hub.id}`, { kind: "hub", hub })
|
548
544
|
}
|
549
545
|
|
550
|
-
workset.inputResolver =
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
{ promiseCache: workset.inputResolverPromiseCache },
|
555
|
-
)
|
546
|
+
workset.inputResolver = new InputResolver(workset.inputResolverNodes, logger)
|
547
|
+
workset.inputResolver.addAllNodesToWorkset()
|
548
|
+
|
549
|
+
await workset.inputResolver.process()
|
556
550
|
|
557
551
|
// resolve inputs for all instances and pass outputs to input hash resolver
|
558
552
|
for (const instance of workset.instanceMap.values()) {
|
559
|
-
const output =
|
553
|
+
const output = workset.inputResolver.requireOutput(`instance:${instance.id}`)
|
560
554
|
if (output.kind !== "instance") {
|
561
555
|
throw new Error("Unexpected output kind")
|
562
556
|
}
|
@@ -565,7 +559,7 @@ export class OperationWorkset {
|
|
565
559
|
|
566
560
|
const component = workset.library.components[instance.type]
|
567
561
|
|
568
|
-
workset.
|
562
|
+
workset.inputHashNodes.set(instance.id, {
|
569
563
|
instance,
|
570
564
|
component,
|
571
565
|
resolvedInputs: output.resolvedInputs,
|
@@ -575,12 +569,10 @@ export class OperationWorkset {
|
|
575
569
|
}
|
576
570
|
|
577
571
|
// prepare input hash resolver
|
578
|
-
workset.inputHashResolver =
|
579
|
-
|
580
|
-
|
581
|
-
|
582
|
-
{ promiseCache: workset.inputHashResolverPromiseCache },
|
583
|
-
)
|
572
|
+
workset.inputHashResolver = new InputHashResolver(workset.inputHashNodes, logger)
|
573
|
+
workset.inputHashResolver.addAllNodesToWorkset()
|
574
|
+
|
575
|
+
await workset.inputHashResolver.process()
|
584
576
|
|
585
577
|
if (operation.type !== "destroy") {
|
586
578
|
await workset.calculateInstanceIdsToUpdate()
|
package/src/project/manager.ts
CHANGED
@@ -6,16 +6,16 @@ import type { ProjectLockManager } from "./lock"
|
|
6
6
|
import { EventEmitter, on } from "node:events"
|
7
7
|
import { isUnitModel, type InstanceModel } from "@highstate/contract"
|
8
8
|
import {
|
9
|
-
|
10
|
-
|
11
|
-
type InputResolverInput,
|
12
|
-
type InputHashResolverInput,
|
9
|
+
type InputResolverNode,
|
10
|
+
type InputHashNode,
|
13
11
|
type InstanceModelPatch,
|
14
12
|
type LibraryUpdate,
|
15
13
|
createInstanceState,
|
16
14
|
type CompositeInstance,
|
17
15
|
type ResolvedInstanceInput,
|
18
16
|
type HubModel,
|
17
|
+
InputResolver,
|
18
|
+
InputHashResolver,
|
19
19
|
} from "../shared"
|
20
20
|
|
21
21
|
type CompositeInstanceEvent =
|
@@ -31,6 +31,10 @@ type CompositeInstanceEvent =
|
|
31
31
|
type: "deleted"
|
32
32
|
instanceId: string
|
33
33
|
}
|
34
|
+
| {
|
35
|
+
type: "failed"
|
36
|
+
instanceId: string
|
37
|
+
}
|
34
38
|
|
35
39
|
type CompositeInstanceEvents = {
|
36
40
|
[K in string]: [CompositeInstanceEvent]
|
@@ -98,7 +102,7 @@ export class ProjectManager {
|
|
98
102
|
|
99
103
|
async createInstance(projectId: string, instance: InstanceModel): Promise<InstanceModel> {
|
100
104
|
const createdInstance = await this.projectBackend.createInstance(projectId, instance)
|
101
|
-
await this.
|
105
|
+
await this.evaluateChangedCompositeInstances(projectId)
|
102
106
|
|
103
107
|
return createdInstance
|
104
108
|
}
|
@@ -109,7 +113,7 @@ export class ProjectManager {
|
|
109
113
|
patch: InstanceModelPatch,
|
110
114
|
): Promise<InstanceModel> {
|
111
115
|
const instance = await this.projectBackend.updateInstance(projectId, instanceId, patch)
|
112
|
-
await this.
|
116
|
+
await this.evaluateChangedCompositeInstances(projectId)
|
113
117
|
|
114
118
|
return instance
|
115
119
|
}
|
@@ -120,7 +124,7 @@ export class ProjectManager {
|
|
120
124
|
newName: string,
|
121
125
|
): Promise<InstanceModel> {
|
122
126
|
const instance = await this.projectBackend.renameInstance(projectId, instanceId, newName)
|
123
|
-
await this.
|
127
|
+
await this.evaluateChangedCompositeInstances(projectId)
|
124
128
|
|
125
129
|
return instance
|
126
130
|
}
|
@@ -132,32 +136,22 @@ export class ProjectManager {
|
|
132
136
|
])
|
133
137
|
}
|
134
138
|
|
135
|
-
private async
|
136
|
-
const {
|
137
|
-
|
138
|
-
const component = library.components[instance.type]
|
139
|
-
if (!component) {
|
140
|
-
return
|
141
|
-
}
|
142
|
-
|
143
|
-
if (isUnitModel(component)) {
|
144
|
-
return
|
145
|
-
}
|
146
|
-
|
147
|
-
const { inputHash: expectedInputHash } = await resolveInputHash(instance.id)
|
148
|
-
const inputHash = await this.stateBackend.getCompositeInstanceInputHash(projectId, instance.id)
|
139
|
+
private async evaluateChangedCompositeInstances(projectId: string): Promise<void> {
|
140
|
+
const { inputHashResolver, instances, library, evaluatedInputHashes } =
|
141
|
+
await this.prepareResolvers(projectId)
|
149
142
|
|
150
|
-
|
151
|
-
|
152
|
-
projectId,
|
153
|
-
instanceId: instance.id,
|
154
|
-
})
|
143
|
+
inputHashResolver.addAllNodesToWorkset()
|
144
|
+
await inputHashResolver.process()
|
155
145
|
|
156
|
-
|
157
|
-
|
158
|
-
|
146
|
+
const instanceIds = instances
|
147
|
+
.filter(instance => !isUnitModel(library.components[instance.type]))
|
148
|
+
.filter(
|
149
|
+
instance =>
|
150
|
+
evaluatedInputHashes[instance.id] !==
|
151
|
+
inputHashResolver.requireOutput(instance.id).inputHash,
|
152
|
+
)
|
153
|
+
.map(instance => instance.id)
|
159
154
|
|
160
|
-
private async evaluateCompositeInstances(projectId: string, instanceIds: string[]) {
|
161
155
|
await this.projectLockManager.getLock(projectId).lockInstances(instanceIds, async () => {
|
162
156
|
this.logger.debug({ instanceIds }, "evaluating composite instances")
|
163
157
|
|
@@ -166,10 +160,10 @@ export class ProjectManager {
|
|
166
160
|
}
|
167
161
|
|
168
162
|
const [
|
169
|
-
{ instances, resolvedInputs, stateMap,
|
163
|
+
{ instances, resolvedInputs, stateMap, inputHashResolver },
|
170
164
|
topLevelCompositeChildrenIds,
|
171
165
|
] = await Promise.all([
|
172
|
-
this.
|
166
|
+
this.prepareResolvers(projectId),
|
173
167
|
this.stateBackend.getTopLevelCompositeChildrenIds(projectId, instanceIds),
|
174
168
|
])
|
175
169
|
|
@@ -188,18 +182,19 @@ export class ProjectManager {
|
|
188
182
|
return newState
|
189
183
|
})
|
190
184
|
|
191
|
-
|
192
|
-
|
193
|
-
const { inputHash } = await resolveInputHash(instanceId)
|
194
|
-
inputHashes.set(instanceId, inputHash)
|
195
|
-
}
|
185
|
+
inputHashResolver.addAllNodesToWorkset()
|
186
|
+
await inputHashResolver.process()
|
196
187
|
|
197
188
|
const compositeInstances = results
|
198
189
|
.filter(result => result.success)
|
199
190
|
.flatMap(result =>
|
200
191
|
result.compositeInstances.map(instance => ({
|
201
192
|
...instance,
|
202
|
-
inputHash:
|
193
|
+
inputHash:
|
194
|
+
// only store inputHash for top-level composite instances
|
195
|
+
instance.instance.id === result.instanceId
|
196
|
+
? inputHashResolver.requireOutput(instance.instance.id).inputHash
|
197
|
+
: "",
|
203
198
|
})),
|
204
199
|
)
|
205
200
|
|
@@ -235,6 +230,18 @@ export class ProjectManager {
|
|
235
230
|
|
236
231
|
for (const state of newStates) {
|
237
232
|
this.stateManager.emitStatePatch(projectId, state)
|
233
|
+
|
234
|
+
if (state.evaluationError) {
|
235
|
+
this.logger.error(
|
236
|
+
{ projectId, instanceId: state.id, error: state.evaluationError },
|
237
|
+
"instance evaluation failed",
|
238
|
+
)
|
239
|
+
|
240
|
+
this.compositeInstanceEE.emit(projectId, {
|
241
|
+
type: "failed",
|
242
|
+
instanceId: state.id,
|
243
|
+
})
|
244
|
+
}
|
238
245
|
}
|
239
246
|
|
240
247
|
for (const instance of compositeInstances) {
|
@@ -270,16 +277,18 @@ export class ProjectManager {
|
|
270
277
|
})
|
271
278
|
}
|
272
279
|
|
273
|
-
private async
|
274
|
-
const { instances, hubs } = await
|
280
|
+
private async prepareResolvers(projectId: string) {
|
281
|
+
const [{ instances, hubs }, states, library, evaluatedInputHashes] = await Promise.all([
|
282
|
+
this.projectBackend.getProject(projectId),
|
283
|
+
this.stateBackend.getAllInstanceStates(projectId),
|
284
|
+
this.libraryBackend.loadLibrary(),
|
285
|
+
this.stateBackend.getCompositeInstanceInputHashes(projectId),
|
286
|
+
])
|
275
287
|
|
276
|
-
const library = await this.libraryBackend.loadLibrary()
|
277
288
|
const filteredInstances = instances.filter(instance => instance.type in library.components)
|
278
|
-
|
279
|
-
const states = await this.stateBackend.getAllInstanceStates(projectId)
|
280
289
|
const stateMap = new Map(states.map(state => [state.id, state]))
|
281
290
|
|
282
|
-
const inputResolverNodes = new Map<string,
|
291
|
+
const inputResolverNodes = new Map<string, InputResolverNode>()
|
283
292
|
|
284
293
|
for (const instance of filteredInstances) {
|
285
294
|
inputResolverNodes.set(`instance:${instance.id}`, {
|
@@ -293,13 +302,16 @@ export class ProjectManager {
|
|
293
302
|
inputResolverNodes.set(`hub:${hub.id}`, { kind: "hub", hub })
|
294
303
|
}
|
295
304
|
|
296
|
-
const
|
305
|
+
const inputResolver = new InputResolver(inputResolverNodes, this.logger)
|
306
|
+
inputResolver.addAllNodesToWorkset()
|
297
307
|
|
298
|
-
const inputHashInputs = new Map<string,
|
308
|
+
const inputHashInputs = new Map<string, InputHashNode>()
|
299
309
|
const resolvedInputs: Record<string, Record<string, ResolvedInstanceInput[]>> = {}
|
300
310
|
|
311
|
+
await inputResolver.process()
|
312
|
+
|
301
313
|
for (const instance of filteredInstances) {
|
302
|
-
const output =
|
314
|
+
const output = inputResolver.requireOutput(`instance:${instance.id}`)
|
303
315
|
if (output.kind !== "instance") {
|
304
316
|
throw new Error("Expected instance node")
|
305
317
|
}
|
@@ -328,14 +340,16 @@ export class ProjectManager {
|
|
328
340
|
resolvedInputs[instance.id] = output.resolvedInputs
|
329
341
|
}
|
330
342
|
|
331
|
-
const
|
343
|
+
const inputHashResolver = new InputHashResolver(inputHashInputs, this.logger)
|
332
344
|
|
333
345
|
return {
|
334
|
-
|
346
|
+
inputHashInputs,
|
347
|
+
inputHashResolver,
|
335
348
|
library,
|
336
|
-
instances,
|
349
|
+
instances: filteredInstances,
|
337
350
|
stateMap,
|
338
351
|
resolvedInputs,
|
352
|
+
evaluatedInputHashes,
|
339
353
|
}
|
340
354
|
}
|
341
355
|
|
@@ -377,7 +391,7 @@ export class ProjectManager {
|
|
377
391
|
|
378
392
|
const projects = await this.projectBackend.getProjectIds()
|
379
393
|
for (const projectId of projects) {
|
380
|
-
const {
|
394
|
+
const { instances } = await this.prepareResolvers(projectId)
|
381
395
|
|
382
396
|
const filteredInstances = instances.filter(
|
383
397
|
instance =>
|
@@ -391,17 +405,8 @@ export class ProjectManager {
|
|
391
405
|
"updating composite instances for project",
|
392
406
|
)
|
393
407
|
|
394
|
-
const inputHashMap = new Map<string, string>()
|
395
|
-
for (const instance of filteredInstances) {
|
396
|
-
const { inputHash } = await resolveInputHash(instance.id)
|
397
|
-
inputHashMap.set(instance.id, inputHash)
|
398
|
-
}
|
399
|
-
|
400
408
|
try {
|
401
|
-
await this.
|
402
|
-
projectId,
|
403
|
-
filteredInstances.map(instance => instance.id),
|
404
|
-
)
|
409
|
+
await this.evaluateChangedCompositeInstances(projectId)
|
405
410
|
} catch (error) {
|
406
411
|
this.logger.error({ error }, "failed to evaluate composite instances")
|
407
412
|
}
|