@highstate/backend 0.9.16 → 0.9.18

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.
Files changed (125) hide show
  1. package/dist/chunk-NAAIDR4U.js +8499 -0
  2. package/dist/chunk-NAAIDR4U.js.map +1 -0
  3. package/dist/chunk-OU5OQBLB.js +74 -0
  4. package/dist/chunk-OU5OQBLB.js.map +1 -0
  5. package/dist/{chunk-WHALQHEZ.js → chunk-Y7DXREVO.js} +502 -774
  6. package/dist/chunk-Y7DXREVO.js.map +1 -0
  7. package/dist/highstate.manifest.json +4 -4
  8. package/dist/index.js +2979 -2233
  9. package/dist/index.js.map +1 -1
  10. package/dist/library/package-resolution-worker.js +7 -5
  11. package/dist/library/package-resolution-worker.js.map +1 -1
  12. package/dist/library/worker/main.js +40 -41
  13. package/dist/library/worker/main.js.map +1 -1
  14. package/dist/magic-string.es-5ABAC4JN.js +1292 -0
  15. package/dist/magic-string.es-5ABAC4JN.js.map +1 -0
  16. package/dist/shared/index.js +3 -216
  17. package/dist/shared/index.js.map +1 -1
  18. package/package.json +9 -6
  19. package/src/artifact/encryption.ts +47 -7
  20. package/src/artifact/factory.ts +2 -2
  21. package/src/artifact/local.ts +2 -6
  22. package/src/business/__traces__/secret/update-instance-secrets/create-and-delete-secrets-simultaneously.md +356 -0
  23. package/src/business/__traces__/secret/update-instance-secrets/create-new-secrets-for-instance.md +274 -0
  24. package/src/business/__traces__/secret/update-instance-secrets/delete-existing-secrets.md +223 -0
  25. package/src/business/__traces__/secret/update-instance-secrets/no-op-when-no-changes.md +147 -0
  26. package/src/business/__traces__/secret/update-instance-secrets/update-existing-secrets.md +280 -0
  27. package/src/business/__traces__/worker/update-unit-registrations/add-new-unit-registration-when-other-exists.md +360 -0
  28. package/src/business/__traces__/worker/update-unit-registrations/add-new-unit-registration.md +215 -0
  29. package/src/business/__traces__/worker/update-unit-registrations/create-multiple-workers-with-different-identities.md +427 -0
  30. package/src/business/__traces__/worker/update-unit-registrations/handle-nonexistent-registration-id-gracefully.md +217 -0
  31. package/src/business/__traces__/worker/update-unit-registrations/no-op-when-no-changes.md +132 -0
  32. package/src/business/__traces__/worker/update-unit-registrations/recreate-worker-when-image-changes.md +454 -0
  33. package/src/business/__traces__/worker/update-unit-registrations/recreate-worker-when-image-version-changes.md +426 -0
  34. package/src/business/__traces__/worker/update-unit-registrations/recreate-worker-with-same-identity-reuses-service-account.md +372 -0
  35. package/src/business/__traces__/worker/update-unit-registrations/remove-one-of-multiple-unit-registrations.md +383 -0
  36. package/src/business/__traces__/worker/update-unit-registrations/remove-unit-registration.md +245 -0
  37. package/src/business/__traces__/worker/update-unit-registrations/update-existing-unit-registration-when-params-change.md +174 -0
  38. package/src/business/__traces__/worker/update-unit-registrations/update-params-and-image-simultaneously.md +432 -0
  39. package/src/business/__traces__/worker/update-unit-registrations/worker-with-multiple-registrations-not-deleted-when-one-removed.md +220 -0
  40. package/src/business/artifact.ts +2 -1
  41. package/src/business/index.ts +1 -0
  42. package/src/business/instance-lock.ts +3 -2
  43. package/src/business/instance-state.ts +202 -60
  44. package/src/business/project-unlock.ts +41 -23
  45. package/src/business/project.ts +299 -0
  46. package/src/business/secret.test.ts +178 -0
  47. package/src/business/secret.ts +139 -45
  48. package/src/business/worker.test.ts +614 -0
  49. package/src/business/worker.ts +289 -52
  50. package/src/common/clock.ts +18 -0
  51. package/src/common/index.ts +3 -0
  52. package/src/common/random.ts +68 -0
  53. package/src/common/test/index.ts +2 -0
  54. package/src/common/test/render.ts +98 -0
  55. package/src/common/test/tracer.ts +359 -0
  56. package/src/config.ts +5 -1
  57. package/src/hotstate/manager.ts +8 -8
  58. package/src/hotstate/validation.ts +0 -1
  59. package/src/library/abstractions.ts +20 -11
  60. package/src/library/local.ts +6 -13
  61. package/src/library/worker/evaluator.ts +30 -34
  62. package/src/library/worker/loader.lite.ts +13 -0
  63. package/src/library/worker/main.ts +8 -8
  64. package/src/library/worker/protocol.ts +0 -11
  65. package/src/lock/index.ts +1 -0
  66. package/src/lock/manager.ts +17 -2
  67. package/src/lock/test.ts +108 -0
  68. package/src/orchestrator/manager.ts +17 -36
  69. package/src/orchestrator/operation-workset.ts +34 -37
  70. package/src/orchestrator/operation.ts +129 -74
  71. package/src/project/abstractions.ts +27 -51
  72. package/src/project/evaluation.ts +248 -0
  73. package/src/project/index.ts +1 -1
  74. package/src/project/local.ts +75 -127
  75. package/src/pubsub/manager.ts +21 -13
  76. package/src/runner/abstractions.ts +29 -9
  77. package/src/runner/artifact-env.ts +3 -3
  78. package/src/runner/local.ts +29 -19
  79. package/src/runner/pulumi.ts +4 -1
  80. package/src/services.ts +77 -24
  81. package/src/shared/models/backend/library.ts +4 -4
  82. package/src/shared/models/backend/project.ts +25 -6
  83. package/src/shared/models/backend/unlock-method.ts +1 -1
  84. package/src/shared/models/base.ts +1 -84
  85. package/src/shared/models/project/api-key.ts +5 -2
  86. package/src/shared/models/project/artifact.ts +3 -33
  87. package/src/shared/models/project/index.ts +1 -2
  88. package/src/shared/models/project/lock.ts +3 -3
  89. package/src/shared/models/project/model.ts +14 -0
  90. package/src/shared/models/project/operation.ts +3 -3
  91. package/src/shared/models/project/page.ts +3 -3
  92. package/src/shared/models/project/secret.ts +4 -18
  93. package/src/shared/models/project/service-account.ts +2 -2
  94. package/src/shared/models/project/state.ts +32 -15
  95. package/src/shared/models/project/terminal.ts +4 -5
  96. package/src/shared/models/project/trigger.ts +1 -1
  97. package/src/shared/models/project/unlock-method.ts +9 -2
  98. package/src/shared/models/project/worker.ts +9 -7
  99. package/src/shared/resolvers/graph-resolver.ts +41 -26
  100. package/src/shared/resolvers/input.ts +47 -5
  101. package/src/shared/resolvers/validation.ts +23 -7
  102. package/src/shared/utils/args.ts +25 -0
  103. package/src/shared/utils/index.ts +1 -0
  104. package/src/state/abstractions.ts +98 -259
  105. package/src/state/encryption.ts +39 -0
  106. package/src/state/index.ts +1 -0
  107. package/src/state/local/backend.ts +29 -222
  108. package/src/state/local/collection.ts +105 -86
  109. package/src/state/manager.ts +358 -287
  110. package/src/state/memory/backend.ts +70 -0
  111. package/src/state/memory/collection.ts +270 -0
  112. package/src/state/memory/index.ts +2 -0
  113. package/src/state/repository/repository.index.ts +1 -1
  114. package/src/state/repository/repository.ts +71 -22
  115. package/src/state/test.ts +457 -0
  116. package/src/unlock/abstractions.ts +49 -0
  117. package/src/unlock/index.ts +2 -0
  118. package/src/unlock/memory.ts +32 -0
  119. package/src/worker/manager.ts +28 -0
  120. package/dist/chunk-RCB4AFGD.js +0 -159
  121. package/dist/chunk-RCB4AFGD.js.map +0 -1
  122. package/dist/chunk-WHALQHEZ.js.map +0 -1
  123. package/src/project/manager.ts +0 -574
  124. package/src/shared/models/project/component.ts +0 -45
  125. package/src/shared/models/project/instance.ts +0 -74
@@ -1,574 +0,0 @@
1
- import type { StateManager } from "../state"
2
- import type { ProjectBackend } from "./abstractions"
3
- import type { Logger } from "pino"
4
- import type { InstanceEvaluationResult, LibraryBackend } from "../library"
5
- import type { InstanceLockService } from "../business"
6
- import type { PubSubManager } from "../pubsub"
7
- import { randomUUID } from "node:crypto"
8
- import { isUnitModel, type InstanceModel } from "@highstate/contract"
9
- import {
10
- type InputResolverNode,
11
- type InputHashNode,
12
- type InstanceModelPatch,
13
- type CompositeInstance,
14
- type ResolvedInstanceInput,
15
- type HubModel,
16
- type LibraryUpdate,
17
- InputResolver,
18
- InputHashResolver,
19
- } from "../shared"
20
- import { renderTree, type TreeNode } from "../common"
21
-
22
- export type FullProjectModel = {
23
- libraryId: string
24
- instances: InstanceModel[]
25
- hubs: HubModel[]
26
- compositeInstances: CompositeInstance[]
27
- }
28
-
29
- export class ProjectManager {
30
- private readonly watchedLibraries = new Set<string>()
31
- private readonly libraryWatchers = new Map<string, AbortController>()
32
-
33
- private constructor(
34
- private readonly projectBackend: ProjectBackend,
35
- private readonly libraryBackend: LibraryBackend,
36
- private readonly stateManager: StateManager,
37
- private readonly instanceLockService: InstanceLockService,
38
- private readonly pubsubManager: PubSubManager,
39
- private readonly logger: Logger,
40
- ) {}
41
-
42
- /**
43
- * Loads the full info of a project, including instances, hubs, and composite instances.
44
- *
45
- * Also filters out instances that are not in the library.
46
- *
47
- * @param projectId The ID of the project to load.
48
- */
49
- async getProject(projectId: string): Promise<FullProjectModel> {
50
- const [projectInfo, project] = await Promise.all([
51
- this.projectBackend.getProjectInfo(projectId),
52
- this.projectBackend.getProject(projectId),
53
- ])
54
-
55
- // Ensure we're watching this library for changes
56
- this.ensureLibraryWatched(projectInfo.libraryId)
57
-
58
- const [compositeInstances, library] = await Promise.all([
59
- this.stateManager.getCompositeInstanceRepository(projectId).getAllItems(),
60
- this.libraryBackend.loadLibrary(projectInfo.libraryId),
61
- ])
62
-
63
- const filteredInstances = project.instances.filter(
64
- instance => instance.type in library.components,
65
- )
66
- const filteredCompositeInstances = compositeInstances
67
- .filter(instance => instance.instance.type in library.components)
68
- .map(instance => ({
69
- ...instance,
70
- children: instance.children.filter(child => child.type in library.components),
71
- }))
72
-
73
- return {
74
- libraryId: projectInfo.libraryId,
75
- instances: filteredInstances,
76
- hubs: project.hubs,
77
- compositeInstances: filteredCompositeInstances,
78
- }
79
- }
80
-
81
- async createInstance(projectId: string, instance: InstanceModel): Promise<InstanceModel> {
82
- const createdInstance = await this.projectBackend.createInstance(projectId, instance)
83
- await this.evaluateChangedCompositeInstances(projectId)
84
-
85
- return createdInstance
86
- }
87
-
88
- async updateInstance(
89
- projectId: string,
90
- instanceId: string,
91
- patch: InstanceModelPatch,
92
- ): Promise<InstanceModel> {
93
- const instance = await this.projectBackend.updateInstance(projectId, instanceId, patch)
94
- // await this.evaluateChangedCompositeInstances(projectId)
95
-
96
- return instance
97
- }
98
-
99
- async renameInstance(
100
- projectId: string,
101
- instanceId: string,
102
- newName: string,
103
- ): Promise<InstanceModel> {
104
- const instance = await this.projectBackend.renameInstance(projectId, instanceId, newName)
105
- await this.evaluateChangedCompositeInstances(projectId)
106
-
107
- return instance
108
- }
109
-
110
- async deleteInstance(projectId: string, instanceId: string): Promise<void> {
111
- await Promise.all([
112
- this.projectBackend.deleteInstance(projectId, instanceId),
113
- this.stateManager.getCompositeInstanceRepository(projectId).delete(instanceId),
114
- ])
115
- }
116
-
117
- async createManyNodes(
118
- projectId: string,
119
- instances: InstanceModel[],
120
- hubs: HubModel[],
121
- ): Promise<void> {
122
- await this.projectBackend.createManyNodes(projectId, instances, hubs)
123
- }
124
-
125
- private async evaluateChangedCompositeInstances(projectId: string): Promise<void> {
126
- const { inputHashResolver, instances, library, evaluatedInputHashes } =
127
- await this.prepareResolvers(projectId)
128
-
129
- inputHashResolver.addAllNodesToWorkset()
130
- await inputHashResolver.process()
131
-
132
- const instanceIds = instances
133
- .filter(instance => !isUnitModel(library.components[instance.type]))
134
- .filter(
135
- instance =>
136
- evaluatedInputHashes[instance.id] !==
137
- inputHashResolver.requireOutput(instance.id).inputHash,
138
- )
139
- .map(instance => instance.id)
140
-
141
- await this.instanceLockService.tryLockInstances(
142
- projectId,
143
- instanceIds,
144
- {
145
- title: "Composite Instance Evaluation",
146
- description: "This instance is now being evaluated",
147
- icon: "logos:typescript-icon-round",
148
- },
149
- {
150
- type: "evaluation",
151
- evaluationId: randomUUID(),
152
- },
153
- )
154
-
155
- try {
156
- this.logger.debug({ instanceIds }, "evaluating composite instances")
157
-
158
- for (const instanceId of instanceIds) {
159
- void this.pubsubManager.publish(`instance:state:patch:${projectId}`, {
160
- patch: {
161
- id: instanceId,
162
- evaluationStatus: {
163
- status: "evaluating",
164
- },
165
- },
166
- })
167
- }
168
-
169
- const [
170
- { instances, resolvedInputs, stateMap, inputHashResolver, libraryId },
171
- existingCompositeInstances,
172
- ] = await Promise.all([
173
- this.prepareResolvers(projectId),
174
- this.stateManager.getCompositeInstanceRepository(projectId).getAllRecord(),
175
- ])
176
-
177
- // Get child instance IDs for the instances being re-evaluated
178
- const oldChildInstanceIds = new Set(
179
- existingCompositeInstances
180
- .filter(ci => instanceIds.includes(ci.instance.id))
181
- .flatMap(ci => ci.childCompositeInstanceIds ?? []),
182
- )
183
-
184
- const results = await this.libraryBackend.evaluateCompositeInstances(
185
- libraryId,
186
- instances,
187
- resolvedInputs,
188
- instanceIds,
189
- )
190
-
191
- const newStates = results.map(result => {
192
- const existingState = stateMap.get(result.instanceId)
193
- const newState = existingState ?? { id: result.instanceId }
194
-
195
- newState.evaluationStatus = result.success
196
- ? { status: "evaluated", message: ProjectManager.createSuccessEvaluationMessage(result) }
197
- : { status: "error", message: result.error }
198
-
199
- return newState
200
- })
201
-
202
- inputHashResolver.addAllNodesToWorkset()
203
- await inputHashResolver.process()
204
-
205
- const compositeInstances = results
206
- .filter(result => result.success)
207
- .flatMap(result =>
208
- result.compositeInstances.map(instance => ({
209
- ...instance,
210
- id: instance.instance.id,
211
- inputHash:
212
- // only store inputHash for top-level composite instances
213
- instance.instance.id === result.instanceId
214
- ? inputHashResolver.requireOutput(instance.instance.id).inputHash
215
- : "",
216
- // only store childCompositeInstanceIds for top-level composite instances
217
- childCompositeInstanceIds:
218
- instance.instance.id === result.instanceId
219
- ? result.compositeInstances
220
- .filter(ci => ci.instance.id !== result.instanceId)
221
- .map(ci => ci.instance.id)
222
- : undefined,
223
- })),
224
- )
225
-
226
- const newChildInstanceIds = new Set(
227
- results
228
- .filter(result => result.success)
229
- .flatMap(result =>
230
- result.compositeInstances
231
- .filter(instance => instance.instance.id !== result.instanceId)
232
- .map(instance => instance.instance.id),
233
- ),
234
- )
235
-
236
- const deletedCompositeInstanceIds = new Set<string>()
237
-
238
- // Find child instances that were deleted (existed before but don't exist now)
239
- for (const oldChildId of oldChildInstanceIds) {
240
- if (!newChildInstanceIds.has(oldChildId)) {
241
- deletedCompositeInstanceIds.add(oldChildId)
242
- }
243
- }
244
-
245
- await this.stateManager
246
- .getCompositeInstanceRepository(projectId)
247
- .deleteMany(Array.from(deletedCompositeInstanceIds))
248
-
249
- for (const state of newStates) {
250
- void this.pubsubManager.publish(`instance:state:patch:${projectId}`, { patch: state })
251
-
252
- if (state.evaluationStatus?.status === "error") {
253
- this.logger.error(
254
- { projectId, instanceId: state.id },
255
- `evaluation error for instance "%s": %s`,
256
- state.evaluationStatus.message,
257
- )
258
- }
259
- }
260
-
261
- for (const instance of compositeInstances) {
262
- void this.pubsubManager.publish(`composite-instance:${projectId}`, {
263
- type: "updated",
264
- instance,
265
- })
266
- }
267
-
268
- for (const instanceId of deletedCompositeInstanceIds) {
269
- void this.pubsubManager.publish(`composite-instance:${projectId}`, {
270
- type: "deleted",
271
- instanceId,
272
- })
273
- }
274
-
275
- const promises: Promise<void>[] = []
276
-
277
- if (newStates.length > 0) {
278
- promises.push(this.stateManager.getInstanceStateRepository(projectId).putMany(newStates))
279
- }
280
-
281
- if (compositeInstances.length > 0) {
282
- promises.push(
283
- this.stateManager.getCompositeInstanceRepository(projectId).putMany(compositeInstances),
284
- )
285
- }
286
-
287
- this.logger.info(
288
- { projectId },
289
- "instance evaluation completed, %d instances persisted",
290
- compositeInstances.length,
291
- )
292
-
293
- this.logger.debug(
294
- { compositeInstanceIds: compositeInstances.map(instance => instance.instance.id) },
295
- "persisted composite instances",
296
- )
297
-
298
- await Promise.all(promises)
299
- } finally {
300
- // TODO: implement detecting of lost evaluation locks
301
- await this.instanceLockService.unlockInstancesUnconditionally(projectId, instanceIds)
302
- }
303
- }
304
-
305
- private async prepareResolvers(projectId: string) {
306
- const [projectInfo, { instances, hubs }, states] = await Promise.all([
307
- this.projectBackend.getProjectInfo(projectId),
308
- this.projectBackend.getProject(projectId),
309
- this.stateManager.getInstanceStateRepository(projectId).getAllItems(),
310
- ])
311
-
312
- const library = await this.libraryBackend.loadLibrary(projectInfo.libraryId)
313
-
314
- const filteredInstances = instances.filter(instance => instance.type in library.components)
315
- const stateMap = new Map(states.map(state => [state.id, state]))
316
-
317
- const inputResolverNodes = new Map<string, InputResolverNode>()
318
-
319
- for (const instance of filteredInstances) {
320
- inputResolverNodes.set(`instance:${instance.id}`, {
321
- kind: "instance",
322
- instance,
323
- component: library.components[instance.type],
324
- })
325
- }
326
-
327
- for (const hub of hubs) {
328
- inputResolverNodes.set(`hub:${hub.id}`, { kind: "hub", hub })
329
- }
330
-
331
- const inputResolver = new InputResolver(inputResolverNodes, this.logger)
332
- inputResolver.addAllNodesToWorkset()
333
-
334
- const inputHashInputs = new Map<string, InputHashNode>()
335
- const resolvedInputs: Record<string, Record<string, ResolvedInstanceInput[]>> = {}
336
-
337
- await inputResolver.process()
338
-
339
- for (const instance of filteredInstances) {
340
- const output = inputResolver.requireOutput(`instance:${instance.id}`)
341
- if (output.kind !== "instance") {
342
- throw new Error("Expected instance node")
343
- }
344
-
345
- let sourceHash: string | undefined
346
- if (isUnitModel(library.components[instance.type])) {
347
- const resolvedUnits = await this.libraryBackend.getResolvedUnitSources(
348
- projectInfo.libraryId,
349
- [instance.type],
350
- )
351
- const resolvedUnit = resolvedUnits.find(unit => unit.unitType === instance.type)
352
-
353
- if (resolvedUnit) {
354
- sourceHash = resolvedUnit.sourceHash
355
- } else {
356
- this.logger.warn(`resolved unit source not found for type "%s"`, instance.type)
357
- sourceHash = undefined
358
- }
359
- }
360
-
361
- inputHashInputs.set(instance.id, {
362
- instance,
363
- component: library.components[instance.type],
364
- resolvedInputs: output.resolvedInputs,
365
- state: stateMap.get(instance.id),
366
- sourceHash,
367
- })
368
-
369
- resolvedInputs[instance.id] = output.resolvedInputs
370
- }
371
-
372
- const inputHashResolver = new InputHashResolver(inputHashInputs, this.logger)
373
-
374
- return {
375
- inputHashInputs,
376
- inputHashResolver,
377
- library,
378
- libraryId: projectInfo.libraryId,
379
- instances: filteredInstances,
380
- stateMap,
381
- resolvedInputs,
382
- }
383
- }
384
-
385
- /**
386
- * Ensures a library is being watched for changes.
387
- * Called when a project using this library is accessed.
388
- */
389
- private ensureLibraryWatched(libraryId: string): void {
390
- if (this.watchedLibraries.has(libraryId)) {
391
- return
392
- }
393
-
394
- this.watchedLibraries.add(libraryId)
395
- const abortController = new AbortController()
396
- this.libraryWatchers.set(libraryId, abortController)
397
-
398
- this.logger.debug({ libraryId }, "starting library watcher")
399
-
400
- // Start watching this library in the background
401
- void this.watchSingleLibrary(libraryId, abortController.signal)
402
- }
403
-
404
- /**
405
- * Watches a single library for changes and handles updates.
406
- */
407
- private async watchSingleLibrary(libraryId: string, signal: AbortSignal): Promise<void> {
408
- try {
409
- for await (const updates of this.libraryBackend.watchLibrary(libraryId, signal)) {
410
- try {
411
- await this.handleLibraryUpdates(libraryId, updates)
412
- } catch (error) {
413
- this.logger.error({ error, libraryId }, "failed to handle library updates")
414
- }
415
- }
416
- } catch (error) {
417
- if (signal.aborted) {
418
- this.logger.debug({ libraryId }, "library watcher stopped")
419
- } else {
420
- this.logger.error({ error, libraryId }, "library watcher failed")
421
- }
422
- } finally {
423
- this.watchedLibraries.delete(libraryId)
424
- this.libraryWatchers.delete(libraryId)
425
- }
426
- }
427
-
428
- private async handleLibraryUpdates(libraryId: string, updates: LibraryUpdate[]): Promise<void> {
429
- const changedComponents = new Set<string>()
430
-
431
- for (const update of updates) {
432
- switch (update.type) {
433
- case "component-updated":
434
- changedComponents.add(update.component.type)
435
- break
436
- case "component-removed":
437
- changedComponents.add(update.componentType)
438
- break
439
- // Handle entity updates if needed
440
- case "entity-updated":
441
- case "entity-removed":
442
- // Entity updates don't directly affect composite instance evaluation
443
- break
444
- }
445
- }
446
-
447
- if (changedComponents.size === 0) {
448
- return
449
- }
450
-
451
- this.logger.info(
452
- { libraryId, changedComponents: Array.from(changedComponents) },
453
- "library components changed, updating composite instances for affected projects",
454
- )
455
-
456
- // Get all projects and find those using this libraryId
457
- const allProjectIds = await this.projectBackend.getProjectIds()
458
-
459
- for (const projectId of allProjectIds) {
460
- try {
461
- // Check if this project uses the changed library
462
- const projectInfo = await this.projectBackend.getProjectInfo(projectId)
463
- if (projectInfo.libraryId !== libraryId) {
464
- continue // Skip projects that don't use this library
465
- }
466
-
467
- // Load the updated library for filtering
468
- const library = await this.libraryBackend.loadLibrary(libraryId)
469
- const { instances } = await this.prepareResolvers(projectId)
470
-
471
- const affectedInstances = instances.filter(
472
- instance =>
473
- changedComponents.has(instance.type) &&
474
- library.components[instance.type] &&
475
- !isUnitModel(library.components[instance.type]),
476
- )
477
-
478
- if (affectedInstances.length > 0) {
479
- this.logger.info(
480
- {
481
- projectId,
482
- libraryId,
483
- affectedInstanceIds: affectedInstances.map(instance => instance.id),
484
- },
485
- "updating composite instances for project due to library changes",
486
- )
487
-
488
- await this.evaluateChangedCompositeInstances(projectId)
489
- }
490
- } catch (error) {
491
- this.logger.error(
492
- { error, projectId, libraryId },
493
- "failed to evaluate composite instances for project during library update",
494
- )
495
- }
496
- }
497
- }
498
-
499
- /**
500
- * Cleanup method to stop all library watchers.
501
- * Should be called when the ProjectManager is being shut down.
502
- */
503
- dispose(): void {
504
- for (const [libraryId, abortController] of this.libraryWatchers.entries()) {
505
- this.logger.debug({ libraryId }, "stopping library watcher")
506
- abortController.abort()
507
- }
508
- this.libraryWatchers.clear()
509
- this.watchedLibraries.clear()
510
- }
511
-
512
- static create(
513
- projectBackend: ProjectBackend,
514
- libraryBackend: LibraryBackend,
515
- stateManager: StateManager,
516
- instanceLockService: InstanceLockService,
517
- pubsubManager: PubSubManager,
518
- logger: Logger,
519
- ): ProjectManager {
520
- return new ProjectManager(
521
- projectBackend,
522
- libraryBackend,
523
- stateManager,
524
- instanceLockService,
525
- pubsubManager,
526
- logger.child({ service: "ProjectManager" }),
527
- )
528
- }
529
-
530
- static createSuccessEvaluationMessage(
531
- result: InstanceEvaluationResult & { success: true },
532
- ): string {
533
- const treeNodes = new Map<string, TreeNode>()
534
-
535
- // the order of composite instances are guaranteed to be topologically sorted
536
- for (const compositeInstance of result.compositeInstances) {
537
- const node: TreeNode = {
538
- text: compositeInstance.id,
539
- children: [],
540
- }
541
-
542
- treeNodes.set(compositeInstance.id, node)
543
-
544
- const parentNode = compositeInstance.instance.parentId
545
- ? treeNodes.get(compositeInstance.instance.parentId)
546
- : undefined
547
-
548
- if (parentNode) {
549
- parentNode.children.push(node)
550
- }
551
-
552
- for (const child of compositeInstance.children) {
553
- const childNode: TreeNode = {
554
- text: child.id,
555
- children: [],
556
- }
557
-
558
- node.children.push(childNode)
559
- treeNodes.set(child.id, childNode)
560
- }
561
- }
562
-
563
- // get the root node
564
- const rootNode = treeNodes.get(result.instanceId)
565
- if (!rootNode) {
566
- return `Composite instance evaluation completed successfully, but failed to build the tree structure.`
567
- }
568
-
569
- // render the tree structure
570
- const tree = renderTree(rootNode)
571
-
572
- return `Composite instance evaluation completed successfully.\n\nInstance Tree:\n${tree}`
573
- }
574
- }
@@ -1,45 +0,0 @@
1
- import { z } from "zod"
2
-
3
- export const metaSchema = z.object({
4
- displayName: z.string().optional(),
5
- description: z.string().optional(),
6
- color: z.string().optional(),
7
- })
8
-
9
- export const entitySchema = z.object({
10
- type: z.string(),
11
- schema: z.any(), // TSchema from TypeBox
12
- meta: metaSchema,
13
- definitionHash: z.string(),
14
- })
15
-
16
- export const componentMetaSchema = metaSchema.extend({
17
- primaryIcon: z.string().optional(),
18
- primaryIconColor: z.string().optional(),
19
- secondaryIcon: z.string().optional(),
20
- secondaryIconColor: z.string().optional(),
21
- category: z.string().optional(),
22
- defaultNamePrefix: z.string().optional(),
23
- })
24
-
25
- export const componentArgumentSchema = z.object({
26
- schema: z.any(), // ArgumentValueSchema from TypeBox
27
- required: z.boolean(),
28
- meta: componentMetaSchema,
29
- })
30
-
31
- export const componentInputSchema = z.object({
32
- type: z.string(),
33
- required: z.boolean(),
34
- multiple: z.boolean(),
35
- meta: componentMetaSchema,
36
- })
37
-
38
- export const componentModelSchema = z.object({
39
- type: z.string(),
40
- args: z.record(componentArgumentSchema),
41
- inputs: z.record(componentInputSchema),
42
- outputs: z.record(componentInputSchema),
43
- meta: componentMetaSchema,
44
- definitionHash: z.string(),
45
- })
@@ -1,74 +0,0 @@
1
- import { z } from "zod"
2
-
3
- export const positionSchema = z.object({
4
- x: z.number(),
5
- y: z.number(),
6
- })
7
-
8
- export const instanceInputSchema = z.object({
9
- instanceId: z.string(),
10
- output: z.string(),
11
- })
12
-
13
- export const hubInstanceInputSchema = z.object({
14
- hubId: z.string(),
15
- })
16
-
17
- export const instanceModelPatchSchema = z.object({
18
- args: z.record(z.unknown()).optional(),
19
- inputs: z.record(z.array(instanceInputSchema)).optional(),
20
- hubInputs: z.record(z.array(hubInstanceInputSchema)).optional(),
21
- injectionInputs: z.array(hubInstanceInputSchema).optional(),
22
- position: positionSchema.optional(),
23
- })
24
-
25
- export const instanceModelSchema = z.object({
26
- id: z.string(),
27
- type: z.string(),
28
- name: z.string(),
29
-
30
- ...instanceModelPatchSchema.shape,
31
- resolvedInputs: z.record(z.array(instanceInputSchema)).optional(),
32
-
33
- parentId: z.string().optional(),
34
- outputs: z.record(z.array(instanceInputSchema)).optional(),
35
- resolvedOutputs: z.record(z.array(instanceInputSchema)).optional(),
36
- })
37
-
38
- export const compositeInstanceSchema = z.object({
39
- instance: instanceModelSchema,
40
- children: z.array(instanceModelSchema),
41
- childCompositeInstanceIds: z.array(z.string()).optional(),
42
- inputHash: z.string().optional(),
43
- })
44
-
45
- export type CompositeInstance = z.infer<typeof compositeInstanceSchema>
46
-
47
- export const compositeInstanceEventSchema = z.discriminatedUnion("type", [
48
- z.object({
49
- type: z.literal("updated"),
50
- instance: compositeInstanceSchema,
51
- }),
52
- z.object({
53
- type: z.literal("deleted"),
54
- instanceId: z.string(),
55
- }),
56
- ])
57
-
58
- export const hubModelPatchSchema = z.object({
59
- position: positionSchema.optional(),
60
- inputs: z.array(instanceInputSchema).optional(),
61
- injectionInputs: z.array(hubInstanceInputSchema).optional(),
62
- })
63
-
64
- export const hubModelSchema = z.object({
65
- id: z.string().nanoid(),
66
- position: positionSchema,
67
-
68
- inputs: z.array(instanceInputSchema).optional(),
69
- injectionInputs: z.array(hubInstanceInputSchema).optional(),
70
- })
71
-
72
- export type InstanceModelPatch = z.infer<typeof instanceModelPatchSchema>
73
- export type HubModel = z.infer<typeof hubModelSchema>
74
- export type HubModelPatch = z.infer<typeof hubModelPatchSchema>