@highstate/backend 0.7.1 → 0.7.3

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 (74) hide show
  1. package/dist/{index.mjs → index.js} +1255 -916
  2. package/dist/library/source-resolution-worker.js +55 -0
  3. package/dist/library/worker/main.js +207 -0
  4. package/dist/{terminal-CqIsctlZ.mjs → library-BW5oPM7V.js} +210 -87
  5. package/dist/shared/index.js +6 -0
  6. package/dist/utils-ByadNcv4.js +102 -0
  7. package/package.json +14 -18
  8. package/src/common/index.ts +3 -0
  9. package/src/common/local.ts +22 -0
  10. package/src/common/pulumi.ts +230 -0
  11. package/src/common/utils.ts +137 -0
  12. package/src/config.ts +40 -0
  13. package/src/index.ts +6 -0
  14. package/src/library/abstractions.ts +83 -0
  15. package/src/library/factory.ts +20 -0
  16. package/src/library/index.ts +2 -0
  17. package/src/library/local.ts +404 -0
  18. package/src/library/source-resolution-worker.ts +96 -0
  19. package/src/library/worker/evaluator.ts +119 -0
  20. package/src/library/worker/loader.ts +93 -0
  21. package/src/library/worker/main.ts +82 -0
  22. package/src/library/worker/protocol.ts +38 -0
  23. package/src/orchestrator/index.ts +1 -0
  24. package/src/orchestrator/manager.ts +165 -0
  25. package/src/orchestrator/operation-workset.ts +483 -0
  26. package/src/orchestrator/operation.ts +647 -0
  27. package/src/preferences/shared.ts +1 -0
  28. package/src/project/abstractions.ts +89 -0
  29. package/src/project/factory.ts +11 -0
  30. package/src/project/index.ts +4 -0
  31. package/src/project/local.ts +412 -0
  32. package/src/project/lock.ts +39 -0
  33. package/src/project/manager.ts +374 -0
  34. package/src/runner/abstractions.ts +146 -0
  35. package/src/runner/factory.ts +22 -0
  36. package/src/runner/index.ts +2 -0
  37. package/src/runner/local.ts +698 -0
  38. package/src/secret/abstractions.ts +59 -0
  39. package/src/secret/factory.ts +22 -0
  40. package/src/secret/index.ts +2 -0
  41. package/src/secret/local.ts +152 -0
  42. package/src/services.ts +133 -0
  43. package/src/shared/index.ts +10 -0
  44. package/src/shared/library.ts +77 -0
  45. package/src/shared/operation.ts +85 -0
  46. package/src/shared/project.ts +62 -0
  47. package/src/shared/resolvers/graph-resolver.ts +111 -0
  48. package/src/shared/resolvers/input-hash.ts +77 -0
  49. package/src/shared/resolvers/input.ts +314 -0
  50. package/src/shared/resolvers/registry.ts +10 -0
  51. package/src/shared/resolvers/validation.ts +94 -0
  52. package/src/shared/state.ts +262 -0
  53. package/src/shared/terminal.ts +13 -0
  54. package/src/state/abstractions.ts +222 -0
  55. package/src/state/factory.ts +22 -0
  56. package/src/state/index.ts +3 -0
  57. package/src/state/local.ts +605 -0
  58. package/src/state/manager.ts +33 -0
  59. package/src/terminal/docker.ts +90 -0
  60. package/src/terminal/factory.ts +20 -0
  61. package/src/terminal/index.ts +3 -0
  62. package/src/terminal/manager.ts +330 -0
  63. package/src/terminal/run.sh.ts +37 -0
  64. package/src/terminal/shared.ts +50 -0
  65. package/src/workspace/abstractions.ts +41 -0
  66. package/src/workspace/factory.ts +14 -0
  67. package/src/workspace/index.ts +2 -0
  68. package/src/workspace/local.ts +54 -0
  69. package/dist/index.d.ts +0 -760
  70. package/dist/library/worker/main.mjs +0 -164
  71. package/dist/runner/source-resolution-worker.mjs +0 -22
  72. package/dist/shared/index.d.ts +0 -85
  73. package/dist/shared/index.mjs +0 -54
  74. package/dist/terminal-Cm2WqcyB.d.ts +0 -1589
@@ -0,0 +1,111 @@
1
+ import type { Logger } from "pino"
2
+ import { unique } from "remeda"
3
+
4
+ export type GraphResolverOptions<TNode, TOutput> = {
5
+ name: string
6
+
7
+ getNodeId(input: TNode): string
8
+
9
+ getNodeDependencies(input: TNode): string[]
10
+
11
+ process(
12
+ node: TNode,
13
+ dependencies: ReadonlyMap<string, TOutput>,
14
+ logger: Logger,
15
+ ): TOutput | Promise<TOutput>
16
+ }
17
+
18
+ export interface GraphResolverBackend<TOutput> {
19
+ promiseCache: Map<string, Promise<TOutput>>
20
+
21
+ setOutput?(id: string, value: TOutput): void
22
+ setDependencies?(id: string, dependencies: string[]): void
23
+ }
24
+
25
+ export type GraphResolverFactory<TNode, TOutput> = {
26
+ factoryName: string
27
+ } & ((
28
+ nodes: ReadonlyMap<string, TNode>,
29
+ logger: Logger,
30
+ backend?: GraphResolverBackend<TOutput>,
31
+ ) => GraphResolver<TOutput>)
32
+
33
+ export type GraphResolver<TOutput> = (id: string) => Promise<TOutput>
34
+
35
+ export class CircularDependencyError extends Error {
36
+ constructor(path: string[]) {
37
+ super(`Circular dependency detected: ${path.join(" -> ")}`)
38
+ this.name = "CircularDependencyError"
39
+ }
40
+ }
41
+
42
+ export function createDefaultGraphResolverBackend<TOutput>(): GraphResolverBackend<TOutput> {
43
+ const promiseCache = new Map<string, Promise<TOutput>>()
44
+
45
+ return {
46
+ promiseCache,
47
+ }
48
+ }
49
+
50
+ export function defineGraphResolver<TInput, TOutput>(
51
+ options: GraphResolverOptions<TInput, TOutput>,
52
+ ): GraphResolverFactory<TInput, TOutput> {
53
+ const factory: GraphResolverFactory<TInput, TOutput> = (nodes, logger, backend) => {
54
+ backend ??= createDefaultGraphResolverBackend<TOutput>()
55
+ logger = logger.child({ resolver: options.name })
56
+
57
+ const outputs = new Map<string, TOutput>()
58
+
59
+ const resolver = (itemId: string, dependencyChain: string[]) => {
60
+ logger.trace({ itemId }, "resolving item")
61
+
62
+ const existingPromise = backend.promiseCache.get(itemId)
63
+ if (existingPromise) {
64
+ return existingPromise
65
+ }
66
+
67
+ if (dependencyChain.includes(itemId)) {
68
+ throw new CircularDependencyError([...dependencyChain, itemId])
69
+ }
70
+
71
+ const item = nodes.get(itemId)
72
+ if (!item) {
73
+ return Promise.resolve(undefined!)
74
+ }
75
+
76
+ const resolve = async () => {
77
+ const dependencies = unique(options.getNodeDependencies(item))
78
+
79
+ backend.setDependencies?.(itemId, dependencies)
80
+ logger.trace({ itemId, dependencies }, "resolving item dependencies")
81
+
82
+ const newChain = [...dependencyChain, itemId]
83
+
84
+ for (const depId of dependencies) {
85
+ await resolver(depId, newChain)
86
+ }
87
+
88
+ return await options.process(item, outputs, logger)
89
+ }
90
+
91
+ const promise = resolve().then(result => {
92
+ if (backend.promiseCache.get(itemId) === promise) {
93
+ // persist the result to the state if provided
94
+ // ignore if the promise has been replaced (which means this calculation was invalidated)
95
+ backend.setOutput?.(itemId, result)
96
+ outputs.set(itemId, result)
97
+ }
98
+
99
+ return result
100
+ })
101
+
102
+ backend.promiseCache.set(itemId, promise)
103
+ return promise
104
+ }
105
+
106
+ return id => resolver(id, [])
107
+ }
108
+
109
+ factory.factoryName = options.name
110
+ return factory
111
+ }
@@ -0,0 +1,77 @@
1
+ import type { ComponentModel, InstanceModel } from "@highstate/contract"
2
+ import type { InstanceState } from "../state"
3
+ import type { ResolvedInstanceInput } from "./input"
4
+ import { sha256 } from "crypto-hash"
5
+ import { defineGraphResolver } from "./graph-resolver"
6
+
7
+ export type InputHashResolverInput = {
8
+ instance: InstanceModel
9
+ component: ComponentModel
10
+ resolvedInputs: Record<string, ResolvedInstanceInput[]>
11
+ state: InstanceState | undefined
12
+ sourceHash: string | undefined
13
+ }
14
+
15
+ export type InputHashResolverOutput = {
16
+ inputHash: string
17
+ outputHash: string
18
+ }
19
+
20
+ /**
21
+ * Resolves the hash of the instance based on its args, resolved input hashes, source hash, and the output hash.
22
+ */
23
+ export const createInputHashResolver = defineGraphResolver<
24
+ InputHashResolverInput,
25
+ InputHashResolverOutput
26
+ >({
27
+ name: "input-hash-resolver",
28
+ getNodeId: node => node.instance.id,
29
+
30
+ getNodeDependencies({ resolvedInputs }) {
31
+ const dependencies: string[] = []
32
+
33
+ for (const inputs of Object.values(resolvedInputs ?? {})) {
34
+ for (const input of inputs) {
35
+ dependencies.push(input.input.instanceId)
36
+ }
37
+ }
38
+
39
+ return dependencies
40
+ },
41
+
42
+ async process({ instance, component, resolvedInputs, sourceHash, state }, dependencies) {
43
+ let sink = component.definitionHash + JSON.stringify(instance.args ?? {})
44
+
45
+ if (sourceHash) {
46
+ sink += sourceHash
47
+ }
48
+
49
+ const sortedInputs = Object.entries(resolvedInputs)
50
+ //
51
+ .sort(([a], [b]) => a.localeCompare(b))
52
+
53
+ for (const [inputKey, inputs] of sortedInputs) {
54
+ if (Object.keys(inputs).length === 0) {
55
+ continue
56
+ }
57
+
58
+ sink += inputKey
59
+
60
+ const instanceIds = inputs.map(input => input.input.instanceId)
61
+ instanceIds.sort()
62
+
63
+ for (const instanceId of instanceIds) {
64
+ const dependency = dependencies.get(instanceId)
65
+ if (!dependency) continue
66
+
67
+ sink += dependency.inputHash
68
+ sink += dependency.outputHash
69
+ }
70
+ }
71
+
72
+ return {
73
+ inputHash: await sha256(sink),
74
+ outputHash: state?.outputHash ?? "",
75
+ }
76
+ },
77
+ })
@@ -0,0 +1,314 @@
1
+ import type { HubModel } from "../project"
2
+ import {
3
+ isUnitModel,
4
+ type ComponentModel,
5
+ type HubInput,
6
+ type InstanceInput,
7
+ type InstanceModel,
8
+ } from "@highstate/contract"
9
+ import { fromEntries, mapValues } from "remeda"
10
+ import { defineGraphResolver } from "./graph-resolver"
11
+
12
+ export type InputResolverInput =
13
+ | {
14
+ kind: "instance"
15
+ instance: InstanceModel
16
+ component: ComponentModel
17
+ }
18
+ | {
19
+ kind: "hub"
20
+ hub: HubModel
21
+ }
22
+
23
+ export type ResolvedInstanceInput = {
24
+ input: InstanceInput
25
+ type: string
26
+ }
27
+
28
+ export type InputResolverOutput =
29
+ | {
30
+ kind: "instance"
31
+ component: ComponentModel
32
+ resolvedInputs: Record<string, ResolvedInstanceInput[]>
33
+ resolvedOutputs: Record<string, InstanceInput[]>
34
+ resolvedInjectionInputs: ResolvedInstanceInput[]
35
+ matchedInjectionInputs: ResolvedInstanceInput[]
36
+ }
37
+ | {
38
+ kind: "hub"
39
+ resolvedInputs: ResolvedInstanceInput[]
40
+ }
41
+
42
+ /**
43
+ * Resolves the all recursive instance and hub inputs based on its direct inputs and injected inputs.
44
+ */
45
+ export const createInputResolver = defineGraphResolver<InputResolverInput, InputResolverOutput>({
46
+ name: "input-resolver",
47
+ getNodeId(node) {
48
+ if (node.kind === "hub") {
49
+ return `hub:${node.hub.id}`
50
+ }
51
+
52
+ return `instance:${node.instance.id}`
53
+ },
54
+
55
+ getNodeDependencies(node) {
56
+ const dependencies: string[] = []
57
+
58
+ if (node.kind === "hub") {
59
+ for (const input of node.hub.inputs ?? []) {
60
+ dependencies.push(`instance:${input.instanceId}`)
61
+ }
62
+
63
+ for (const input of node.hub.injectionInputs ?? []) {
64
+ dependencies.push(`hub:${input.hubId}`)
65
+ }
66
+
67
+ return dependencies
68
+ }
69
+
70
+ for (const inputs of Object.values(node.instance.inputs ?? {})) {
71
+ for (const input of inputs) {
72
+ dependencies.push(`instance:${input.instanceId}`)
73
+ }
74
+ }
75
+
76
+ for (const inputs of Object.values(node.instance.hubInputs ?? {})) {
77
+ for (const input of inputs) {
78
+ dependencies.push(`hub:${input.hubId}`)
79
+ }
80
+ }
81
+
82
+ for (const input of node.instance.injectionInputs ?? []) {
83
+ dependencies.push(`hub:${input.hubId}`)
84
+ }
85
+
86
+ return dependencies
87
+ },
88
+
89
+ process(node, dependencies, logger) {
90
+ const getHubOutput = (input: HubInput) => {
91
+ const output = dependencies.get(`hub:${input.hubId}`)
92
+ if (!output) {
93
+ return { resolvedInputs: [] }
94
+ }
95
+
96
+ if (output.kind !== "hub") {
97
+ throw new Error("Expected hub node")
98
+ }
99
+
100
+ return output
101
+ }
102
+
103
+ const getInstanceOutput = (input: InstanceInput) => {
104
+ const output = dependencies.get(`instance:${input.instanceId}`)
105
+ if (!output) {
106
+ return {
107
+ component: null,
108
+ resolvedInputs: [] as ResolvedInstanceInput[],
109
+ resolvedOutputs: [] as InstanceInput[],
110
+ }
111
+ }
112
+
113
+ if (output.kind !== "instance") {
114
+ throw new Error("Expected instance node")
115
+ }
116
+
117
+ return {
118
+ component: output.component,
119
+ resolvedInputs: output.resolvedInputs[input.output] ?? [],
120
+ resolvedOutputs: output.resolvedOutputs[input.output] ?? [],
121
+ }
122
+ }
123
+
124
+ // resolve inputs for hub
125
+
126
+ if (node.kind === "hub") {
127
+ const hubResult: Map<string, ResolvedInstanceInput> = new Map()
128
+
129
+ const addHubResult = (input: ResolvedInstanceInput) => {
130
+ hubResult.set(`${input.input.instanceId}:${input.input.output}`, input)
131
+ }
132
+
133
+ for (const input of node.hub.inputs ?? []) {
134
+ const { component } = getInstanceOutput(input)
135
+ const componentInput = component?.outputs[input.output]
136
+
137
+ if (!componentInput) {
138
+ logger.warn({ msg: "output not found in the component", input, component })
139
+ continue
140
+ }
141
+
142
+ addHubResult({ input, type: componentInput.type })
143
+ }
144
+
145
+ for (const injectionInput of node.hub.injectionInputs ?? []) {
146
+ const { resolvedInputs } = getHubOutput(injectionInput)
147
+
148
+ for (const input of resolvedInputs) {
149
+ addHubResult(input)
150
+ }
151
+ }
152
+
153
+ return {
154
+ kind: "hub",
155
+ resolvedInputs: Array.from(hubResult.values()),
156
+ }
157
+ }
158
+
159
+ // хуяк (which translates as "lets reuse resolved inputs of level 2+ composite instances provided by the evaluator")
160
+ if (node.instance.resolvedInputs) {
161
+ return {
162
+ kind: "instance",
163
+ component: node.component,
164
+ resolvedInputs: mapValues(node.instance.resolvedInputs, (inputs, inputName) => {
165
+ const componentInput = node.component.inputs[inputName]
166
+ if (!componentInput) {
167
+ logger.warn({
168
+ msg: "input not found in the component",
169
+ inputName,
170
+ component: node.component,
171
+ })
172
+ return []
173
+ }
174
+
175
+ return inputs.map(input => ({ input, type: componentInput.type }))
176
+ }),
177
+ resolvedOutputs: node.instance.resolvedOutputs ?? {},
178
+ resolvedInjectionInputs: [],
179
+ matchedInjectionInputs: [],
180
+ }
181
+ }
182
+
183
+ // resolve inputs for instance
184
+
185
+ const resolvedInputsMap: Map<string, Map<string, ResolvedInstanceInput>> = new Map()
186
+
187
+ const addInstanceResult = (inputName: string, input: ResolvedInstanceInput) => {
188
+ let inputs = resolvedInputsMap.get(inputName)
189
+ if (!inputs) {
190
+ inputs = new Map()
191
+ resolvedInputsMap.set(inputName, inputs)
192
+ }
193
+
194
+ inputs.set(`${input.input.instanceId}:${input.input.output}`, input)
195
+ }
196
+
197
+ const addInstanceInput = (inputName: string, input: InstanceInput) => {
198
+ const componentInput = node.component.inputs[inputName]
199
+ if (!componentInput) {
200
+ logger.warn({ msg: "input not found in the component", input, component: node.component })
201
+ return
202
+ }
203
+
204
+ const { component, resolvedOutputs } = getInstanceOutput(input)
205
+
206
+ if (!component) {
207
+ logger.warn({ instanceId: node.instance.id, input }, "no output found for the input")
208
+ return
209
+ }
210
+
211
+ if (isUnitModel(component)) {
212
+ addInstanceResult(inputName, { input, type: componentInput.type })
213
+ return
214
+ }
215
+
216
+ for (const output of resolvedOutputs) {
217
+ addInstanceResult(inputName, { input: output, type: componentInput.type })
218
+ }
219
+ }
220
+
221
+ for (const [inputName, inputs] of Object.entries(node.instance.inputs ?? {})) {
222
+ for (const input of inputs) {
223
+ addInstanceInput(inputName, input)
224
+ }
225
+ }
226
+
227
+ const injectionInputs: Map<string, ResolvedInstanceInput> = new Map()
228
+ const matchedInjectionInputs: Map<string, ResolvedInstanceInput> = new Map()
229
+
230
+ for (const injectionInput of node.instance.injectionInputs ?? []) {
231
+ const { resolvedInputs } = getHubOutput(injectionInput)
232
+ for (const input of resolvedInputs) {
233
+ injectionInputs.set(`${input.input.instanceId}:${input.input.output}`, input)
234
+ }
235
+ }
236
+
237
+ for (const [inputName, componentInput] of Object.entries(node.component.inputs ?? {})) {
238
+ const allInputs = new Map<string, ResolvedInstanceInput>(injectionInputs)
239
+ const hubInputs = node.instance.hubInputs?.[inputName] ?? []
240
+
241
+ for (const hubInput of hubInputs) {
242
+ const { resolvedInputs } = getHubOutput(hubInput)
243
+ for (const input of resolvedInputs) {
244
+ allInputs.set(`${input.input.instanceId}:${input.input.output}`, input)
245
+ }
246
+ }
247
+
248
+ for (const input of allInputs.values()) {
249
+ if (input.type === componentInput.type) {
250
+ addInstanceInput(inputName, input.input)
251
+
252
+ const key = `${input.input.instanceId}:${input.input.output}`
253
+ if (injectionInputs.has(key)) {
254
+ matchedInjectionInputs.set(key, input)
255
+ }
256
+ }
257
+ }
258
+ }
259
+
260
+ const resolvedInputs = fromEntries(
261
+ Array.from(resolvedInputsMap.entries()).map(([inputName, inputs]) => [
262
+ inputName,
263
+ Array.from(inputs.values()),
264
+ ]),
265
+ )
266
+
267
+ return {
268
+ kind: "instance",
269
+ component: node.component,
270
+ resolvedInputs,
271
+ resolvedOutputs: node.instance.resolvedOutputs ?? {},
272
+ resolvedInjectionInputs: Array.from(injectionInputs.values()),
273
+ matchedInjectionInputs: Array.from(matchedInjectionInputs.values()),
274
+ }
275
+ },
276
+ })
277
+
278
+ export function getResolvedHubInputs(output: InputResolverOutput): ResolvedInstanceInput[] {
279
+ if (output.kind !== "hub") {
280
+ throw new Error("Expected hub node")
281
+ }
282
+
283
+ return output.resolvedInputs
284
+ }
285
+
286
+ export function getResolvedInstanceInputs(
287
+ output: InputResolverOutput,
288
+ ): Record<string, ResolvedInstanceInput[]> {
289
+ if (output.kind !== "instance") {
290
+ throw new Error("Expected instance node")
291
+ }
292
+
293
+ return output.resolvedInputs
294
+ }
295
+
296
+ export function getResolvedInjectionInstanceInputs(
297
+ output: InputResolverOutput,
298
+ ): ResolvedInstanceInput[] {
299
+ if (output.kind !== "instance") {
300
+ throw new Error("Expected instance node")
301
+ }
302
+
303
+ return output.resolvedInjectionInputs
304
+ }
305
+
306
+ export function getMatchedInjectionInstanceInputs(
307
+ output: InputResolverOutput,
308
+ ): ResolvedInstanceInput[] {
309
+ if (output.kind !== "instance") {
310
+ throw new Error("Expected instance node")
311
+ }
312
+
313
+ return output.matchedInjectionInputs
314
+ }
@@ -0,0 +1,10 @@
1
+ import type { GraphResolverFactory } from "./graph-resolver"
2
+ import { createInputResolver } from "./input"
3
+ import { createInputHashResolver } from "./input-hash"
4
+ import { createValidationResolver } from "./validation"
5
+
6
+ export const resolverFactories: Record<string, GraphResolverFactory<any, any>> = {
7
+ [createInputResolver.factoryName]: createInputResolver,
8
+ [createInputHashResolver.factoryName]: createInputHashResolver,
9
+ [createValidationResolver.factoryName]: createValidationResolver,
10
+ }
@@ -0,0 +1,94 @@
1
+ import type { ComponentModel, InstanceModel } from "@highstate/contract"
2
+ import type { ResolvedInstanceInput } from "./input"
3
+ import { Ajv } from "ajv"
4
+ import { defineGraphResolver } from "./graph-resolver"
5
+
6
+ export type ValidationResolverInput = {
7
+ instance: InstanceModel
8
+ component: ComponentModel
9
+ resolvedInputs: Record<string, ResolvedInstanceInput[]>
10
+ }
11
+
12
+ export type ValidationResolverOutput =
13
+ | {
14
+ status: "ok"
15
+ }
16
+ | {
17
+ status: "invalid-args"
18
+ errorText: string
19
+ }
20
+ | {
21
+ status: "invalid-inputs" | "missing-inputs"
22
+ errorText: string
23
+ }
24
+
25
+ /**
26
+ * Validates the instance based on its arguments and inputs.
27
+ */
28
+ export const createValidationResolver = defineGraphResolver<
29
+ ValidationResolverInput,
30
+ ValidationResolverOutput
31
+ >({
32
+ name: "validation-resolver",
33
+ getNodeId: node => node.instance.id,
34
+
35
+ getNodeDependencies({ resolvedInputs }) {
36
+ const dependencies: string[] = []
37
+
38
+ for (const inputs of Object.values(resolvedInputs)) {
39
+ for (const input of inputs) {
40
+ dependencies.push(input.input.instanceId)
41
+ }
42
+ }
43
+
44
+ return dependencies
45
+ },
46
+
47
+ process({ instance, component, resolvedInputs }, dependencies, logger) {
48
+ const ajv = new Ajv()
49
+
50
+ logger.debug({ instanceId: instance.id }, "validating instance")
51
+
52
+ for (const [name, argument] of Object.entries(component.args)) {
53
+ if (!argument.required && !instance.args?.[name]) {
54
+ continue
55
+ }
56
+
57
+ if (!ajv.validate(argument.schema, instance.args?.[name])) {
58
+ logger.debug({ instanceId: instance.id, argumentName: name }, "invalid argument")
59
+
60
+ return {
61
+ status: "invalid-args",
62
+ errorText: ajv.errorsText(),
63
+ }
64
+ }
65
+ }
66
+
67
+ for (const inputs of Object.values(resolvedInputs)) {
68
+ for (const input of inputs) {
69
+ const inputInstance = dependencies.get(input.input.instanceId)
70
+ if (inputInstance?.status !== "ok") {
71
+ return {
72
+ status: "invalid-inputs",
73
+ errorText: `instance "${input.input.instanceId}" is invalid`,
74
+ }
75
+ }
76
+ }
77
+ }
78
+
79
+ for (const [name, input] of Object.entries(component.inputs)) {
80
+ if (!input.required || input.multiple) {
81
+ continue
82
+ }
83
+
84
+ if (!resolvedInputs[name] || !resolvedInputs[name].length) {
85
+ return {
86
+ status: "missing-inputs",
87
+ errorText: `input "${name}" is missing`,
88
+ }
89
+ }
90
+ }
91
+
92
+ return { status: "ok" }
93
+ },
94
+ })