@highstate/contract 0.19.1 → 0.20.0
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 +682 -426
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
- package/src/component.spec.ts +912 -3
- package/src/component.ts +524 -89
- package/src/cuidv2d.spec.ts +39 -0
- package/src/cuidv2d.ts +54 -0
- package/src/entity.ts +321 -81
- package/src/evaluation.ts +62 -12
- package/src/index.ts +9 -1
- package/src/instance-input.ts +219 -0
- package/src/instance.ts +307 -65
- package/src/json-schema.ts +115 -0
- package/src/pulumi.ts +45 -16
- package/src/unit.ts +26 -10
- package/src/compaction.spec.ts +0 -215
- package/src/compaction.ts +0 -381
package/src/component.ts
CHANGED
|
@@ -2,12 +2,27 @@
|
|
|
2
2
|
|
|
3
3
|
import type { Simplify } from "type-fest"
|
|
4
4
|
import type { Entity, implementedTypes } from "./entity"
|
|
5
|
-
import type { OptionalEmptyRecords,
|
|
5
|
+
import type { OptionalEmptyRecords, PartialKeys } from "./utils"
|
|
6
6
|
import { isNonNullish, mapValues, pickBy, uniqueBy } from "remeda"
|
|
7
7
|
import { z } from "zod"
|
|
8
|
-
import { boundaryInput,
|
|
8
|
+
import { boundaryInput, registerInstance } from "./evaluation"
|
|
9
9
|
import { camelCaseToHumanReadable } from "./i18n"
|
|
10
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
type EntityInput,
|
|
12
|
+
type InstanceId,
|
|
13
|
+
type InstanceInput,
|
|
14
|
+
inputKey,
|
|
15
|
+
type MultipleInput,
|
|
16
|
+
type RequiredInput,
|
|
17
|
+
type RequiredMultipleInput,
|
|
18
|
+
type RuntimeInput,
|
|
19
|
+
} from "./instance"
|
|
20
|
+
import {
|
|
21
|
+
createDeepOutputAccessor,
|
|
22
|
+
createInput,
|
|
23
|
+
createMultipleInputAccessor,
|
|
24
|
+
createNonProvidedInput,
|
|
25
|
+
} from "./instance-input"
|
|
11
26
|
import {
|
|
12
27
|
fieldNameSchema,
|
|
13
28
|
genericNameSchema,
|
|
@@ -77,6 +92,13 @@ export const componentInputSchema = z.object({
|
|
|
77
92
|
*/
|
|
78
93
|
type: versionedNameSchema,
|
|
79
94
|
|
|
95
|
+
/**
|
|
96
|
+
* The input name this output type is derived from.
|
|
97
|
+
*
|
|
98
|
+
* If set, the output uses the referenced input as its fallback type source.
|
|
99
|
+
*/
|
|
100
|
+
fromInput: fieldNameSchema.optional(),
|
|
101
|
+
|
|
80
102
|
/**
|
|
81
103
|
* Whether the input is required.
|
|
82
104
|
*/
|
|
@@ -107,33 +129,75 @@ export type FullComponentInputOptions = {
|
|
|
107
129
|
|
|
108
130
|
export type ComponentInputOptions = Entity | FullComponentInputOptions
|
|
109
131
|
|
|
132
|
+
export type FromInputComponentOutputOptions<TInputName extends string = string> = {
|
|
133
|
+
fromInput: TInputName
|
|
134
|
+
meta?: PartialKeys<ComponentInput["meta"], "title">
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export type ComponentOutputOptions<
|
|
138
|
+
TInputs extends Record<string, ComponentInputOptions> = Record<string, ComponentInputOptions>,
|
|
139
|
+
> = ComponentInputOptions | FromInputComponentOutputOptions<Extract<keyof TInputs, string>>
|
|
140
|
+
|
|
110
141
|
type ComponentInputOptionsToOutputRef<T extends ComponentInputOptions> = T extends Entity
|
|
111
|
-
?
|
|
142
|
+
? EntityInput<T>
|
|
112
143
|
: T extends FullComponentInputOptions
|
|
113
|
-
? T["
|
|
114
|
-
? T["
|
|
115
|
-
|
|
116
|
-
: InstanceInput<T["entity"][typeof implementedTypes]> | undefined
|
|
117
|
-
: T["multiple"] extends true
|
|
118
|
-
? InstanceInput<T["entity"][typeof implementedTypes]>[]
|
|
119
|
-
: InstanceInput<T["entity"][typeof implementedTypes]>
|
|
144
|
+
? T["multiple"] extends true
|
|
145
|
+
? RequiredMultipleInput<EntityInput<T["entity"]>>
|
|
146
|
+
: EntityInput<T["entity"]>
|
|
120
147
|
: never
|
|
121
148
|
|
|
149
|
+
type MultipleInputConstructorValue<TEntity extends Entity> =
|
|
150
|
+
| MultipleInput<EntityInput<TEntity>>
|
|
151
|
+
| EntityInput<TEntity>[]
|
|
152
|
+
| InstanceInput[]
|
|
153
|
+
| Array<MultipleInput<EntityInput<TEntity>> | EntityInput<TEntity>[] | InstanceInput[]>
|
|
154
|
+
|
|
122
155
|
/**
|
|
123
156
|
* The type-level specification of a component input hold by the component model.
|
|
124
157
|
*/
|
|
125
|
-
export type ComponentInputSpec
|
|
158
|
+
export type ComponentInputSpec<TEntity extends Entity = Entity> = [
|
|
159
|
+
entity: TEntity,
|
|
160
|
+
required: boolean,
|
|
161
|
+
multiple: boolean,
|
|
162
|
+
]
|
|
126
163
|
|
|
127
164
|
export type ComponentInputOptionsToSpec<T extends ComponentInputOptions> = T extends Entity
|
|
128
|
-
? [T, true, false]
|
|
165
|
+
? [T, true, false]
|
|
129
166
|
: T extends FullComponentInputOptions
|
|
130
167
|
? T["required"] extends false
|
|
131
168
|
? T["multiple"] extends true
|
|
132
|
-
?
|
|
133
|
-
|
|
169
|
+
? ComponentInputSpec<T["entity"]> extends infer Spec
|
|
170
|
+
? Spec extends ComponentInputSpec
|
|
171
|
+
? [Spec[0], false, true]
|
|
172
|
+
: never
|
|
173
|
+
: never
|
|
174
|
+
: ComponentInputSpec<T["entity"]> extends infer Spec
|
|
175
|
+
? Spec extends ComponentInputSpec
|
|
176
|
+
? [Spec[0], false, false]
|
|
177
|
+
: never
|
|
178
|
+
: never
|
|
134
179
|
: T["multiple"] extends true
|
|
135
|
-
?
|
|
136
|
-
|
|
180
|
+
? ComponentInputSpec<T["entity"]> extends infer Spec
|
|
181
|
+
? Spec extends ComponentInputSpec
|
|
182
|
+
? [Spec[0], true, true]
|
|
183
|
+
: never
|
|
184
|
+
: never
|
|
185
|
+
: ComponentInputSpec<T["entity"]> extends infer Spec
|
|
186
|
+
? Spec extends ComponentInputSpec
|
|
187
|
+
? [Spec[0], true, false]
|
|
188
|
+
: never
|
|
189
|
+
: never
|
|
190
|
+
: never
|
|
191
|
+
|
|
192
|
+
export type ComponentOutputOptionsToSpec<
|
|
193
|
+
TOutput extends ComponentOutputOptions<TInputs>,
|
|
194
|
+
TInputs extends Record<string, ComponentInputOptions>,
|
|
195
|
+
> = TOutput extends FromInputComponentOutputOptions<infer TInputName>
|
|
196
|
+
? TInputName extends keyof TInputs
|
|
197
|
+
? ComponentInputOptionsToSpec<TInputs[TInputName]>
|
|
198
|
+
: never
|
|
199
|
+
: TOutput extends ComponentInputOptions
|
|
200
|
+
? ComponentInputOptionsToSpec<TOutput>
|
|
137
201
|
: never
|
|
138
202
|
|
|
139
203
|
export type ComponentInputOptionsMapToSpecMap<T extends Record<string, ComponentInputOptions>> =
|
|
@@ -141,14 +205,23 @@ export type ComponentInputOptionsMapToSpecMap<T extends Record<string, Component
|
|
|
141
205
|
? Record<string, never>
|
|
142
206
|
: { [K in keyof T]: ComponentInputOptionsToSpec<T[K]> }
|
|
143
207
|
|
|
144
|
-
type ComponentInputMapToValue<T extends Record<string, ComponentInputOptions>> =
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
}>
|
|
208
|
+
type ComponentInputMapToValue<T extends Record<string, ComponentInputOptions>> = {
|
|
209
|
+
[K in keyof T]: ComponentInputOptionsToOutputRef<T[K]>
|
|
210
|
+
}
|
|
148
211
|
|
|
149
|
-
type
|
|
150
|
-
|
|
151
|
-
|
|
212
|
+
type ComponentOutputMapToCreateOutputValue<
|
|
213
|
+
TOutputs extends Record<string, ComponentOutputOptions<TInputs>>,
|
|
214
|
+
TInputs extends Record<string, ComponentInputOptions>,
|
|
215
|
+
> = {
|
|
216
|
+
[K in keyof TOutputs]: InputSpecToInputRef<ComponentOutputOptionsToSpec<TOutputs[K], TInputs>>
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
type ComponentOutputMapToReturnType<
|
|
220
|
+
TOutputs extends Record<string, ComponentOutputOptions<TInputs>>,
|
|
221
|
+
TInputs extends Record<string, ComponentInputOptions>,
|
|
222
|
+
> = TOutputs extends Record<string, never> // biome-ignore lint/suspicious/noConfusingVoidType: this is return type
|
|
223
|
+
? void
|
|
224
|
+
: Partial<ComponentOutputMapToCreateOutputValue<TOutputs, TInputs>>
|
|
152
225
|
|
|
153
226
|
export type ComponentParams<
|
|
154
227
|
TArgs extends Record<string, ComponentArgumentOptions>,
|
|
@@ -174,7 +247,7 @@ export type ComponentOptions<
|
|
|
174
247
|
TType extends VersionedName,
|
|
175
248
|
TArgs extends Record<string, ComponentArgumentOptions>,
|
|
176
249
|
TInputs extends Record<string, ComponentInputOptions>,
|
|
177
|
-
TOutputs extends Record<string,
|
|
250
|
+
TOutputs extends Record<string, ComponentOutputOptions<TInputs>>,
|
|
178
251
|
> = {
|
|
179
252
|
/**
|
|
180
253
|
* The type of the component.
|
|
@@ -215,7 +288,9 @@ export type ComponentOptions<
|
|
|
215
288
|
* Their names are not automatically prefixed with the component name,
|
|
216
289
|
* so you must prefix them manually if needed or guarantee uniqueness in another way.
|
|
217
290
|
*/
|
|
218
|
-
create: (
|
|
291
|
+
create: (
|
|
292
|
+
params: ComponentParams<TArgs, TInputs>,
|
|
293
|
+
) => ComponentOutputMapToReturnType<TOutputs, TInputs>
|
|
219
294
|
|
|
220
295
|
/**
|
|
221
296
|
* For internal use only.
|
|
@@ -290,25 +365,117 @@ export type ComponentModel = z.infer<typeof componentModelSchema>
|
|
|
290
365
|
|
|
291
366
|
type InputSpecToInputRef<T extends ComponentInputSpec> = T[1] extends true
|
|
292
367
|
? T[2] extends true
|
|
293
|
-
?
|
|
294
|
-
:
|
|
368
|
+
? MultipleInputConstructorValue<T[0]>
|
|
369
|
+
: EntityInput<T[0]> | InstanceInput
|
|
295
370
|
: T[2] extends true
|
|
296
|
-
?
|
|
297
|
-
:
|
|
371
|
+
? MultipleInputConstructorValue<T[0]>
|
|
372
|
+
: EntityInput<T[0]> | InstanceInput
|
|
298
373
|
|
|
299
|
-
type
|
|
300
|
-
?
|
|
301
|
-
:
|
|
374
|
+
type InputSpecToUnitOutputRef<T extends ComponentInputSpec> = T[2] extends true
|
|
375
|
+
? RequiredMultipleInput<EntityInput<T[0]>>
|
|
376
|
+
: RequiredInput<EntityInput<T[0]>>
|
|
377
|
+
|
|
378
|
+
type InputSpecToCompositeOutputRef<T extends ComponentInputSpec> = T[2] extends true
|
|
379
|
+
? RequiredMultipleInput<EntityInput<T[0]>>
|
|
380
|
+
: EntityInput<T[0]>
|
|
302
381
|
|
|
303
382
|
export type InputSpecMapToInputRefMap<TInputs extends Record<string, ComponentInputSpec>> =
|
|
304
383
|
TInputs extends Record<string, [string, never, never]>
|
|
305
384
|
? Record<string, never>
|
|
306
|
-
: {
|
|
385
|
+
: {
|
|
386
|
+
[K in keyof TInputs as TInputs[K][1] extends true ? K : never]-?: InputSpecToInputRef<
|
|
387
|
+
TInputs[K]
|
|
388
|
+
>
|
|
389
|
+
} & {
|
|
390
|
+
[K in keyof TInputs as TInputs[K][1] extends true ? never : K]?: InputSpecToInputRef<
|
|
391
|
+
TInputs[K]
|
|
392
|
+
>
|
|
393
|
+
}
|
|
307
394
|
|
|
308
|
-
export type OutputRefMap<
|
|
309
|
-
TInputs extends Record<string,
|
|
310
|
-
|
|
311
|
-
|
|
395
|
+
export type OutputRefMap<
|
|
396
|
+
TInputs extends Record<string, ComponentInputSpec>,
|
|
397
|
+
TKind extends ComponentKind = "composite",
|
|
398
|
+
> = TInputs extends Record<string, [string, never, never]>
|
|
399
|
+
? Record<string, never>
|
|
400
|
+
: {
|
|
401
|
+
[K in keyof TInputs]: TKind extends "unit"
|
|
402
|
+
? InputSpecToUnitOutputRef<TInputs[K]>
|
|
403
|
+
: InputSpecToCompositeOutputRef<TInputs[K]>
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
type StaticOutputRefByKind<
|
|
407
|
+
TOutputSpec extends ComponentInputSpec,
|
|
408
|
+
TKind extends ComponentKind,
|
|
409
|
+
> = TKind extends "unit"
|
|
410
|
+
? InputSpecToUnitOutputRef<TOutputSpec>
|
|
411
|
+
: InputSpecToCompositeOutputRef<TOutputSpec>
|
|
412
|
+
|
|
413
|
+
type ResolveForwardedOutputRef<
|
|
414
|
+
TForwardedInputName extends string,
|
|
415
|
+
TCallInputs extends Record<string, unknown>,
|
|
416
|
+
TFallbackSpec extends ComponentInputSpec,
|
|
417
|
+
TKind extends ComponentKind,
|
|
418
|
+
> = TForwardedInputName extends keyof TCallInputs
|
|
419
|
+
? NonNullable<TCallInputs[TForwardedInputName]>
|
|
420
|
+
: StaticOutputRefByKind<TFallbackSpec, TKind>
|
|
421
|
+
|
|
422
|
+
type ResolveOutputRefForCall<
|
|
423
|
+
TOutputOption,
|
|
424
|
+
TCallInputs extends Record<string, unknown>,
|
|
425
|
+
TFallbackSpec extends ComponentInputSpec,
|
|
426
|
+
TKind extends ComponentKind,
|
|
427
|
+
> = TOutputOption extends FromInputComponentOutputOptions<infer TInputName>
|
|
428
|
+
? ResolveForwardedOutputRef<TInputName, TCallInputs, TFallbackSpec, TKind>
|
|
429
|
+
: StaticOutputRefByKind<TFallbackSpec, TKind>
|
|
430
|
+
|
|
431
|
+
type ResolveOutputRefMapForCall<
|
|
432
|
+
TOutputSpecs extends Record<string, ComponentInputSpec>,
|
|
433
|
+
TOutputOptions extends Record<string, ComponentOutputOptions<TInputOptions>>,
|
|
434
|
+
TInputOptions extends Record<string, ComponentInputOptions>,
|
|
435
|
+
TCallInputs extends Record<string, unknown>,
|
|
436
|
+
TKind extends ComponentKind,
|
|
437
|
+
> = {
|
|
438
|
+
[K in keyof TOutputSpecs]: K extends keyof TOutputOptions
|
|
439
|
+
? [TOutputOptions[K]] extends [never]
|
|
440
|
+
? StaticOutputRefByKind<TOutputSpecs[K], TKind>
|
|
441
|
+
: ResolveOutputRefForCall<TOutputOptions[K], TCallInputs, TOutputSpecs[K], TKind>
|
|
442
|
+
: StaticOutputRefByKind<TOutputSpecs[K], TKind>
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
type IncludesAllExpectedTypes<
|
|
446
|
+
TProvided extends Record<string, unknown>,
|
|
447
|
+
TExpected extends Record<string, unknown>,
|
|
448
|
+
> = Exclude<keyof TExpected, keyof TProvided> extends never ? true : false
|
|
449
|
+
|
|
450
|
+
type ExtractProvidedInputTypes<TValue> = TValue extends RuntimeInput<infer TTypes, any>
|
|
451
|
+
? TTypes
|
|
452
|
+
: TValue extends Array<infer TItem>
|
|
453
|
+
? ExtractProvidedInputTypes<TItem>
|
|
454
|
+
: never
|
|
455
|
+
|
|
456
|
+
type ValidateProvidedInputValue<TValue, TExpectedSpec extends ComponentInputSpec> = [
|
|
457
|
+
ExtractProvidedInputTypes<TValue>,
|
|
458
|
+
] extends [never]
|
|
459
|
+
? TValue
|
|
460
|
+
: ExtractProvidedInputTypes<TValue> extends infer TProvidedTypes
|
|
461
|
+
? TProvidedTypes extends Record<string, unknown>
|
|
462
|
+
? IncludesAllExpectedTypes<
|
|
463
|
+
TProvidedTypes,
|
|
464
|
+
TExpectedSpec[0][typeof implementedTypes]
|
|
465
|
+
> extends true
|
|
466
|
+
? TValue
|
|
467
|
+
: never
|
|
468
|
+
: never
|
|
469
|
+
: never
|
|
470
|
+
|
|
471
|
+
type ValidateCallInputs<
|
|
472
|
+
TCallInputs extends Record<string, unknown>,
|
|
473
|
+
TInputSpecs extends Record<string, ComponentInputSpec>,
|
|
474
|
+
> = {
|
|
475
|
+
[K in keyof TCallInputs]: K extends keyof TInputSpecs
|
|
476
|
+
? ValidateProvidedInputValue<TCallInputs[K], TInputSpecs[K]>
|
|
477
|
+
: TCallInputs[K]
|
|
478
|
+
}
|
|
312
479
|
|
|
313
480
|
export const originalCreate = Symbol("originalCreate")
|
|
314
481
|
export const kind = Symbol("kind")
|
|
@@ -318,6 +485,12 @@ export type Component<
|
|
|
318
485
|
TArgs extends Record<string, z.ZodType> = Record<string, never>,
|
|
319
486
|
TInputs extends Record<string, ComponentInputSpec> = Record<string, never>,
|
|
320
487
|
TOutputs extends Record<string, ComponentInputSpec> = Record<string, never>,
|
|
488
|
+
TKind extends ComponentKind = "composite",
|
|
489
|
+
TInputOptions extends Record<string, ComponentInputOptions> = Record<string, never>,
|
|
490
|
+
TOutputOptions extends Record<string, ComponentOutputOptions<TInputOptions>> = Record<
|
|
491
|
+
string,
|
|
492
|
+
never
|
|
493
|
+
>,
|
|
321
494
|
> = {
|
|
322
495
|
/**
|
|
323
496
|
* The type of the component.
|
|
@@ -334,12 +507,14 @@ export type Component<
|
|
|
334
507
|
*/
|
|
335
508
|
entities: Map<string, Entity>
|
|
336
509
|
|
|
337
|
-
(
|
|
510
|
+
<TCallInputs extends InputSpecMapToInputRefMap<TInputs> = InputSpecMapToInputRefMap<TInputs>>(
|
|
338
511
|
context: InputComponentParams<
|
|
339
512
|
{ [K in keyof TArgs]: z.input<TArgs[K]> },
|
|
340
513
|
InputSpecMapToInputRefMap<TInputs>
|
|
341
|
-
|
|
342
|
-
|
|
514
|
+
> & {
|
|
515
|
+
inputs?: ValidateCallInputs<TCallInputs, TInputs>
|
|
516
|
+
},
|
|
517
|
+
): ResolveOutputRefMapForCall<TOutputs, TOutputOptions, TInputOptions, TCallInputs, TKind>
|
|
343
518
|
|
|
344
519
|
/**
|
|
345
520
|
* The original create function.
|
|
@@ -353,14 +528,18 @@ export function defineComponent<
|
|
|
353
528
|
TType extends VersionedName = VersionedName,
|
|
354
529
|
TArgs extends Record<string, ComponentArgumentOptions> = Record<string, never>,
|
|
355
530
|
TInputs extends Record<string, ComponentInputOptions> = Record<string, never>,
|
|
356
|
-
TOutputs extends Record<string,
|
|
531
|
+
TOutputs extends Record<string, ComponentOutputOptions<TInputs>> = Record<string, never>,
|
|
532
|
+
TKind extends ComponentKind = "composite",
|
|
357
533
|
>(
|
|
358
|
-
options: ComponentOptions<TType, TArgs, TInputs, TOutputs
|
|
534
|
+
options: ComponentOptions<TType, TArgs, TInputs, TOutputs> & { [kind]?: TKind },
|
|
359
535
|
): Component<
|
|
360
536
|
TType,
|
|
361
537
|
{ [K in keyof TArgs]: ComponentArgumentOptionsToSchema<TArgs[K]> },
|
|
362
538
|
{ [K in keyof TInputs]: ComponentInputOptionsToSpec<TInputs[K]> },
|
|
363
|
-
{ [K in keyof TOutputs]:
|
|
539
|
+
{ [K in keyof TOutputs]: ComponentOutputOptionsToSpec<TOutputs[K], TInputs> },
|
|
540
|
+
TKind,
|
|
541
|
+
TInputs,
|
|
542
|
+
TOutputs
|
|
364
543
|
> {
|
|
365
544
|
try {
|
|
366
545
|
componentModelSchema.shape.type.parse(options.type)
|
|
@@ -374,13 +553,14 @@ export function defineComponent<
|
|
|
374
553
|
|
|
375
554
|
const entities = new Map<string, Entity>()
|
|
376
555
|
const mapInput = createInputMapper(entities)
|
|
556
|
+
const mapOutput = createOutputMapper(options.inputs, entities)
|
|
377
557
|
|
|
378
558
|
const model: ComponentModel = {
|
|
379
559
|
type: options.type,
|
|
380
560
|
kind: options[kind] ?? "composite",
|
|
381
561
|
args: mapValues(options.args ?? {}, mapArgument),
|
|
382
562
|
inputs: mapValues(options.inputs ?? {}, mapInput),
|
|
383
|
-
outputs: mapValues(options.outputs ?? {},
|
|
563
|
+
outputs: mapValues(options.outputs ?? {}, mapOutput),
|
|
384
564
|
meta: {
|
|
385
565
|
...options.meta,
|
|
386
566
|
title: options.meta?.title || camelCaseToHumanReadable(parseVersionedName(options.type)[0]),
|
|
@@ -392,43 +572,45 @@ export function defineComponent<
|
|
|
392
572
|
definitionHash: null!,
|
|
393
573
|
}
|
|
394
574
|
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
575
|
+
type RuntimeCreateInputGroup =
|
|
576
|
+
| RuntimeInput
|
|
577
|
+
| InstanceInput
|
|
578
|
+
| (Array<RuntimeCreateInputGroup> & Partial<{ [boundaryInput]: InstanceInput }>)
|
|
579
|
+
|
|
580
|
+
function create(params: InputComponentParams<any, Record<string, RuntimeCreateInputGroup>>): any {
|
|
398
581
|
const { name, args = {}, inputs } = params
|
|
399
582
|
const instanceId = getInstanceId(options.type, name)
|
|
400
583
|
|
|
401
|
-
const flatInputs: Record<string,
|
|
584
|
+
const flatInputs: Record<string, RuntimeInput[]> = {}
|
|
402
585
|
const tracedInputs: Record<string, InstanceInput[]> = {}
|
|
586
|
+
const runtimeInputKey = (input: RuntimeInput): string => {
|
|
587
|
+
if (input.provided) {
|
|
588
|
+
return inputKey(input)
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
return `missing:${input[boundaryInput].instanceId}:${input[boundaryInput].output}`
|
|
592
|
+
}
|
|
403
593
|
|
|
404
594
|
for (const [key, inputGroup] of Object.entries(inputs ?? {})) {
|
|
595
|
+
if (!(key in model.inputs)) {
|
|
596
|
+
continue
|
|
597
|
+
}
|
|
598
|
+
|
|
405
599
|
if (!inputGroup) {
|
|
406
600
|
continue
|
|
407
601
|
}
|
|
408
602
|
|
|
409
603
|
if (!Array.isArray(inputGroup)) {
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
flatInputs[key] = [inputGroup]
|
|
604
|
+
const normalizedInput = normalizeIncomingInput(inputGroup, key)
|
|
605
|
+
tracedInputs[key] = [normalizedInput[boundaryInput]]
|
|
606
|
+
flatInputs[key] = [normalizedInput]
|
|
415
607
|
continue
|
|
416
608
|
}
|
|
417
609
|
|
|
418
|
-
|
|
419
|
-
const group: InstanceInput[] = [...(inputGroup[boundaryInputs] ?? [])]
|
|
420
|
-
const inputs: InstanceInput[] = []
|
|
421
|
-
|
|
422
|
-
for (const item of inputGroup.flat(1)) {
|
|
423
|
-
if (item[boundaryInput]) {
|
|
424
|
-
group.push(item[boundaryInput])
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
inputs.push(item)
|
|
428
|
-
}
|
|
610
|
+
const { group, inputs } = normalizeIncomingInputGroup(inputGroup, key)
|
|
429
611
|
|
|
430
612
|
tracedInputs[key] = uniqueBy(group, inputKey)
|
|
431
|
-
flatInputs[key] = uniqueBy(inputs,
|
|
613
|
+
flatInputs[key] = uniqueBy(inputs, runtimeInputKey)
|
|
432
614
|
}
|
|
433
615
|
|
|
434
616
|
return registerInstance(
|
|
@@ -440,38 +622,43 @@ export function defineComponent<
|
|
|
440
622
|
name,
|
|
441
623
|
args,
|
|
442
624
|
inputs: tracedInputs,
|
|
443
|
-
resolvedInputs: mapValues(flatInputs, inputs =>
|
|
444
|
-
inputs
|
|
445
|
-
|
|
625
|
+
resolvedInputs: mapValues(flatInputs, inputs => {
|
|
626
|
+
return inputs
|
|
627
|
+
.filter(input => input.provided)
|
|
628
|
+
.map(input => ({
|
|
629
|
+
instanceId: input.instanceId,
|
|
630
|
+
output: input.output,
|
|
631
|
+
...(input.path ? { path: input.path } : {}),
|
|
632
|
+
}))
|
|
633
|
+
}),
|
|
446
634
|
},
|
|
447
635
|
() => {
|
|
448
636
|
const markedInputs = mapValues(model.inputs, (componentInput, key) => {
|
|
449
637
|
if (!componentInput.multiple) {
|
|
450
|
-
// for single component use first available input and attach boundaryInput
|
|
451
|
-
// technically, caller can pass multiple inputs or array of inputs ignoring the TypeScript restriction,
|
|
452
|
-
// but we don't care about that
|
|
453
638
|
const input = flatInputs[key]?.[0]
|
|
454
639
|
|
|
455
|
-
if (input) {
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
640
|
+
if (input?.provided) {
|
|
641
|
+
return createDeepOutputAccessor({
|
|
642
|
+
...input,
|
|
643
|
+
[boundaryInput]: { instanceId: instanceId, output: key },
|
|
644
|
+
})
|
|
459
645
|
}
|
|
460
646
|
|
|
461
|
-
|
|
462
|
-
return { provided: false, [boundaryInput]: { instanceId: instanceId, output: key } }
|
|
647
|
+
return createNonProvidedInput({ instanceId: instanceId, output: key })
|
|
463
648
|
}
|
|
464
649
|
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
650
|
+
const inputs = (flatInputs[key] ?? []).filter(isProvidedRuntimeInput).map(input =>
|
|
651
|
+
createDeepOutputAccessor({
|
|
652
|
+
...input,
|
|
653
|
+
[boundaryInput]: { instanceId: instanceId, output: key },
|
|
654
|
+
}),
|
|
655
|
+
)
|
|
470
656
|
|
|
471
|
-
|
|
657
|
+
const multipleBoundary = { instanceId, output: key }
|
|
658
|
+
return createMultipleInputAccessor(inputs, multipleBoundary)
|
|
472
659
|
})
|
|
473
660
|
|
|
474
|
-
const outputs: Record<string,
|
|
661
|
+
const outputs: Record<string, RuntimeInput | MultipleInput | null | undefined> =
|
|
475
662
|
options.create({
|
|
476
663
|
id: instanceId,
|
|
477
664
|
name,
|
|
@@ -479,9 +666,74 @@ export function defineComponent<
|
|
|
479
666
|
inputs: markedInputs as any,
|
|
480
667
|
}) ?? {}
|
|
481
668
|
|
|
482
|
-
|
|
669
|
+
const normalizedOutputs = normalizeCreateOutputs(outputs, model, instanceId)
|
|
670
|
+
|
|
671
|
+
return withDeepOutputAccessors(normalizedOutputs, model, instanceId)
|
|
483
672
|
},
|
|
484
673
|
)
|
|
674
|
+
|
|
675
|
+
function normalizeIncomingInput(
|
|
676
|
+
input: RuntimeInput | InstanceInput,
|
|
677
|
+
inputName: string,
|
|
678
|
+
): RuntimeInput {
|
|
679
|
+
if (isStableInstanceInput(input)) {
|
|
680
|
+
return createInput(input)
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
const runtimeInput = input as RuntimeInput
|
|
684
|
+
|
|
685
|
+
if (runtimeInput.provided) {
|
|
686
|
+
const inputBoundary = runtimeInput[boundaryInput] ?? {
|
|
687
|
+
instanceId: runtimeInput.instanceId,
|
|
688
|
+
output: runtimeInput.output,
|
|
689
|
+
...(runtimeInput.path ? { path: runtimeInput.path } : {}),
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
return createInput(runtimeInput, { boundary: inputBoundary })
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
return createNonProvidedInput(
|
|
696
|
+
runtimeInput[boundaryInput] ?? { instanceId, output: inputName },
|
|
697
|
+
)
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
function normalizeIncomingInputGroup(
|
|
701
|
+
inputGroup: RuntimeCreateInputGroup,
|
|
702
|
+
inputName: string,
|
|
703
|
+
): { group: InstanceInput[]; inputs: RuntimeInput[] } {
|
|
704
|
+
const group: InstanceInput[] = []
|
|
705
|
+
const inputs: RuntimeInput[] = []
|
|
706
|
+
|
|
707
|
+
const visit = (value: RuntimeCreateInputGroup): void => {
|
|
708
|
+
if (Array.isArray(value)) {
|
|
709
|
+
const arrayBoundary = (value as Partial<{ [boundaryInput]: InstanceInput }>)[
|
|
710
|
+
boundaryInput
|
|
711
|
+
]
|
|
712
|
+
|
|
713
|
+
if (arrayBoundary) {
|
|
714
|
+
group.push(arrayBoundary)
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
for (const item of value) {
|
|
718
|
+
if (!item) {
|
|
719
|
+
continue
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
visit(item)
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
return
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
const normalizedInput = normalizeIncomingInput(value, inputName)
|
|
729
|
+
group.push(normalizedInput[boundaryInput])
|
|
730
|
+
inputs.push(normalizedInput)
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
visit(inputGroup)
|
|
734
|
+
|
|
735
|
+
return { group, inputs }
|
|
736
|
+
}
|
|
485
737
|
}
|
|
486
738
|
|
|
487
739
|
try {
|
|
@@ -526,6 +778,131 @@ function processArgs(
|
|
|
526
778
|
return validatedArgs
|
|
527
779
|
}
|
|
528
780
|
|
|
781
|
+
function withDeepOutputAccessors(
|
|
782
|
+
outputs: Record<string, RuntimeInput[]>,
|
|
783
|
+
model: ComponentModel,
|
|
784
|
+
instanceId: InstanceId,
|
|
785
|
+
): Record<string, RuntimeInput | MultipleInput> {
|
|
786
|
+
return mapValues(outputs, (outputGroup, outputName) => {
|
|
787
|
+
const outputSpec = model.outputs[outputName]
|
|
788
|
+
if (!outputSpec) {
|
|
789
|
+
return outputGroup[0]!
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
const normalizedOutputs = outputGroup.map(output => createDeepOutputAccessor(output))
|
|
793
|
+
|
|
794
|
+
if (outputSpec.multiple) {
|
|
795
|
+
return createMultipleInputAccessor(normalizedOutputs, {
|
|
796
|
+
instanceId,
|
|
797
|
+
output: outputName,
|
|
798
|
+
})
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
return normalizedOutputs[0]!
|
|
802
|
+
})
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
function isProvidedRuntimeInput(input: RuntimeInput): input is RequiredInput {
|
|
806
|
+
return input.provided
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
function normalizeCreateOutputGroup(
|
|
810
|
+
outputGroup: RuntimeInput | InstanceInput | Array<RuntimeInput | InstanceInput>,
|
|
811
|
+
instanceId: InstanceId,
|
|
812
|
+
outputName: string,
|
|
813
|
+
): RuntimeInput[] {
|
|
814
|
+
return [outputGroup]
|
|
815
|
+
.flat(2)
|
|
816
|
+
.filter(isNonNullish)
|
|
817
|
+
.map(output => {
|
|
818
|
+
if (isStableInstanceInput(output)) {
|
|
819
|
+
return createInput(output)
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
const runtimeOutput = output as RuntimeInput
|
|
823
|
+
|
|
824
|
+
if (runtimeOutput.provided) {
|
|
825
|
+
const stableBoundary = runtimeOutput[boundaryInput] ?? {
|
|
826
|
+
instanceId: runtimeOutput.instanceId,
|
|
827
|
+
output: runtimeOutput.output,
|
|
828
|
+
...(runtimeOutput.path ? { path: runtimeOutput.path } : {}),
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
return createInput(runtimeOutput, { boundary: stableBoundary })
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
return createNonProvidedInput(
|
|
835
|
+
runtimeOutput[boundaryInput] ?? { instanceId, output: outputName },
|
|
836
|
+
)
|
|
837
|
+
})
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
function normalizeCreateOutputs(
|
|
841
|
+
outputs: Record<string, RuntimeInput | MultipleInput | null | undefined>,
|
|
842
|
+
model: ComponentModel,
|
|
843
|
+
instanceId: InstanceId,
|
|
844
|
+
): Record<string, RuntimeInput[]> {
|
|
845
|
+
const normalizedOutputs: Record<string, RuntimeInput[]> = {}
|
|
846
|
+
|
|
847
|
+
for (const [outputName, outputSpec] of Object.entries(model.outputs)) {
|
|
848
|
+
const outputGroup = outputs[outputName]
|
|
849
|
+
|
|
850
|
+
if (!isNonNullish(outputGroup)) {
|
|
851
|
+
if (model.kind === "unit") {
|
|
852
|
+
throw new Error(`Unit output "${outputName}" in instance "${instanceId}" must be provided`)
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
normalizedOutputs[outputName] = outputSpec.multiple
|
|
856
|
+
? []
|
|
857
|
+
: [createNonProvidedInput({ instanceId, output: outputName })]
|
|
858
|
+
continue
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
const normalizedGroup = normalizeCreateOutputGroup(outputGroup, instanceId, outputName)
|
|
862
|
+
|
|
863
|
+
if (outputSpec.multiple && normalizedGroup.some(output => !output.provided)) {
|
|
864
|
+
throw new Error(
|
|
865
|
+
`Multiple output "${outputName}" in instance "${instanceId}" cannot contain non-provided items`,
|
|
866
|
+
)
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
if (model.kind === "unit") {
|
|
870
|
+
if (normalizedGroup.length === 0 || normalizedGroup.some(output => !output.provided)) {
|
|
871
|
+
throw new Error(`Unit output "${outputName}" in instance "${instanceId}" must be provided`)
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
normalizedOutputs[outputName] =
|
|
876
|
+
outputSpec.multiple || normalizedGroup.length > 0
|
|
877
|
+
? normalizedGroup
|
|
878
|
+
: [createNonProvidedInput({ instanceId, output: outputName })]
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
for (const [outputName, outputGroup] of Object.entries(pickBy(outputs, isNonNullish))) {
|
|
882
|
+
if (!(outputName in model.outputs) || outputName in normalizedOutputs) {
|
|
883
|
+
continue
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
normalizedOutputs[outputName] = normalizeCreateOutputGroup(outputGroup, instanceId, outputName)
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
return normalizedOutputs
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
function isStableInstanceInput(value: unknown): value is InstanceInput {
|
|
893
|
+
if (!value || typeof value !== "object") {
|
|
894
|
+
return false
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
if ("provided" in value) {
|
|
898
|
+
return false
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
const input = value as Partial<InstanceInput>
|
|
902
|
+
|
|
903
|
+
return typeof input.instanceId === "string" && typeof input.output === "string"
|
|
904
|
+
}
|
|
905
|
+
|
|
529
906
|
/**
|
|
530
907
|
* Checks if the value is a Highstate component.
|
|
531
908
|
*
|
|
@@ -602,6 +979,44 @@ export function createInputMapper(entities: Map<string, Entity>) {
|
|
|
602
979
|
}
|
|
603
980
|
}
|
|
604
981
|
|
|
982
|
+
function isFromInputOutputOptions(
|
|
983
|
+
value: ComponentOutputOptions,
|
|
984
|
+
): value is FromInputComponentOutputOptions {
|
|
985
|
+
return typeof value === "object" && value !== null && "fromInput" in value
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
function createOutputMapper(
|
|
989
|
+
inputs: Record<string, ComponentInputOptions> | undefined,
|
|
990
|
+
entities: Map<string, Entity>,
|
|
991
|
+
) {
|
|
992
|
+
const mapInput = createInputMapper(entities)
|
|
993
|
+
|
|
994
|
+
return (value: ComponentOutputOptions, key: string): ComponentInput => {
|
|
995
|
+
if (!isFromInputOutputOptions(value)) {
|
|
996
|
+
return mapInput(value, key)
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
const sourceInput = inputs?.[value.fromInput]
|
|
1000
|
+
if (!sourceInput) {
|
|
1001
|
+
throw new Error(
|
|
1002
|
+
`Output "${key}" references missing input "${value.fromInput}" via fromInput.`,
|
|
1003
|
+
)
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
const mappedInput = mapInput(sourceInput, value.fromInput)
|
|
1007
|
+
|
|
1008
|
+
return {
|
|
1009
|
+
...mappedInput,
|
|
1010
|
+
fromInput: value.fromInput,
|
|
1011
|
+
meta: {
|
|
1012
|
+
...mappedInput.meta,
|
|
1013
|
+
...value.meta,
|
|
1014
|
+
title: value.meta?.title || camelCaseToHumanReadable(key),
|
|
1015
|
+
},
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
|
|
605
1020
|
/**
|
|
606
1021
|
* Formats the instance id from the instance type and instance name.
|
|
607
1022
|
*
|
|
@@ -635,6 +1050,26 @@ function toFullComponentInputOptions<T extends Record<string, ComponentInputOpti
|
|
|
635
1050
|
return mapValues(inputs, input => ("entity" in input ? input : { entity: input })) as any
|
|
636
1051
|
}
|
|
637
1052
|
|
|
1053
|
+
type ToFullComponentOutputOptions<T extends Record<string, ComponentOutputOptions>> = Simplify<{
|
|
1054
|
+
[K in keyof T]: T[K] extends Entity ? { entity: T[K] } : T[K]
|
|
1055
|
+
}>
|
|
1056
|
+
|
|
1057
|
+
function toFullComponentOutputOptions<T extends Record<string, ComponentOutputOptions>>(
|
|
1058
|
+
outputs: T,
|
|
1059
|
+
): ToFullComponentOutputOptions<T> {
|
|
1060
|
+
return mapValues(outputs, output => {
|
|
1061
|
+
if (typeof output !== "object" || output === null) {
|
|
1062
|
+
return { entity: output }
|
|
1063
|
+
}
|
|
1064
|
+
|
|
1065
|
+
if ("entity" in output || "fromInput" in output) {
|
|
1066
|
+
return output
|
|
1067
|
+
}
|
|
1068
|
+
|
|
1069
|
+
return { entity: output }
|
|
1070
|
+
}) as any
|
|
1071
|
+
}
|
|
1072
|
+
|
|
638
1073
|
/**
|
|
639
1074
|
* The helper marker for component arguments.
|
|
640
1075
|
*
|
|
@@ -662,10 +1097,10 @@ export function $inputs<T extends Record<string, ComponentInputOptions>>(
|
|
|
662
1097
|
*
|
|
663
1098
|
* Helps validating outputs types and gives the compiler a hint for generating `meta` fields.
|
|
664
1099
|
*/
|
|
665
|
-
export function $outputs<T extends Record<string,
|
|
1100
|
+
export function $outputs<T extends Record<string, ComponentOutputOptions>>(
|
|
666
1101
|
outputs: T,
|
|
667
|
-
):
|
|
668
|
-
return
|
|
1102
|
+
): ToFullComponentOutputOptions<T> {
|
|
1103
|
+
return toFullComponentOutputOptions(outputs)
|
|
669
1104
|
}
|
|
670
1105
|
|
|
671
1106
|
/**
|