@highstate/contract 0.19.1 → 0.21.1

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/src/instance.ts CHANGED
@@ -1,7 +1,8 @@
1
- import type { Entity, EntityTypes } from "./entity"
2
- import type { boundaryInput, boundaryInputs } from "./evaluation"
1
+ /** biome-ignore-all lint/suspicious/noExplicitAny: for simplicity of the API, maybe fix later */
2
+
3
+ import type { Entity, EntityIncludeRef, EntityTypes } from "./entity"
3
4
  import { z } from "zod"
4
- import { componentKindSchema } from "./component"
5
+ import { createInput, createNonProvidedInput } from "./instance-input"
5
6
  import {
6
7
  type GenericName,
7
8
  genericNameSchema,
@@ -9,36 +10,235 @@ import {
9
10
  type VersionedName,
10
11
  versionedNameSchema,
11
12
  } from "./meta"
13
+ import { boundaryInput, componentKindSchema } from "./shared"
12
14
 
13
15
  declare const type: unique symbol
14
16
 
15
- export type InstanceInput<TTypes extends EntityTypes = EntityTypes> = {
17
+ export type InstanceInput = z.infer<typeof instanceInputSchema>
18
+
19
+ export type RuntimeInput<
20
+ TTypes extends EntityTypes = EntityTypes,
21
+ TPath extends string | undefined = string | undefined,
22
+ > = {
16
23
  [type]?: TTypes
17
- [boundaryInput]?: InstanceInput
18
- instanceId: InstanceId
19
- output: string
24
+ [boundaryInput]: InstanceInput
25
+ } & (
26
+ | ({
27
+ provided: true
28
+ instanceId: InstanceId
29
+ output: string
30
+ } & (undefined extends TPath ? { path?: string } : { path: TPath }))
31
+ | {
32
+ provided: false
33
+ path?: string
34
+ }
35
+ )
36
+
37
+ export type MultipleInput<
38
+ TInput extends RuntimeInput = RuntimeInput,
39
+ TSelectors extends Record<string, unknown> = Record<never, never>,
40
+ > = TInput[] &
41
+ TSelectors & {
42
+ [boundaryInput]: InstanceInput
43
+ path?: string
44
+ }
45
+
46
+ type RequiredInputChildValue<TValue> = TValue extends RuntimeInput
47
+ ? RequiredInput<TValue>
48
+ : TValue extends MultipleInput<infer TItem, infer TSelectors>
49
+ ? MultipleInput<RequiredInput<TItem>, RequiredInputChildSelectors<TSelectors>>
50
+ : TValue extends Array<infer TItem>
51
+ ? Array<RequiredInputChildValue<TItem>>
52
+ : TValue
53
+
54
+ type RequiredInputChildSelectors<TSelectors extends Record<string, unknown>> = {
55
+ [K in keyof TSelectors]: RequiredInputChildValue<TSelectors[K]>
56
+ }
57
+
58
+ type RequiredInputChildren<TInput extends RuntimeInput> = {
59
+ [K in keyof TInput as K extends
60
+ | "instanceId"
61
+ | "output"
62
+ | "path"
63
+ | "provided"
64
+ | typeof boundaryInput
65
+ | typeof type
66
+ ? never
67
+ : K]: RequiredInputChildValue<TInput[K]>
20
68
  }
21
69
 
70
+ type RequiredInputProvidedBranch<TInput extends RuntimeInput> = Extract<TInput, { provided: true }>
71
+
72
+ type StrictRequiredInput<TInput extends RuntimeInput> = RequiredInputProvidedBranch<TInput> &
73
+ RequiredInputChildren<RequiredInputProvidedBranch<TInput>>
74
+
75
+ export type RequiredInput<TInput extends RuntimeInput = RuntimeInput> = StrictRequiredInput<TInput>
76
+
77
+ type RequiredMultipleInputSelectors<TInput extends RuntimeInput> = LiftMultipleSelectorFields<
78
+ RequiredInputChildren<RequiredInputProvidedBranch<TInput>>
79
+ >
80
+
81
+ export type RequiredMultipleInput<TInput extends RuntimeInput = RuntimeInput> = MultipleInput<
82
+ RequiredInput<TInput>,
83
+ RequiredMultipleInputSelectors<TInput>
84
+ >
85
+
86
+ export type RequiredMultipleInputs<TInput extends RuntimeInput = RuntimeInput> =
87
+ RequiredMultipleInput<TInput>
88
+
22
89
  export type EntityInput<TEntity extends Entity> = TEntity extends Entity<
23
90
  VersionedName,
24
- infer TImplementedTypes
91
+ infer TImplementedTypes,
92
+ z.ZodTypeAny,
93
+ unknown,
94
+ unknown,
95
+ infer TIncludes
25
96
  >
26
- ? InstanceInput<TImplementedTypes>
97
+ ? RuntimeInput<TImplementedTypes, undefined> &
98
+ (TIncludes extends Record<string, EntityIncludeRef>
99
+ ? EntityInputIncludes<TIncludes>
100
+ : Record<never, never>)
27
101
  : never
28
102
 
29
- export type OptionalInstanceInput<TTypes extends EntityTypes = EntityTypes> =
30
- | ({ provided: true } & InstanceInput<TTypes>)
31
- | { provided: false; [boundaryInput]?: InstanceInput }
103
+ export type RequiredEntityInput<TEntity extends Entity> = RequiredInput<EntityInput<TEntity>>
104
+
105
+ type EntityInputDepth = [1, 1, 1, 1]
106
+
107
+ type DecDepth<T extends readonly unknown[]> = T extends readonly [unknown, ...infer Rest]
108
+ ? Rest
109
+ : readonly []
110
+
111
+ type ResolveIncludeEntity<TIncludeRef> = TIncludeRef extends { entity: infer E }
112
+ ? E extends () => infer ER
113
+ ? ER
114
+ : E
115
+ : TIncludeRef
116
+
117
+ type IncludeMultiple<TIncludeRef> = TIncludeRef extends { multiple?: infer M }
118
+ ? M extends true
119
+ ? true
120
+ : false
121
+ : false
122
+
123
+ type IncludeRequired<TIncludeRef> = TIncludeRef extends { required?: infer R }
124
+ ? R extends false
125
+ ? false
126
+ : true
127
+ : true
128
+
129
+ type IncludeRefValue<
130
+ TIncludeRef,
131
+ TDepth extends readonly unknown[],
132
+ > = ResolveIncludeEntity<TIncludeRef> extends infer TEntity
133
+ ? TEntity extends Entity
134
+ ? IncludeMultiple<TIncludeRef> extends true
135
+ ? MultipleInput<
136
+ EntityInputWithDepth<TEntity, TDepth>,
137
+ TDepth extends readonly []
138
+ ? Record<never, never>
139
+ : TEntity extends Entity<
140
+ VersionedName,
141
+ EntityTypes,
142
+ z.ZodTypeAny,
143
+ unknown,
144
+ unknown,
145
+ infer TIncludes
146
+ >
147
+ ? TIncludes extends Record<string, EntityIncludeRef>
148
+ ? LiftMultipleSelectorFields<EntityInputIncludes<TIncludes, DecDepth<TDepth>>>
149
+ : Record<never, never>
150
+ : Record<never, never>
151
+ >
152
+ : IncludeRequired<TIncludeRef> extends false
153
+ ? OptionalEntityInput<TEntity, TDepth>
154
+ : EntityInputWithDepth<TEntity, TDepth>
155
+ : never
156
+ : never
32
157
 
33
- export type InstanceInputGroup<TTypes extends EntityTypes = EntityTypes> =
34
- InstanceInput<TTypes>[] & {
35
- [boundaryInputs]?: InstanceInputGroup<TTypes>
36
- }
158
+ type OptionalEntityInput<TEntity extends Entity, TDepth extends readonly unknown[]> =
159
+ | ({ provided: true } & EntityInputWithDepth<TEntity, TDepth>)
160
+ | ({ provided: false; [boundaryInput]: InstanceInput } & OptionalizedInputFields<
161
+ EntityInputWithDepth<TEntity, TDepth>
162
+ >)
163
+
164
+ type OptionalizeInputValue<T> = [T] extends [InstanceInput]
165
+ ? RuntimeInput
166
+ : T extends MultipleInput<infer TItem, infer TSelectors>
167
+ ? MultipleInput<OptionalizeInputValue<TItem>, OptionalizedSelectorFields<TSelectors>>
168
+ : T extends Array<infer TItem>
169
+ ? Array<OptionalizeInputValue<TItem>>
170
+ : T
171
+
172
+ type OptionalizedSelectorFields<TSelectors extends Record<string, unknown>> = {
173
+ [K in keyof TSelectors]: OptionalizeInputValue<TSelectors[K]>
174
+ }
175
+
176
+ type RuntimeInputSelectorFields<TInput extends RuntimeInput> = {
177
+ [K in keyof TInput as K extends
178
+ | "instanceId"
179
+ | "output"
180
+ | "path"
181
+ | "provided"
182
+ | typeof boundaryInput
183
+ | typeof type
184
+ ? never
185
+ : K]: LiftMultipleSelectorValue<TInput[K]>
186
+ }
187
+
188
+ type LiftMultipleSelectorValue<T> = T extends MultipleInput<infer TItem, infer TSelectors>
189
+ ? MultipleInput<TItem, LiftMultipleSelectorFields<TSelectors>>
190
+ : T extends RuntimeInput
191
+ ? MultipleInput<T, RuntimeInputSelectorFields<T>>
192
+ : T
193
+
194
+ type LiftMultipleSelectorFields<TSelectors extends Record<string, unknown>> = {
195
+ [K in keyof TSelectors]: LiftMultipleSelectorValue<TSelectors[K]>
196
+ }
37
197
 
38
- export function inputKey(input: InstanceInput): string {
39
- return `${input.instanceId}:${input.output}`
198
+ type OptionalizedInputFields<TInput extends RuntimeInput> = {
199
+ [K in keyof TInput as K extends "instanceId" | "output" | "provided" | typeof boundaryInput
200
+ ? never
201
+ : K]: OptionalizeInputValue<TInput[K]>
40
202
  }
41
203
 
204
+ type EntityInputWithDepth<
205
+ TEntity extends Entity,
206
+ TDepth extends readonly unknown[],
207
+ > = TEntity extends Entity<
208
+ VersionedName,
209
+ infer TImplementedTypes,
210
+ z.ZodTypeAny,
211
+ unknown,
212
+ unknown,
213
+ infer TIncludes
214
+ >
215
+ ? RuntimeInput<TImplementedTypes, undefined> &
216
+ (TDepth extends readonly []
217
+ ? Record<never, never>
218
+ : TIncludes extends Record<string, EntityIncludeRef>
219
+ ? EntityInputIncludes<TIncludes, DecDepth<TDepth>>
220
+ : Record<never, never>)
221
+ : never
222
+
223
+ type EntityInputIncludes<
224
+ TIncludes extends Record<string, EntityIncludeRef>,
225
+ TDepth extends readonly unknown[] = EntityInputDepth,
226
+ > = TIncludes extends Record<string, never>
227
+ ? Record<never, never>
228
+ : string extends keyof TIncludes
229
+ ? Record<never, never>
230
+ : {
231
+ [K in keyof TIncludes]: IncludeRefValue<TIncludes[K], TDepth>
232
+ }
233
+
234
+ export type InstanceInputGroup<TTypes extends EntityTypes = EntityTypes> = MultipleInput<
235
+ RuntimeInput<TTypes>
236
+ >
237
+
238
+ type SelectInputResult<TInput extends RuntimeInput> = TInput extends RequiredInput<infer TRuntime>
239
+ ? TRuntime
240
+ : TInput
241
+
42
242
  export const positionSchema = z.object({
43
243
  x: z.number(),
44
244
  y: z.number(),
@@ -53,6 +253,7 @@ export type InstanceId = z.infer<typeof instanceIdSchema>
53
253
  export const instanceInputSchema = z.object({
54
254
  instanceId: instanceIdSchema,
55
255
  output: z.string(),
256
+ path: z.string().optional(),
56
257
  })
57
258
 
58
259
  export const hubInputSchema = z.object({
@@ -209,52 +410,52 @@ export function parseInstanceId(
209
410
  return parts as [VersionedName, GenericName]
210
411
  }
211
412
 
212
- export function findInput<T extends EntityTypes>(
213
- inputs: InstanceInput<T>[],
413
+ /**
414
+ * Selects a single input from a group by instance id or instance name.
415
+ *
416
+ * The function accepts a plain array but still preserves Highstate boundary semantics.
417
+ * It first tries to read boundary metadata from the array object itself and then falls back
418
+ * to the first item boundary when needed.
419
+ *
420
+ * @param inputs The input group to search in.
421
+ * @param name The instance id or instance name to select.
422
+ * @returns The selected input with inherited group boundary, or a provided-false input when missing.
423
+ */
424
+ export function selectInput<TInput extends RuntimeInput>(
425
+ inputs: TInput[] & Partial<{ [boundaryInput]: InstanceInput }>,
214
426
  name: string,
215
- ): InstanceInput<T> | null {
216
- const matchedInputs = inputs.filter(
217
- input => parseInstanceId(input.instanceId)[1] === name || input.instanceId === name,
218
- )
219
-
220
- if (matchedInputs.length === 0) {
221
- return null
222
- }
427
+ ): SelectInputResult<TInput> {
428
+ const groupBoundary = inputs[boundaryInput] ?? inputs[0]?.[boundaryInput]
223
429
 
224
- if (matchedInputs.length > 1) {
430
+ if (inputs.length === 0 && !groupBoundary) {
225
431
  throw new Error(
226
- `Multiple inputs found for "${name}": ${matchedInputs.map(input => input.instanceId).join(", ")}. Specify the full instance id to disambiguate.`,
432
+ `Cannot select input "${name}": empty input group has no boundary metadata to build a missing input reference.`,
227
433
  )
228
434
  }
229
435
 
230
- return matchedInputs[0]
231
- }
436
+ const input = inputs.find(
437
+ input =>
438
+ input.provided &&
439
+ (input.instanceId === name || parseInstanceId(input.instanceId)[1] === name),
440
+ )
232
441
 
233
- export function findRequiredInput<T extends EntityTypes>(
234
- inputs: InstanceInput<T>[],
235
- name: string,
236
- ): InstanceInput<T> {
237
- const input = findInput(inputs, name)
442
+ if (!input || !input.provided) {
443
+ const fallbackBoundary =
444
+ groupBoundary ??
445
+ inputs.find(input => Boolean(input[boundaryInput]))?.[boundaryInput] ??
446
+ inputs[0]?.[boundaryInput]
238
447
 
239
- if (input === null) {
240
- throw new Error(`Required input "${name}" not found.`)
241
- }
448
+ if (!fallbackBoundary) {
449
+ throw new Error(
450
+ `Cannot select input "${name}": input group has no boundary metadata to build a missing input reference.`,
451
+ )
452
+ }
242
453
 
243
- return input
244
- }
245
-
246
- export function findInputs<T extends EntityTypes>(
247
- inputs: InstanceInput<T>[],
248
- names: string[],
249
- ): InstanceInput<T>[] {
250
- return names.map(name => findInput(inputs, name)).filter(Boolean) as InstanceInput<T>[]
251
- }
454
+ return createNonProvidedInput(fallbackBoundary) as SelectInputResult<TInput>
455
+ }
252
456
 
253
- export function findRequiredInputs<T extends EntityTypes>(
254
- inputs: InstanceInput<T>[],
255
- names: string[],
256
- ): InstanceInput<T>[] {
257
- return names.map(name => findRequiredInput(inputs, name))
457
+ const boundary = groupBoundary ?? input[boundaryInput]
458
+ return createInput(input, { boundary }) as SelectInputResult<TInput>
258
459
  }
259
460
 
260
461
  /**
@@ -265,8 +466,7 @@ export function findRequiredInputs<T extends EntityTypes>(
265
466
  export enum HighstateSignature {
266
467
  Artifact = "d55c63ac-3174-4756-808f-f778e99af0d1",
267
468
  Yaml = "c857cac5-caa6-4421-b82c-e561fbce6367",
268
- Id = "348d020e-0d9e-4ae7-9415-b91af99f5339",
269
- Ref = "6d7f9da0-9cb6-496d-b72e-cf85ee4d9cf8",
469
+ Secret = "240e5789-6ae4-4b22-b9d8-87169e8b4bab",
270
470
  }
271
471
 
272
472
  export const yamlValueSchema = z.object({
@@ -274,17 +474,6 @@ export const yamlValueSchema = z.object({
274
474
  value: z.string(),
275
475
  })
276
476
 
277
- export const objectWithIdSchema = z.object({
278
- [HighstateSignature.Id]: z.literal(true),
279
- id: z.number(),
280
- value: z.unknown(),
281
- })
282
-
283
- export const objectRefSchema = z.object({
284
- [HighstateSignature.Ref]: z.literal(true),
285
- id: z.number(),
286
- })
287
-
288
477
  export type YamlValue = z.infer<typeof yamlValueSchema>
289
478
 
290
479
  export const fileMetaSchema = z.object({
@@ -351,3 +540,49 @@ export const instanceStatusFieldSchema = z.object({
351
540
 
352
541
  export type InstanceStatusFieldValue = z.infer<typeof instanceStatusFieldValueSchema>
353
542
  export type InstanceStatusField = z.infer<typeof instanceStatusFieldSchema>
543
+
544
+ export function secretSchema<TSchema extends z.ZodType>(
545
+ schema: TSchema,
546
+ ): z.ZodCodec<
547
+ z.ZodUnion<
548
+ [
549
+ z.ZodObject<{
550
+ [HighstateSignature.Secret]: z.ZodLiteral<true>
551
+ value: TSchema
552
+ }>,
553
+ TSchema,
554
+ ]
555
+ >,
556
+ z.ZodObject<{
557
+ [HighstateSignature.Secret]: z.ZodLiteral<true>
558
+ value: TSchema
559
+ }>
560
+ > {
561
+ const secretType = z.object({
562
+ [HighstateSignature.Secret]: z.literal(true),
563
+ value: schema,
564
+ })
565
+
566
+ return z.codec(z.union([secretType, schema]), secretType, {
567
+ decode: value =>
568
+ typeof value === "object" && value !== null && HighstateSignature.Secret in value
569
+ ? (value as any)
570
+ : { [HighstateSignature.Secret]: true, value },
571
+ encode: value => value as any,
572
+ })
573
+ }
574
+
575
+ export type Secret<T> = {
576
+ [HighstateSignature.Secret]: true
577
+ value: T
578
+ }
579
+
580
+ /**
581
+ * Checks if the given value is a Secret.
582
+ *
583
+ * @param value The value to check.
584
+ * @returns True if the value is a Secret, false otherwise.
585
+ */
586
+ export function isSecret<T>(value: unknown): value is Secret<T> {
587
+ return typeof value === "object" && value !== null && HighstateSignature.Secret in value
588
+ }
@@ -0,0 +1,115 @@
1
+ function isRecord(value: unknown): value is Record<string, unknown> {
2
+ return typeof value === "object" && value !== null && !Array.isArray(value)
3
+ }
4
+
5
+ /**
6
+ * Normalizes JSON schema produced by Zod exports.
7
+ *
8
+ * In particular, it merges intersections of object schemas represented as
9
+ * `allOf: [{ type: "object", ... }, { type: "object", ... }, ...]` into a single
10
+ * object schema so that validators treat `additionalProperties` consistently.
11
+ */
12
+ export function fixJsonSchema(schema: unknown): unknown {
13
+ if (!isRecord(schema)) {
14
+ return schema
15
+ }
16
+
17
+ const allOf = schema.allOf
18
+ if (Array.isArray(allOf) && allOf.length > 0) {
19
+ const objectSchemas: Record<string, unknown>[] = []
20
+ const otherSchemas: unknown[] = []
21
+
22
+ for (const item of allOf) {
23
+ if (isRecord(item) && item.type === "object") {
24
+ objectSchemas.push(item)
25
+ continue
26
+ }
27
+
28
+ otherSchemas.push(item)
29
+ }
30
+
31
+ if (objectSchemas.length > 1) {
32
+ const required = new Set<string>()
33
+ const properties: Record<string, unknown> = {}
34
+ let additionalProperties: unknown
35
+
36
+ for (const objectSchema of objectSchemas) {
37
+ const objectRequired = objectSchema.required
38
+ if (Array.isArray(objectRequired)) {
39
+ for (const key of objectRequired) {
40
+ if (typeof key === "string") {
41
+ required.add(key)
42
+ }
43
+ }
44
+ }
45
+
46
+ const objectProperties = objectSchema.properties
47
+ if (isRecord(objectProperties)) {
48
+ for (const [key, value] of Object.entries(objectProperties)) {
49
+ const existing = properties[key]
50
+ if (existing === undefined) {
51
+ properties[key] = value
52
+ continue
53
+ }
54
+
55
+ properties[key] = { allOf: [existing, value] }
56
+ }
57
+ }
58
+
59
+ if ("additionalProperties" in objectSchema) {
60
+ if (additionalProperties === undefined) {
61
+ additionalProperties = objectSchema.additionalProperties
62
+ } else if (additionalProperties !== objectSchema.additionalProperties) {
63
+ additionalProperties = false
64
+ }
65
+ }
66
+ }
67
+
68
+ const mergedObjectSchema: Record<string, unknown> = {
69
+ type: "object",
70
+ properties,
71
+ ...(required.size > 0 ? { required: Array.from(required) } : {}),
72
+ ...(additionalProperties !== undefined
73
+ ? { additionalProperties }
74
+ : { additionalProperties: false }),
75
+ }
76
+
77
+ const merged =
78
+ otherSchemas.length === 0
79
+ ? mergedObjectSchema
80
+ : {
81
+ ...schema,
82
+ allOf: [mergedObjectSchema, ...otherSchemas],
83
+ }
84
+
85
+ return fixJsonSchema(merged)
86
+ }
87
+ }
88
+
89
+ const next: Record<string, unknown> = { ...schema }
90
+
91
+ if (Array.isArray(next.allOf)) {
92
+ next.allOf = next.allOf.map(fixJsonSchema)
93
+ }
94
+ if (Array.isArray(next.anyOf)) {
95
+ next.anyOf = next.anyOf.map(fixJsonSchema)
96
+ }
97
+ if (Array.isArray(next.oneOf)) {
98
+ next.oneOf = next.oneOf.map(fixJsonSchema)
99
+ }
100
+ if (isRecord(next.properties)) {
101
+ next.properties = Object.fromEntries(
102
+ Object.entries(next.properties).map(([key, value]) => [key, fixJsonSchema(value)]),
103
+ )
104
+ }
105
+ if (isRecord(next.items)) {
106
+ next.items = fixJsonSchema(next.items)
107
+ } else if (Array.isArray(next.items)) {
108
+ next.items = next.items.map(fixJsonSchema)
109
+ }
110
+ if (isRecord(next.additionalProperties)) {
111
+ next.additionalProperties = fixJsonSchema(next.additionalProperties)
112
+ }
113
+
114
+ return next
115
+ }
package/src/pulumi.ts CHANGED
@@ -1,33 +1,49 @@
1
1
  import { parse } from "yaml"
2
2
  import { z } from "zod"
3
- import { entityInclusionSchema } from "./entity"
4
3
  import {
5
4
  fileMetaSchema,
6
5
  HighstateSignature,
7
- instanceIdSchema,
8
6
  instanceInputSchema,
7
+ secretSchema,
9
8
  yamlValueSchema,
10
9
  } from "./instance"
11
10
  import { commonObjectMetaSchema } from "./meta"
12
11
  import { triggerInvocationSchema } from "./trigger"
13
12
 
14
- export type UnitInputReference = z.infer<typeof unitInputReference>
15
-
16
- export const unitInputReference = z.object({
13
+ export const unitInputSourceSchema = z.object({
17
14
  ...instanceInputSchema.shape,
15
+ })
18
16
 
17
+ export type UnitInputSource = z.infer<typeof unitInputSourceSchema>
18
+
19
+ export const unitInputValueSchema = z.object({
19
20
  /**
20
- * The resolved inclusion needed to extract the input value.
21
+ * Inline resolved value passed to the unit.
22
+ *
23
+ * The backend is responsible for resolving the correct entity snapshot value
24
+ * and applying any inclusion transformations.
21
25
  */
22
- inclusion: entityInclusionSchema.optional(),
26
+ value: z.unknown(),
27
+
28
+ /**
29
+ * Optional provenance of the value.
30
+ */
31
+ source: unitInputSourceSchema.optional(),
23
32
  })
24
33
 
34
+ export type UnitInputValue = z.infer<typeof unitInputValueSchema>
35
+
25
36
  export const unitConfigSchema = z.object({
26
37
  /**
27
38
  * The ID of the instance.
28
39
  */
29
40
  instanceId: z.string(),
30
41
 
42
+ /**
43
+ * The state ID of the instance.
44
+ */
45
+ stateId: z.string(),
46
+
31
47
  /**
32
48
  * The record of argument values for the unit.
33
49
  */
@@ -36,7 +52,7 @@ export const unitConfigSchema = z.object({
36
52
  /**
37
53
  * The record of input references for the unit.
38
54
  */
39
- inputs: z.record(z.string(), unitInputReference.array()),
55
+ inputs: z.record(z.string(), unitInputValueSchema.array()),
40
56
 
41
57
  /**
42
58
  * The list of triggers that have been invoked for this unit.
@@ -44,14 +60,11 @@ export const unitConfigSchema = z.object({
44
60
  invokedTriggers: triggerInvocationSchema.array(),
45
61
 
46
62
  /**
47
- * The list of secret names that exists and provided to the unit.
63
+ * The record of secret values provided to the unit.
64
+ *
65
+ * It is stored in Pulumi stack config as a secret.
48
66
  */
49
- secretNames: z.string().array(),
50
-
51
- /**
52
- * The map of instance ID to state ID in order to resolve instance references.
53
- */
54
- stateIdMap: z.record(instanceIdSchema, z.string()),
67
+ secretValues: z.record(z.string(), z.unknown()),
55
68
 
56
69
  /**
57
70
  * The base path for imports.
@@ -87,7 +100,6 @@ export function parseArgumentValue(value: unknown): unknown {
87
100
 
88
101
  export enum HighstateConfigKey {
89
102
  Config = "highstate",
90
- Secrets = "highstate.secrets",
91
103
  }
92
104
 
93
105
  export const unitArtifactId = Symbol("unitArtifactId")
@@ -118,6 +130,23 @@ export const fileContentSchema = z.union([
118
130
  */
119
131
  value: z.string(),
120
132
  }),
133
+ z.object({
134
+ type: z.literal("embedded-secret"),
135
+
136
+ /**
137
+ * Whether the content is binary or not.
138
+ *
139
+ * If true, the `value` will be a base64 encoded string.
140
+ */
141
+ isBinary: z.boolean().optional(),
142
+
143
+ /**
144
+ * The content of the file wrapped as a Highstate secret.
145
+ *
146
+ * If `isBinary` is true, this will be a base64 encoded string.
147
+ */
148
+ value: secretSchema(z.string()),
149
+ }),
121
150
  z.object({
122
151
  type: z.literal("artifact"),
123
152
  ...unitArtifactSchema.shape,
package/src/shared.ts ADDED
@@ -0,0 +1,15 @@
1
+ import type { InstanceInput } from "./instance"
2
+ import { z } from "zod"
3
+
4
+ export const componentKindSchema = z.enum(["composite", "unit"])
5
+ export type ComponentKind = z.infer<typeof componentKindSchema>
6
+
7
+ export const runtimeSchema = Symbol("runtimeSchema")
8
+ export const kind = Symbol("kind")
9
+ export const boundaryInput = Symbol("boundaryInput")
10
+
11
+ export function inputKey(input: InstanceInput): string {
12
+ return input.path
13
+ ? `${input.instanceId}:${input.output}:${input.path}`
14
+ : `${input.instanceId}:${input.output}`
15
+ }