@highstate/backend 0.9.9 → 0.9.11

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.
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  errorToString
3
- } from "../../chunk-EQ4LMS7B.js";
3
+ } from "../../chunk-WXDYCRTT.js";
4
4
 
5
5
  // src/library/worker/main.ts
6
6
  import { parentPort, workerData } from "node:worker_threads";
@@ -1,16 +1,15 @@
1
1
  import {
2
- CircularDependencyError,
2
+ GraphResolver,
3
+ InputHashResolver,
4
+ InputResolver,
5
+ ValidationResolver,
3
6
  applyLibraryUpdate,
4
7
  applyPartialInstanceState,
5
8
  buildDependentInstanceStateMap,
6
9
  compositeInstanceSchema,
7
- createDefaultGraphResolverBackend,
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-NMGIUI6X.js";
50
+ } from "../chunk-DQDXRDUA.js";
52
51
  export {
53
- CircularDependencyError,
52
+ GraphResolver,
53
+ InputHashResolver,
54
+ InputResolver,
55
+ ValidationResolver,
54
56
  applyLibraryUpdate,
55
57
  applyPartialInstanceState,
56
58
  buildDependentInstanceStateMap,
57
59
  compositeInstanceSchema,
58
- createDefaultGraphResolverBackend,
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.9",
3
+ "version": "0.9.11",
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.9",
29
+ "@highstate/contract": "^0.9.11",
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": "1c04a713fa1bb7c0231e5e6d0537709097bb0e8e"
68
+ "gitHead": "d62cae30ea9fadaa1aa4fb3b5734a16cf53810ce"
69
69
  }
@@ -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
- type GraphResolver,
19
- type InputHashResolverInput,
20
- type InputHashResolverOutput,
21
- type InputResolverInput,
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!: GraphResolver<InputResolverOutput>
48
- private readonly inputResolverInputs = new Map<string, InputResolverInput>()
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!: GraphResolver<InputHashResolverOutput>
52
- private readonly inputHashResolverInputs = new Map<string, InputHashResolverInput>()
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 } = await this.inputHashResolver(instance.id)
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 } = await this.inputHashResolver(child.id)
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
- const component = this.library.components[instance.type]
449
-
450
- this.inputHashResolverInputs.set(instance.id, {
451
- instance,
452
- component,
453
- resolvedInputs: this.resolvedInstanceInputs.get(instance.id)!,
454
- state: this.stateMap.get(instance.id),
455
- sourceHash: this.getSourceHashIfApplicable(instance, component),
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
- this.inputHashResolverPromiseCache.delete(instance.id)
452
+ this.inputHashResolver.invalidate(instance.id)
453
+ await this.inputHashResolver.process()
459
454
 
460
- const { inputHash } = await this.inputHashResolver(instance.id)
461
- return inputHash
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.inputResolverInputs.set(`instance:${instance.id}`, {
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.inputResolverInputs.set(`hub:${hub.id}`, { kind: "hub", hub })
543
+ workset.inputResolverNodes.set(`hub:${hub.id}`, { kind: "hub", hub })
548
544
  }
549
545
 
550
- workset.inputResolver = createInputResolver(
551
- //
552
- workset.inputResolverInputs,
553
- logger,
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 = await workset.inputResolver(`instance:${instance.id}`)
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.inputHashResolverInputs.set(instance.id, {
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 = createInputHashResolver(
579
- //
580
- workset.inputHashResolverInputs,
581
- logger,
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()
@@ -14,9 +14,9 @@ import {
14
14
  type ProjectOperation,
15
15
  type InstanceTriggerInvocation,
16
16
  createInstanceState,
17
+ createAsyncBatcher,
17
18
  } from "../shared"
18
19
  import {
19
- createAsyncBatcher,
20
20
  errorToString,
21
21
  isAbortError,
22
22
  isAbortErrorLike,
@@ -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
- createInputResolver,
10
- createInputHashResolver,
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.updateCompositeInstance(projectId, createdInstance)
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.updateCompositeInstance(projectId, instance)
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.updateCompositeInstance(projectId, instance)
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 updateCompositeInstance(projectId: string, instance: InstanceModel): Promise<void> {
136
- const { resolveInputHash, library } = await this.prepareInputHashResolver(projectId)
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
- if (inputHash !== expectedInputHash) {
151
- this.logger.info("re-evaluating instance since input hash has changed", {
152
- projectId,
153
- instanceId: instance.id,
154
- })
143
+ inputHashResolver.addAllNodesToWorkset()
144
+ await inputHashResolver.process()
155
145
 
156
- await this.evaluateCompositeInstances(projectId, [instance.id])
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, resolveInputHash },
163
+ { instances, resolvedInputs, stateMap, inputHashResolver },
170
164
  topLevelCompositeChildrenIds,
171
165
  ] = await Promise.all([
172
- this.prepareInputHashResolver(projectId),
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
- const inputHashes = new Map<string, string>()
192
- for (const instanceId of instanceIds) {
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: inputHashes.get(instance.instance.id),
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 prepareInputHashResolver(projectId: string) {
274
- const { instances, hubs } = await this.projectBackend.getProject(projectId)
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, InputResolverInput>()
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 resolveInputs = createInputResolver(inputResolverNodes, this.logger)
305
+ const inputResolver = new InputResolver(inputResolverNodes, this.logger)
306
+ inputResolver.addAllNodesToWorkset()
297
307
 
298
- const inputHashInputs = new Map<string, InputHashResolverInput>()
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 = await resolveInputs(`instance:${instance.id}`)
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 resolveInputHash = createInputHashResolver(inputHashInputs, this.logger)
343
+ const inputHashResolver = new InputHashResolver(inputHashInputs, this.logger)
332
344
 
333
345
  return {
334
- resolveInputHash,
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 { resolveInputHash, instances } = await this.prepareInputHashResolver(projectId)
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.evaluateCompositeInstances(
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
  }