@highstate/contract 0.7.2 → 0.7.4
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/index.js +240 -0
- package/package.json +8 -8
- package/src/component.spec.ts +166 -0
- package/src/component.ts +361 -0
- package/src/entity.ts +54 -0
- package/src/evaluation.ts +60 -0
- package/src/index.ts +42 -0
- package/src/instance.ts +165 -0
- package/src/types.ts +45 -0
- package/src/unit.ts +102 -0
- package/src/utils.ts +89 -0
- package/dist/index.d.ts +0 -326
- package/dist/index.mjs +0 -192
package/src/component.ts
ADDED
@@ -0,0 +1,361 @@
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
2
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
3
|
+
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
4
|
+
/* eslint-disable @typescript-eslint/no-unsafe-return */
|
5
|
+
|
6
|
+
import type { Entity } from "./entity"
|
7
|
+
import type { ArgumentValue, ArgumentValueSchema, Meta } from "./types"
|
8
|
+
import {
|
9
|
+
type Static,
|
10
|
+
type TObject,
|
11
|
+
type TOptional,
|
12
|
+
type TSchema,
|
13
|
+
OptionalKind,
|
14
|
+
} from "@sinclair/typebox"
|
15
|
+
import { isNonNullish, mapValues, pickBy } from "remeda"
|
16
|
+
import { Ajv } from "ajv"
|
17
|
+
import { boundaryInput, type InstanceInput } from "./instance"
|
18
|
+
import { type OptionalUndefinedFields, type OptionalEmptyRecords } from "./utils"
|
19
|
+
import { registerInstance } from "./evaluation"
|
20
|
+
|
21
|
+
const ajv = new Ajv()
|
22
|
+
|
23
|
+
// Argument
|
24
|
+
export type ComponentArgument = {
|
25
|
+
schema: ArgumentValueSchema
|
26
|
+
required: boolean
|
27
|
+
meta: Meta
|
28
|
+
}
|
29
|
+
|
30
|
+
type ComponentArgumentFullOptions = Meta & {
|
31
|
+
schema: ArgumentValueSchema
|
32
|
+
required?: boolean
|
33
|
+
}
|
34
|
+
|
35
|
+
export type ComponentArgumentOptions = ArgumentValueSchema | ComponentArgumentFullOptions
|
36
|
+
|
37
|
+
export type ComponentArgumentOptionsToSchema<T extends ComponentArgumentOptions> = T extends TSchema
|
38
|
+
? T
|
39
|
+
: T["required"] extends false
|
40
|
+
? TOptional<T["schema"]>
|
41
|
+
: T["schema"]
|
42
|
+
|
43
|
+
type ComponentArgumentMapToValue<T extends Record<string, ComponentArgumentOptions>> = {
|
44
|
+
[K in keyof T]: Static<ComponentArgumentOptionsToSchema<T[K]>>
|
45
|
+
}
|
46
|
+
|
47
|
+
// Input
|
48
|
+
export type ComponentInput = {
|
49
|
+
type: string
|
50
|
+
required: boolean
|
51
|
+
multiple: boolean
|
52
|
+
meta: Meta
|
53
|
+
}
|
54
|
+
|
55
|
+
type ComponentInputFullOptions = Meta & {
|
56
|
+
entity: Entity
|
57
|
+
required?: boolean
|
58
|
+
multiple?: boolean
|
59
|
+
}
|
60
|
+
|
61
|
+
export type ComponentInputOptions = Entity | ComponentInputFullOptions
|
62
|
+
|
63
|
+
type ComponentInputOptionsToOutputRef<T extends ComponentInputOptions> = T extends Entity
|
64
|
+
? InstanceInput<T["type"]>
|
65
|
+
: T extends ComponentInputFullOptions
|
66
|
+
? T["required"] extends false
|
67
|
+
? T["multiple"] extends true
|
68
|
+
? InstanceInput<T["entity"]["type"]>[] | undefined
|
69
|
+
: InstanceInput<T["entity"]["type"]> | undefined
|
70
|
+
: T["multiple"] extends true
|
71
|
+
? InstanceInput<T["entity"]["type"]>[]
|
72
|
+
: InstanceInput<T["entity"]["type"]>
|
73
|
+
: never
|
74
|
+
|
75
|
+
export type ComponentInputSpec = [entity: Entity, required: boolean, multiple: boolean]
|
76
|
+
|
77
|
+
export type ComponentInputOptionsToSpec<T extends ComponentInputOptions> = T extends Entity
|
78
|
+
? [T, true, false] // [Entity, required, multiple]
|
79
|
+
: T extends ComponentInputFullOptions
|
80
|
+
? T["required"] extends false
|
81
|
+
? T["multiple"] extends true
|
82
|
+
? [T["entity"], false, true]
|
83
|
+
: [T["entity"], false, false]
|
84
|
+
: T["multiple"] extends true
|
85
|
+
? [T["entity"], true, true]
|
86
|
+
: [T["entity"], true, false]
|
87
|
+
: never
|
88
|
+
|
89
|
+
export type ComponentInputOptionsMapToSpecMap<T extends Record<string, ComponentInputOptions>> =
|
90
|
+
T extends Record<string, never>
|
91
|
+
? Record<string, never>
|
92
|
+
: { [K in keyof T]: ComponentInputOptionsToSpec<T[K]> }
|
93
|
+
|
94
|
+
type ComponentInputMapToValue<T extends Record<string, ComponentInputOptions>> =
|
95
|
+
OptionalUndefinedFields<{
|
96
|
+
[K in keyof T]: ComponentInputOptionsToOutputRef<T[K]>
|
97
|
+
}>
|
98
|
+
|
99
|
+
type ComponentInputMapToReturnType<T extends Record<string, ComponentInputOptions>> =
|
100
|
+
T extends Record<string, never> ? void : ComponentInputMapToValue<T>
|
101
|
+
|
102
|
+
// Params & Options
|
103
|
+
export type ComponentParams<
|
104
|
+
TArgs extends Record<string, ComponentArgumentOptions>,
|
105
|
+
TInputs extends Record<string, ComponentInputOptions>,
|
106
|
+
> = {
|
107
|
+
id: string
|
108
|
+
name: string
|
109
|
+
args: ComponentArgumentMapToValue<TArgs>
|
110
|
+
inputs: ComponentInputMapToValue<TInputs>
|
111
|
+
}
|
112
|
+
|
113
|
+
export type InputComponentParams<
|
114
|
+
TArgs extends Record<string, ArgumentValue>,
|
115
|
+
TInputs extends Record<string, unknown>,
|
116
|
+
> = {
|
117
|
+
name: string
|
118
|
+
} & OptionalEmptyRecords<{
|
119
|
+
args: TArgs
|
120
|
+
inputs: TInputs
|
121
|
+
}>
|
122
|
+
|
123
|
+
export type ComponentOptions<
|
124
|
+
TArgs extends Record<string, ComponentArgumentOptions>,
|
125
|
+
TInputs extends Record<string, ComponentInputOptions>,
|
126
|
+
TOutputs extends Record<string, ComponentInputOptions>,
|
127
|
+
> = {
|
128
|
+
type: string
|
129
|
+
meta?: ComponentMeta
|
130
|
+
|
131
|
+
args?: TArgs
|
132
|
+
inputs?: TInputs
|
133
|
+
outputs?: TOutputs
|
134
|
+
|
135
|
+
create: (params: ComponentParams<TArgs, TInputs>) => ComponentInputMapToReturnType<TOutputs>
|
136
|
+
}
|
137
|
+
|
138
|
+
// Models
|
139
|
+
export type ComponentMeta = Meta & {
|
140
|
+
primaryIcon?: string
|
141
|
+
primaryIconColor?: string
|
142
|
+
secondaryIcon?: string
|
143
|
+
secondaryIconColor?: string
|
144
|
+
category?: string
|
145
|
+
defaultNamePrefix?: string
|
146
|
+
}
|
147
|
+
|
148
|
+
export type ComponentModel = {
|
149
|
+
/**
|
150
|
+
* The type of the component.
|
151
|
+
*/
|
152
|
+
type: string
|
153
|
+
|
154
|
+
/**
|
155
|
+
* The record of the argument schemas.
|
156
|
+
*/
|
157
|
+
args: Record<string, ComponentArgument>
|
158
|
+
|
159
|
+
/**
|
160
|
+
* The record of the input schemas.
|
161
|
+
*/
|
162
|
+
inputs: Record<string, ComponentInput>
|
163
|
+
|
164
|
+
/**
|
165
|
+
* The record of the output schemas.
|
166
|
+
*/
|
167
|
+
outputs: Record<string, ComponentInput>
|
168
|
+
|
169
|
+
/**
|
170
|
+
* The extra metadata of the component.
|
171
|
+
*/
|
172
|
+
meta: ComponentMeta
|
173
|
+
|
174
|
+
/**
|
175
|
+
* The hash of the component definition.
|
176
|
+
*/
|
177
|
+
definitionHash: string
|
178
|
+
}
|
179
|
+
|
180
|
+
type InputSpecToOutputRef<T extends ComponentInputSpec> = T[1] extends true
|
181
|
+
? T[2] extends true
|
182
|
+
? InstanceInput<T[0]["type"]>[]
|
183
|
+
: InstanceInput<T[0]["type"]>
|
184
|
+
: T[2] extends true
|
185
|
+
? InstanceInput<T[0]["type"]>[] | undefined
|
186
|
+
: InstanceInput<T[0]["type"]> | undefined
|
187
|
+
|
188
|
+
export type OutputRefMap<TInputs extends Record<string, ComponentInputSpec>> =
|
189
|
+
TInputs extends Record<string, [string, never, never]>
|
190
|
+
? Record<string, never>
|
191
|
+
: { [K in keyof TInputs]: InputSpecToOutputRef<TInputs[K]> }
|
192
|
+
|
193
|
+
export const originalCreate = Symbol("originalCreate")
|
194
|
+
|
195
|
+
export type Component<
|
196
|
+
TArgs extends Record<string, ArgumentValue> = Record<string, never>,
|
197
|
+
TInputs extends Record<string, ComponentInputSpec> = Record<string, never>,
|
198
|
+
TOutputs extends Record<string, ComponentInputSpec> = Record<string, never>,
|
199
|
+
> = {
|
200
|
+
/**
|
201
|
+
* The non-generic model of the component.
|
202
|
+
*/
|
203
|
+
model: ComponentModel
|
204
|
+
|
205
|
+
/**
|
206
|
+
* The entities used in the inputs or outputs of the component.
|
207
|
+
*/
|
208
|
+
entities: Map<string, Entity>
|
209
|
+
|
210
|
+
/**
|
211
|
+
* Creates the component at the evaluation time.
|
212
|
+
*/
|
213
|
+
(context: InputComponentParams<TArgs, OutputRefMap<TInputs>>): OutputRefMap<TOutputs>
|
214
|
+
|
215
|
+
/**
|
216
|
+
* The original create function.
|
217
|
+
*
|
218
|
+
* Used to calculate the definition hash.
|
219
|
+
*/
|
220
|
+
[originalCreate]: (params: InputComponentParams<any, any>) => any
|
221
|
+
}
|
222
|
+
|
223
|
+
export type ArgumentOptionsMapToStatic<T extends Record<string, ComponentArgumentOptions>> =
|
224
|
+
T extends Record<string, never>
|
225
|
+
? Record<string, never>
|
226
|
+
: Static<TObject<{ [K in keyof T]: ComponentArgumentOptionsToSchema<T[K]> }>>
|
227
|
+
|
228
|
+
export function defineComponent<
|
229
|
+
TArgs extends Record<string, ComponentArgumentOptions> = Record<string, never>,
|
230
|
+
TInputs extends Record<string, ComponentInputOptions> = Record<string, never>,
|
231
|
+
TOutputs extends Record<string, ComponentInputOptions> = Record<string, never>,
|
232
|
+
>(
|
233
|
+
options: ComponentOptions<TArgs, TInputs, TOutputs>,
|
234
|
+
): Component<
|
235
|
+
ArgumentOptionsMapToStatic<TArgs>,
|
236
|
+
ComponentInputOptionsMapToSpecMap<TInputs>,
|
237
|
+
ComponentInputOptionsMapToSpecMap<TOutputs>
|
238
|
+
> {
|
239
|
+
function create(params: InputComponentParams<any, any>): any {
|
240
|
+
const { name, args, inputs } = params
|
241
|
+
const id = `${options.type}:${name}`
|
242
|
+
|
243
|
+
validateArgs(id, create.model as ComponentModel, args ?? {})
|
244
|
+
|
245
|
+
const flatInputs = mapValues(pickBy(inputs ?? {}, isNonNullish), inputs => [inputs].flat(2))
|
246
|
+
|
247
|
+
return registerInstance(
|
248
|
+
create.model as ComponentModel,
|
249
|
+
{
|
250
|
+
id,
|
251
|
+
type: options.type,
|
252
|
+
name,
|
253
|
+
args: args ?? {},
|
254
|
+
inputs: mapValues(flatInputs, inputs => inputs.map(input => input[boundaryInput] ?? input)),
|
255
|
+
resolvedInputs: flatInputs,
|
256
|
+
},
|
257
|
+
() => {
|
258
|
+
const markedInputs = mapValues(flatInputs, (inputs, key) => {
|
259
|
+
const result = inputs.map(input => ({
|
260
|
+
...input,
|
261
|
+
[boundaryInput]: { instanceId: id, output: key },
|
262
|
+
}))
|
263
|
+
|
264
|
+
return (create.model as ComponentModel).inputs?.[key]?.multiple === false
|
265
|
+
? result[0]
|
266
|
+
: result
|
267
|
+
})
|
268
|
+
|
269
|
+
const outputs: Record<string, InstanceInput[]> =
|
270
|
+
options.create({
|
271
|
+
id,
|
272
|
+
name,
|
273
|
+
args: (args as any) ?? {},
|
274
|
+
inputs: markedInputs as any,
|
275
|
+
}) ?? {}
|
276
|
+
|
277
|
+
return mapValues(pickBy(outputs, isNonNullish), outputs => [outputs].flat(2))
|
278
|
+
},
|
279
|
+
)
|
280
|
+
}
|
281
|
+
|
282
|
+
create.entities = new Map<string, Entity>()
|
283
|
+
const mapInput = createInputMapper(create.entities)
|
284
|
+
|
285
|
+
create.model = {
|
286
|
+
type: options.type,
|
287
|
+
args: mapValues(options.args ?? {}, mapArgument),
|
288
|
+
inputs: mapValues(options.inputs ?? {}, mapInput),
|
289
|
+
outputs: mapValues(options.outputs ?? {}, mapInput),
|
290
|
+
meta: options.meta ?? {},
|
291
|
+
}
|
292
|
+
|
293
|
+
create[originalCreate] = options.create
|
294
|
+
|
295
|
+
return create as any
|
296
|
+
}
|
297
|
+
|
298
|
+
export function isComponent(value: unknown): value is Component {
|
299
|
+
return typeof value === "function" && "model" in value
|
300
|
+
}
|
301
|
+
|
302
|
+
export function mapArgument(value: ComponentArgumentOptions) {
|
303
|
+
if ("schema" in value) {
|
304
|
+
return {
|
305
|
+
schema: value.schema,
|
306
|
+
required: value.required ?? (!value.schema[OptionalKind] && !value.schema.default),
|
307
|
+
meta: {
|
308
|
+
displayName: value.displayName,
|
309
|
+
description: value.description,
|
310
|
+
color: value.color,
|
311
|
+
},
|
312
|
+
}
|
313
|
+
}
|
314
|
+
|
315
|
+
return {
|
316
|
+
schema: value,
|
317
|
+
required: !value[OptionalKind] && !value.default,
|
318
|
+
meta: {},
|
319
|
+
}
|
320
|
+
}
|
321
|
+
|
322
|
+
export function createInputMapper(entities: Map<string, Entity>) {
|
323
|
+
return (value: ComponentInputOptions) => {
|
324
|
+
if ("entity" in value) {
|
325
|
+
entities.set(value.entity.type, value.entity)
|
326
|
+
|
327
|
+
return {
|
328
|
+
type: value.entity.type,
|
329
|
+
required: value.required ?? true,
|
330
|
+
multiple: value.multiple ?? false,
|
331
|
+
meta: {
|
332
|
+
displayName: value.displayName,
|
333
|
+
description: value.description,
|
334
|
+
color: value.color,
|
335
|
+
},
|
336
|
+
}
|
337
|
+
}
|
338
|
+
|
339
|
+
entities.set(value.type, value)
|
340
|
+
|
341
|
+
return {
|
342
|
+
type: value.type,
|
343
|
+
required: true,
|
344
|
+
multiple: false,
|
345
|
+
meta: {},
|
346
|
+
}
|
347
|
+
}
|
348
|
+
}
|
349
|
+
|
350
|
+
function validateArgs(instanceId: string, model: ComponentModel, args: Record<string, unknown>) {
|
351
|
+
for (const [key, argModel] of Object.entries(model.args)) {
|
352
|
+
const value = args[key]
|
353
|
+
if (!value && argModel.required) {
|
354
|
+
throw new Error(`Missing required argument "${key}" for instance "${instanceId}"`)
|
355
|
+
}
|
356
|
+
|
357
|
+
if (value && !ajv.validate(argModel.schema, value)) {
|
358
|
+
throw new Error(`Invalid argument "${key}" for instance "${instanceId}": ${ajv.errorsText()}`)
|
359
|
+
}
|
360
|
+
}
|
361
|
+
}
|
package/src/entity.ts
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
import type { TAnySchema, TSchema } from "@sinclair/typebox"
|
2
|
+
import type { Meta } from "./types"
|
3
|
+
import type { PartialKeys } from "./utils"
|
4
|
+
|
5
|
+
/**
|
6
|
+
* The entity is some abstract object which can be passed from one component to another through their inputs and outputs.
|
7
|
+
* Every entity must have a type.
|
8
|
+
* Every component inputs and outputs will reference such types and only entities of the same type can be passed.
|
9
|
+
*/
|
10
|
+
export type Entity<TType extends string = string, TEntitySchema extends TSchema = TSchema> = {
|
11
|
+
/**
|
12
|
+
* The static type of the entity.
|
13
|
+
*/
|
14
|
+
type: TType
|
15
|
+
|
16
|
+
/**
|
17
|
+
* The JSON schema of the entity value.
|
18
|
+
*/
|
19
|
+
schema: TEntitySchema
|
20
|
+
|
21
|
+
/**
|
22
|
+
* The extra metadata of the entity.
|
23
|
+
*/
|
24
|
+
meta: Meta
|
25
|
+
|
26
|
+
/**
|
27
|
+
* The hash of the entity definition.
|
28
|
+
*/
|
29
|
+
definitionHash: string
|
30
|
+
}
|
31
|
+
|
32
|
+
export type EntityOptions<TType extends string, TSchema extends TAnySchema> = Omit<
|
33
|
+
PartialKeys<Entity<TType, TSchema>, "meta">,
|
34
|
+
"definitionHash"
|
35
|
+
>
|
36
|
+
|
37
|
+
export function defineEntity<TType extends string, TSchema extends TAnySchema>(
|
38
|
+
options: EntityOptions<TType, TSchema>,
|
39
|
+
): Entity<TType, TSchema> {
|
40
|
+
return {
|
41
|
+
meta: {},
|
42
|
+
...options,
|
43
|
+
} as Entity<TType, TSchema>
|
44
|
+
}
|
45
|
+
|
46
|
+
export function isEntity(value: unknown): value is Entity {
|
47
|
+
return (
|
48
|
+
typeof value === "object" &&
|
49
|
+
value !== null &&
|
50
|
+
"type" in value &&
|
51
|
+
"schema" in value &&
|
52
|
+
"meta" in value
|
53
|
+
)
|
54
|
+
}
|
@@ -0,0 +1,60 @@
|
|
1
|
+
import type { ComponentModel } from "./component"
|
2
|
+
import { mapValues } from "remeda"
|
3
|
+
import { boundaryInput, type InstanceInput, type InstanceModel } from "./instance"
|
4
|
+
import { isUnitModel } from "./unit"
|
5
|
+
|
6
|
+
export type CompositeInstance = {
|
7
|
+
instance: InstanceModel
|
8
|
+
children: InstanceModel[]
|
9
|
+
}
|
10
|
+
|
11
|
+
const compositeInstances: Map<string, CompositeInstance> = new Map()
|
12
|
+
let currentCompositeInstance: CompositeInstance | null = null
|
13
|
+
|
14
|
+
export function resetEvaluation(): void {
|
15
|
+
compositeInstances.clear()
|
16
|
+
currentCompositeInstance = null
|
17
|
+
}
|
18
|
+
|
19
|
+
export function getCompositeInstances(): CompositeInstance[] {
|
20
|
+
return Array.from(compositeInstances.values())
|
21
|
+
}
|
22
|
+
|
23
|
+
export function registerInstance<T>(
|
24
|
+
component: ComponentModel,
|
25
|
+
instance: InstanceModel,
|
26
|
+
fn: () => T,
|
27
|
+
): T {
|
28
|
+
if (currentCompositeInstance) {
|
29
|
+
instance.parentId = currentCompositeInstance.instance.id
|
30
|
+
currentCompositeInstance.children.push(instance)
|
31
|
+
}
|
32
|
+
|
33
|
+
let previousParentInstance: CompositeInstance | null = null
|
34
|
+
if (!isUnitModel(component)) {
|
35
|
+
previousParentInstance = currentCompositeInstance
|
36
|
+
currentCompositeInstance = { instance, children: [] }
|
37
|
+
compositeInstances.set(currentCompositeInstance.instance.id, currentCompositeInstance)
|
38
|
+
}
|
39
|
+
|
40
|
+
try {
|
41
|
+
const outputs = fn() as Record<string, InstanceInput[]>
|
42
|
+
|
43
|
+
instance.resolvedOutputs = outputs
|
44
|
+
instance.outputs = mapValues(outputs ?? {}, outputs =>
|
45
|
+
outputs.map(output => output[boundaryInput] ?? output),
|
46
|
+
)
|
47
|
+
|
48
|
+
// mark all outputs with the boundary input of the instance
|
49
|
+
return mapValues(outputs, (outputs, outputKey) =>
|
50
|
+
outputs.map(output => ({
|
51
|
+
...output,
|
52
|
+
[boundaryInput]: { instanceId: instance.id, output: outputKey },
|
53
|
+
})),
|
54
|
+
) as T
|
55
|
+
} finally {
|
56
|
+
if (previousParentInstance) {
|
57
|
+
currentCompositeInstance = previousParentInstance
|
58
|
+
}
|
59
|
+
}
|
60
|
+
}
|
package/src/index.ts
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
export {
|
2
|
+
type InstanceInput,
|
3
|
+
type HubInput,
|
4
|
+
type InstanceModel,
|
5
|
+
type Position,
|
6
|
+
getInstanceId,
|
7
|
+
parseInstanceId,
|
8
|
+
findInput,
|
9
|
+
findRequiredInput,
|
10
|
+
} from "./instance"
|
11
|
+
export { getCompositeInstances, resetEvaluation, type CompositeInstance } from "./evaluation"
|
12
|
+
export { type Entity, defineEntity, isEntity } from "./entity"
|
13
|
+
export {
|
14
|
+
type Component,
|
15
|
+
type ComponentModel,
|
16
|
+
type ComponentMeta,
|
17
|
+
type ComponentArgument,
|
18
|
+
type ComponentInput,
|
19
|
+
type ComponentInputSpec,
|
20
|
+
defineComponent,
|
21
|
+
isComponent,
|
22
|
+
originalCreate,
|
23
|
+
} from "./component"
|
24
|
+
export { type Unit, type UnitModel, type UnitSource, defineUnit, isUnitModel } from "./unit"
|
25
|
+
export { type RequiredKeys, type PartialKeys, text, trimIndentation } from "./utils"
|
26
|
+
export { type ArgumentValue, type ArgumentValueSchema } from "./types"
|
27
|
+
export { type Static } from "@sinclair/typebox"
|
28
|
+
|
29
|
+
import { Type as BaseType, type TLiteral, type TUnion } from "@sinclair/typebox"
|
30
|
+
|
31
|
+
type MapToLiteral<T extends readonly string[]> = {
|
32
|
+
[K in keyof T]: T[K] extends string ? TLiteral<T[K]> : never
|
33
|
+
}
|
34
|
+
|
35
|
+
function StringEnum<T extends string[]>(values: [...T]): TUnion<MapToLiteral<T>> {
|
36
|
+
return Type.Union(values.map(value => Type.Literal(value))) as TUnion<MapToLiteral<T>>
|
37
|
+
}
|
38
|
+
|
39
|
+
export const Type = {
|
40
|
+
...BaseType,
|
41
|
+
StringEnum,
|
42
|
+
} as typeof BaseType & { StringEnum: typeof StringEnum }
|
package/src/instance.ts
ADDED
@@ -0,0 +1,165 @@
|
|
1
|
+
declare const type: unique symbol
|
2
|
+
|
3
|
+
export type InstanceInput<TType extends string = string> = {
|
4
|
+
[type]?: TType
|
5
|
+
[boundaryInput]?: InstanceInput
|
6
|
+
instanceId: string
|
7
|
+
output: string
|
8
|
+
}
|
9
|
+
|
10
|
+
export const boundaryInput = Symbol("boundaryInput")
|
11
|
+
|
12
|
+
export type HubInput = {
|
13
|
+
hubId: string
|
14
|
+
}
|
15
|
+
|
16
|
+
export type Position = {
|
17
|
+
x: number
|
18
|
+
y: number
|
19
|
+
}
|
20
|
+
|
21
|
+
export type InstanceModel = {
|
22
|
+
/**
|
23
|
+
* The id of the instance unique within the project.
|
24
|
+
*
|
25
|
+
* The format is `${instanceType}:${instanceName}`.
|
26
|
+
*/
|
27
|
+
id: string
|
28
|
+
|
29
|
+
/**
|
30
|
+
* The type of the instance.
|
31
|
+
*/
|
32
|
+
type: string
|
33
|
+
|
34
|
+
/**
|
35
|
+
* The name of the instance.
|
36
|
+
*
|
37
|
+
* Must be unique within instances of the same type in the project.
|
38
|
+
*/
|
39
|
+
name: string
|
40
|
+
|
41
|
+
/**
|
42
|
+
* The static arguments passed to the instance.
|
43
|
+
*/
|
44
|
+
args?: Record<string, unknown>
|
45
|
+
|
46
|
+
/**
|
47
|
+
* The direct instances passed as inputs to the instance.
|
48
|
+
*/
|
49
|
+
inputs?: Record<string, InstanceInput[]>
|
50
|
+
|
51
|
+
/**
|
52
|
+
* The resolved unit inputs for the instance.
|
53
|
+
*
|
54
|
+
* Only for computed composite instances.
|
55
|
+
*/
|
56
|
+
resolvedInputs?: Record<string, InstanceInput[]>
|
57
|
+
|
58
|
+
/**
|
59
|
+
* The inputs passed to the instance from the hubs.
|
60
|
+
*
|
61
|
+
* Only for designer-first instances.
|
62
|
+
*/
|
63
|
+
hubInputs?: Record<string, HubInput[]>
|
64
|
+
|
65
|
+
/**
|
66
|
+
* The inputs injected to the instance from the hubs.
|
67
|
+
*
|
68
|
+
* While `hubInputs` allows to pass hubs to distinct inputs,
|
69
|
+
* `injectionInputs` allows to pass hubs to the instance as a whole filling all inputs with matching types.
|
70
|
+
*
|
71
|
+
* Only for designer-first instances.
|
72
|
+
*/
|
73
|
+
injectionInputs?: HubInput[]
|
74
|
+
|
75
|
+
/**
|
76
|
+
* The position of the instance on the canvas.
|
77
|
+
*
|
78
|
+
* Only for designer-first instances.
|
79
|
+
*/
|
80
|
+
position?: Position
|
81
|
+
|
82
|
+
/**
|
83
|
+
* The id of the top level parent instance.
|
84
|
+
*
|
85
|
+
* Only for child instances of the composite instances.
|
86
|
+
*/
|
87
|
+
parentId?: string
|
88
|
+
|
89
|
+
/**
|
90
|
+
* The direct instance inputs and same-level children outputs returned by the instance as outputs.
|
91
|
+
*
|
92
|
+
* Only for computed composite instances.
|
93
|
+
*/
|
94
|
+
outputs?: Record<string, InstanceInput[]>
|
95
|
+
|
96
|
+
/**
|
97
|
+
* The resolved unit outputs for the instance.
|
98
|
+
*
|
99
|
+
* Only for computed composite instances.
|
100
|
+
*/
|
101
|
+
resolvedOutputs?: Record<string, InstanceInput[]>
|
102
|
+
}
|
103
|
+
|
104
|
+
/**
|
105
|
+
* Formats the instance id from the instance type and instance name.
|
106
|
+
*
|
107
|
+
* @param instanceType The type of the instance.
|
108
|
+
* @param instanceName The name of the instance.
|
109
|
+
*
|
110
|
+
* @returns The formatted instance id.
|
111
|
+
*/
|
112
|
+
export function getInstanceId(instanceType: string, instanceName: string): string {
|
113
|
+
return `${instanceType}:${instanceName}`
|
114
|
+
}
|
115
|
+
|
116
|
+
/**
|
117
|
+
* Parses the instance id into the instance type and instance name.
|
118
|
+
*
|
119
|
+
* @param instanceId The instance id to parse.
|
120
|
+
*
|
121
|
+
* @returns The instance type and instance name.
|
122
|
+
*/
|
123
|
+
export function parseInstanceId(instanceId: string): [instanceType: string, instanceName: string] {
|
124
|
+
const parts = instanceId.split(":")
|
125
|
+
|
126
|
+
if (parts.length !== 2) {
|
127
|
+
throw new Error(`Invalid instance key: ${instanceId}`)
|
128
|
+
}
|
129
|
+
|
130
|
+
return parts as [string, string]
|
131
|
+
}
|
132
|
+
|
133
|
+
export function findInput<T extends string>(
|
134
|
+
inputs: InstanceInput<T>[],
|
135
|
+
name: string,
|
136
|
+
): InstanceInput<T> | null {
|
137
|
+
const matchedInputs = inputs.filter(
|
138
|
+
input => parseInstanceId(input.instanceId)[1] === name || input.instanceId === name,
|
139
|
+
)
|
140
|
+
|
141
|
+
if (matchedInputs.length === 0) {
|
142
|
+
return null
|
143
|
+
}
|
144
|
+
|
145
|
+
if (1 < matchedInputs.length) {
|
146
|
+
throw new Error(
|
147
|
+
`Multiple inputs found for "${name}": ${matchedInputs.map(input => input.instanceId).join(", ")}. Specify the full instance id to disambiguate.`,
|
148
|
+
)
|
149
|
+
}
|
150
|
+
|
151
|
+
return matchedInputs[0]
|
152
|
+
}
|
153
|
+
|
154
|
+
export function findRequiredInput<T extends string>(
|
155
|
+
inputs: InstanceInput<T>[],
|
156
|
+
name: string,
|
157
|
+
): InstanceInput<T> {
|
158
|
+
const input = findInput(inputs, name)
|
159
|
+
|
160
|
+
if (input === null) {
|
161
|
+
throw new Error(`Required input "${name}" not found.`)
|
162
|
+
}
|
163
|
+
|
164
|
+
return input
|
165
|
+
}
|