@highstate/pulumi 0.9.15 → 0.9.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/highstate.manifest.json +1 -1
- package/dist/index.js +211 -122
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
- package/src/index.ts +1 -1
- package/src/secret.ts +54 -23
- package/src/unit.ts +301 -123
package/src/unit.ts
CHANGED
@@ -5,20 +5,24 @@
|
|
5
5
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
6
6
|
|
7
7
|
import type { DeepInput, InputArray, InputMap } from "./utils"
|
8
|
+
import type { ComponentSecret } from "../../contract/src/unit"
|
8
9
|
import {
|
9
|
-
type ArgumentValue,
|
10
10
|
type ComponentInputSpec,
|
11
|
-
type
|
11
|
+
type EntityModel,
|
12
12
|
type Unit,
|
13
13
|
type ComponentInput,
|
14
14
|
type InstanceInput,
|
15
15
|
parseInstanceId,
|
16
|
-
type ArgumentValueSchema,
|
17
16
|
getInstanceId,
|
18
17
|
type ComponentArgumentSpec,
|
19
18
|
type ComponentArgumentSpecToStatic,
|
19
|
+
HighstateSignature,
|
20
|
+
camelCaseToHumanReadable,
|
21
|
+
z,
|
22
|
+
type UnitSecretModel,
|
23
|
+
unitSecretSchema,
|
24
|
+
unitArtifactSchema,
|
20
25
|
} from "@highstate/contract"
|
21
|
-
import { Type, type Static } from "@sinclair/typebox"
|
22
26
|
import { mapValues, pickBy, pipe } from "remeda"
|
23
27
|
import {
|
24
28
|
Config,
|
@@ -31,59 +35,71 @@ import {
|
|
31
35
|
type Unwrap,
|
32
36
|
} from "@pulumi/pulumi"
|
33
37
|
import { Ajv } from "ajv"
|
34
|
-
import {
|
38
|
+
import { updatedSecretValues, type UnitSecret } from "./secret"
|
35
39
|
|
36
40
|
const ajv = new Ajv({ strict: false })
|
37
41
|
|
38
|
-
export type
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
+
export type ObjectMeta = {
|
43
|
+
title?: Input<string | undefined>
|
44
|
+
description?: Input<string | undefined>
|
45
|
+
icon?: Input<string | undefined>
|
46
|
+
iconColor?: Input<string | undefined>
|
42
47
|
}
|
43
48
|
|
44
|
-
export type
|
49
|
+
export type InstanceFileMeta = {
|
45
50
|
name: Input<string>
|
46
|
-
|
47
|
-
|
48
|
-
|
51
|
+
contentType?: Input<string>
|
52
|
+
size?: Input<number>
|
53
|
+
isBinary?: Input<boolean>
|
54
|
+
mode?: Input<number>
|
55
|
+
}
|
56
|
+
|
57
|
+
export type UnitArtifact = {
|
58
|
+
hash: Input<string>
|
59
|
+
meta?: Input<ObjectMeta>
|
60
|
+
}
|
61
|
+
|
62
|
+
export type InstanceFile = {
|
63
|
+
meta: Input<InstanceFileMeta>
|
64
|
+
content:
|
65
|
+
| { type: "embedded"; value: Input<string> }
|
66
|
+
| {
|
67
|
+
type: "artifact"
|
68
|
+
[HighstateSignature.Artifact]: UnitArtifact
|
69
|
+
}
|
70
|
+
}
|
71
|
+
|
72
|
+
export type InstanceTerminalSpec = {
|
49
73
|
image: Input<string>
|
50
74
|
command: InputArray<string>
|
51
75
|
cwd?: Input<string | undefined>
|
52
76
|
env?: InputMap<string | undefined>
|
53
|
-
files?: InputMap<
|
77
|
+
files?: InputMap<InstanceFile | string | undefined>
|
78
|
+
}
|
79
|
+
|
80
|
+
export type InstanceTerminal = {
|
81
|
+
name: Input<string>
|
82
|
+
meta: Input<ObjectMeta>
|
83
|
+
spec: Input<InstanceTerminalSpec>
|
54
84
|
}
|
55
85
|
|
56
86
|
export type StatusFieldValue = string | number | boolean | string[]
|
57
87
|
|
58
88
|
export type StatusField<TArgName extends string = string> = {
|
59
89
|
name: Input<string>
|
60
|
-
|
61
|
-
displayName?: Input<string | undefined>
|
62
|
-
sensitive?: Input<boolean | undefined>
|
63
|
-
url?: Input<string | undefined>
|
90
|
+
meta?: Input<ObjectMeta>
|
64
91
|
complementaryTo?: Input<TArgName | undefined>
|
65
|
-
|
66
|
-
|
67
|
-
export type InstanceFileMeta = {
|
68
|
-
name: Input<string>
|
69
|
-
contentType: Input<string>
|
70
|
-
isBinary?: Input<boolean>
|
71
|
-
size: Input<number>
|
72
|
-
}
|
73
|
-
|
74
|
-
export type InstanceFile = {
|
75
|
-
meta: Input<InstanceFileMeta>
|
76
|
-
content: Input<string>
|
92
|
+
value?: Input<StatusFieldValue | undefined>
|
77
93
|
}
|
78
94
|
|
79
95
|
export type InstancePageBlock =
|
80
96
|
| { type: "markdown"; content: Input<string> }
|
81
97
|
| { type: "qr"; content: Input<string>; showContent?: boolean; language?: string }
|
82
|
-
| { type: "file"
|
98
|
+
| ({ type: "file" } & InstanceFile)
|
83
99
|
|
84
100
|
export type InstancePage = {
|
85
101
|
name: Input<string>
|
86
|
-
|
102
|
+
meta: Input<ObjectMeta>
|
87
103
|
content: InputArray<InstancePageBlock>
|
88
104
|
}
|
89
105
|
|
@@ -103,8 +119,14 @@ export type InstanceTrigger = {
|
|
103
119
|
spec: Input<InstanceTriggerSpec>
|
104
120
|
}
|
105
121
|
|
122
|
+
export type InstanceWorker = {
|
123
|
+
name: Input<string>
|
124
|
+
image: Input<string>
|
125
|
+
params?: InputMap<unknown>
|
126
|
+
}
|
127
|
+
|
106
128
|
export type ExtraOutputs<TArgName extends string = string> = {
|
107
|
-
$
|
129
|
+
$statusFields?:
|
108
130
|
| InputMap<Omit<StatusField<TArgName>, "name"> | StatusFieldValue | undefined>
|
109
131
|
| InputArray<StatusField<TArgName> | undefined>
|
110
132
|
|
@@ -113,11 +135,14 @@ export type ExtraOutputs<TArgName extends string = string> = {
|
|
113
135
|
| InputArray<InstanceTerminal | undefined>
|
114
136
|
|
115
137
|
$pages?: InputMap<Omit<InstancePage, "name"> | undefined> | InputArray<InstancePage | undefined>
|
116
|
-
$files?: InputArray<InstanceFile | undefined>
|
117
138
|
|
118
139
|
$triggers?:
|
119
140
|
| InputMap<Omit<InstanceTrigger, "name"> | undefined>
|
120
141
|
| InputArray<InstanceTrigger | undefined>
|
142
|
+
|
143
|
+
$workers?:
|
144
|
+
| InputMap<Omit<InstanceWorker, "name"> | undefined>
|
145
|
+
| InputArray<InstanceWorker | undefined>
|
121
146
|
}
|
122
147
|
|
123
148
|
export type InstanceTriggerInvocation = {
|
@@ -129,17 +154,21 @@ type OutputMapToDeepInputMap<T extends Record<string, unknown>, TArgName extends
|
|
129
154
|
? ExtraOutputs
|
130
155
|
: { [K in keyof T]: DeepInput<T[K]> } & ExtraOutputs<TArgName>
|
131
156
|
|
157
|
+
export type SecretValueMapToSecretMap<T extends Record<string, unknown>> = {
|
158
|
+
[K in keyof T]-?: UnitSecret<T[K]>
|
159
|
+
}
|
160
|
+
|
132
161
|
export interface UnitContext<
|
133
|
-
TArgs extends Record<string,
|
162
|
+
TArgs extends Record<string, unknown>,
|
134
163
|
TInputs extends Record<string, unknown>,
|
135
164
|
TOutputs extends Record<string, unknown>,
|
136
|
-
TSecrets extends Record<string,
|
165
|
+
TSecrets extends Record<string, unknown>,
|
137
166
|
> {
|
138
167
|
args: TArgs
|
139
168
|
instanceId: string
|
140
169
|
type: string
|
141
170
|
name: string
|
142
|
-
secrets:
|
171
|
+
secrets: SecretValueMapToSecretMap<TSecrets>
|
143
172
|
|
144
173
|
inputs: TInputs extends Record<string, never>
|
145
174
|
? never
|
@@ -158,10 +187,10 @@ export interface UnitContext<
|
|
158
187
|
}
|
159
188
|
|
160
189
|
type InputSpecToValue<T extends ComponentInputSpec> = T[2] extends true
|
161
|
-
?
|
190
|
+
? z.infer<T[0]["schema"]>[]
|
162
191
|
: T[1] extends true
|
163
|
-
?
|
164
|
-
:
|
192
|
+
? z.infer<T[0]["schema"]>
|
193
|
+
: z.infer<T[0]["schema"]> | undefined
|
165
194
|
|
166
195
|
type InputSpecMapToValueMap<T extends Record<string, ComponentInputSpec>> =
|
167
196
|
T extends Record<string, never>
|
@@ -210,18 +239,20 @@ function getOutput(unit: Unit, input: ComponentInput, refs: InstanceInput[]) {
|
|
210
239
|
const value = getStackRef(ref).requireOutput(ref.output)
|
211
240
|
|
212
241
|
return value.apply(value => {
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
242
|
+
if (Array.isArray(value)) {
|
243
|
+
for (const [index, item] of value.entries()) {
|
244
|
+
if (!ajv.validate(entity.schema, item)) {
|
245
|
+
throw new Error(`Invalid output for '${input.type}[${index}]': ${ajv.errorsText()}`)
|
246
|
+
}
|
247
|
+
}
|
248
|
+
} else {
|
249
|
+
if (!ajv.validate(entity.schema, value)) {
|
250
|
+
throw new Error(`Invalid output for '${input.type}': ${ajv.errorsText()}`)
|
251
|
+
}
|
221
252
|
}
|
222
253
|
|
223
254
|
if (Array.isArray(value)) {
|
224
|
-
return value
|
255
|
+
return value
|
225
256
|
}
|
226
257
|
|
227
258
|
return input.multiple ? [value] : value
|
@@ -237,17 +268,15 @@ function getOutput(unit: Unit, input: ComponentInput, refs: InstanceInput[]) {
|
|
237
268
|
return values
|
238
269
|
}
|
239
270
|
|
240
|
-
function isAnyOfSchema(schema:
|
271
|
+
function isAnyOfSchema(schema: z.core.JSONSchema.BaseSchema, itemType: string): boolean {
|
241
272
|
if (schema.anyOf) {
|
242
|
-
return Object.values(schema.anyOf).every(schema =>
|
243
|
-
isAnyOfSchema(schema as ArgumentValueSchema, itemType),
|
244
|
-
)
|
273
|
+
return Object.values(schema.anyOf).every(schema => isAnyOfSchema(schema, itemType))
|
245
274
|
}
|
246
275
|
|
247
276
|
return schema.type === itemType
|
248
277
|
}
|
249
278
|
|
250
|
-
function isStringSchema(schema:
|
279
|
+
function isStringSchema(schema: z.core.JSONSchema.BaseSchema): boolean {
|
251
280
|
if (schema.type === "string") {
|
252
281
|
return true
|
253
282
|
}
|
@@ -259,7 +288,7 @@ function isStringSchema(schema: ArgumentValueSchema): boolean {
|
|
259
288
|
return false
|
260
289
|
}
|
261
290
|
|
262
|
-
function isNumberSchema(schema:
|
291
|
+
function isNumberSchema(schema: z.core.JSONSchema.BaseSchema): boolean {
|
263
292
|
if (schema.type === "number") {
|
264
293
|
return true
|
265
294
|
}
|
@@ -271,7 +300,7 @@ function isNumberSchema(schema: ArgumentValueSchema): boolean {
|
|
271
300
|
return false
|
272
301
|
}
|
273
302
|
|
274
|
-
function isBooleanSchema(schema:
|
303
|
+
function isBooleanSchema(schema: z.core.JSONSchema.BaseSchema): boolean {
|
275
304
|
if (schema.type === "boolean") {
|
276
305
|
return true
|
277
306
|
}
|
@@ -302,19 +331,48 @@ export function forUnit<
|
|
302
331
|
const args = mapValues(unit.model.args, (arg, argName) => {
|
303
332
|
switch (true) {
|
304
333
|
case isStringSchema(arg.schema): {
|
305
|
-
|
334
|
+
if (arg.required) {
|
335
|
+
return config.require(argName)
|
336
|
+
}
|
337
|
+
|
338
|
+
// handle empty strings as undefined
|
339
|
+
return config.get(argName) || arg.schema.default
|
306
340
|
}
|
307
341
|
case isNumberSchema(arg.schema): {
|
308
|
-
|
309
|
-
|
310
|
-
|
342
|
+
if (arg.required) {
|
343
|
+
return config.requireNumber(argName)
|
344
|
+
}
|
345
|
+
|
346
|
+
// handle empty strings as undefined
|
347
|
+
const value = config.get(argName)
|
348
|
+
if (!value) {
|
349
|
+
return arg.schema.default
|
350
|
+
}
|
351
|
+
|
352
|
+
return config.getNumber(argName) ?? arg.schema.default
|
311
353
|
}
|
312
354
|
case isBooleanSchema(arg.schema): {
|
313
|
-
|
314
|
-
|
315
|
-
|
355
|
+
if (arg.required) {
|
356
|
+
return config.requireBoolean(argName)
|
357
|
+
}
|
358
|
+
|
359
|
+
// handle empty strings as undefined
|
360
|
+
const value = config.get(argName)
|
361
|
+
if (!value) {
|
362
|
+
return arg.schema.default
|
363
|
+
}
|
364
|
+
|
365
|
+
return config.getBoolean(argName) ?? arg.schema.default
|
316
366
|
}
|
317
367
|
default: {
|
368
|
+
if (!arg.required) {
|
369
|
+
const value = config.get(argName)
|
370
|
+
// handle empty strings as undefined
|
371
|
+
if (!value) {
|
372
|
+
return arg.schema.default
|
373
|
+
}
|
374
|
+
}
|
375
|
+
|
318
376
|
const value = arg.required ? config.requireObject(argName) : config.getObject(argName)
|
319
377
|
if (value === undefined) return arg.schema.default
|
320
378
|
|
@@ -327,36 +385,54 @@ export function forUnit<
|
|
327
385
|
}
|
328
386
|
}) as ComponentArgumentSpecToStatic<TArgs>
|
329
387
|
|
330
|
-
const
|
331
|
-
mapValues(unit.model.secrets, (secret, secretName) => {
|
332
|
-
switch (true) {
|
333
|
-
case isStringSchema(secret.schema): {
|
334
|
-
return secret.required ? config.requireSecret(secretName) : config.getSecret(secretName)
|
335
|
-
}
|
336
|
-
case isNumberSchema(secret.schema): {
|
337
|
-
return secret.required
|
338
|
-
? config.requireSecretNumber(secretName)
|
339
|
-
: config.getSecretNumber(secretName)
|
340
|
-
}
|
341
|
-
case isBooleanSchema(secret.schema): {
|
342
|
-
return secret.required
|
343
|
-
? config.requireSecretBoolean(secretName)
|
344
|
-
: config.getSecretBoolean(secretName)
|
345
|
-
}
|
346
|
-
default: {
|
347
|
-
const value = secret.required
|
348
|
-
? config.requireSecretObject(secretName)
|
349
|
-
: config.getSecretObject(secretName)
|
388
|
+
const secretIds = config.requireObject<Record<string, string>>("$secretIds")
|
350
389
|
|
351
|
-
|
352
|
-
|
353
|
-
|
390
|
+
const getSecretValue = (
|
391
|
+
secretName: string,
|
392
|
+
secret: ComponentSecret,
|
393
|
+
): Output<unknown> | undefined => {
|
394
|
+
switch (true) {
|
395
|
+
case isStringSchema(secret.schema): {
|
396
|
+
return secret.required ? config.requireSecret(secretName) : config.getSecret(secretName)
|
397
|
+
}
|
398
|
+
case isNumberSchema(secret.schema): {
|
399
|
+
return secret.required
|
400
|
+
? config.requireSecretNumber(secretName)
|
401
|
+
: config.getSecretNumber(secretName)
|
402
|
+
}
|
403
|
+
case isBooleanSchema(secret.schema): {
|
404
|
+
return secret.required
|
405
|
+
? config.requireSecretBoolean(secretName)
|
406
|
+
: config.getSecretBoolean(secretName)
|
407
|
+
}
|
408
|
+
default: {
|
409
|
+
const value = secret.required
|
410
|
+
? config.requireSecretObject(secretName)
|
411
|
+
: config.getSecretObject(secretName)
|
354
412
|
|
355
|
-
|
413
|
+
if (!ajv.validate(secret.schema, value)) {
|
414
|
+
throw new Error(`Invalid secret for '${secretName}': ${ajv.errorsText()}`)
|
356
415
|
}
|
416
|
+
|
417
|
+
return value
|
357
418
|
}
|
358
|
-
}
|
359
|
-
|
419
|
+
}
|
420
|
+
}
|
421
|
+
|
422
|
+
const secrets = mapValues(unit.model.secrets, (secret, secretName): UnitSecret<unknown> => {
|
423
|
+
const secretId = secretIds[secretName]
|
424
|
+
if (!secretId) {
|
425
|
+
throw new Error(`Secret '${secretName}' not found in the config.`)
|
426
|
+
}
|
427
|
+
|
428
|
+
return {
|
429
|
+
[HighstateSignature.Secret]: true,
|
430
|
+
id: secretId,
|
431
|
+
value: secret.required
|
432
|
+
? config.requireSecret(secretName)
|
433
|
+
: output(getSecretValue(secretName, secret)),
|
434
|
+
}
|
435
|
+
})
|
360
436
|
|
361
437
|
const inputs = mapValues(unit.model.inputs, (input, inputName) => {
|
362
438
|
const value = input.required
|
@@ -382,24 +458,20 @@ export function forUnit<
|
|
382
458
|
instanceId,
|
383
459
|
type,
|
384
460
|
name: instanceName,
|
385
|
-
secrets
|
461
|
+
secrets: secrets as SecretValueMapToSecretMap<ComponentArgumentSpecToStatic<TSecrets>>,
|
386
462
|
inputs: inputs as any,
|
387
463
|
invokedTriggers: config.getObject<InstanceTriggerInvocation[]>("$invokedTriggers") ?? [],
|
388
464
|
|
389
465
|
outputs: async (outputs: any = {}) => {
|
390
466
|
const result: any = mapValues(outputs, (outputValue, outputName) => {
|
391
|
-
if (outputName === "$
|
392
|
-
return output(outputValue).apply(
|
467
|
+
if (outputName === "$statusFields") {
|
468
|
+
return output(outputValue).apply(mapStatusFields)
|
393
469
|
}
|
394
470
|
|
395
471
|
if (outputName === "$pages") {
|
396
472
|
return output(outputValue).apply(mapPages)
|
397
473
|
}
|
398
474
|
|
399
|
-
if (outputName === "$files") {
|
400
|
-
return output(outputValue).apply(mapFiles)
|
401
|
-
}
|
402
|
-
|
403
475
|
if (outputName === "$terminals") {
|
404
476
|
return output(outputValue).apply(mapTerminals)
|
405
477
|
}
|
@@ -433,7 +505,7 @@ export function forUnit<
|
|
433
505
|
return undefined
|
434
506
|
}
|
435
507
|
|
436
|
-
const schema = outputModel.multiple ?
|
508
|
+
const schema = outputModel.multiple ? entity.schema.array() : entity.schema
|
437
509
|
|
438
510
|
if (!ajv.validate(schema, value)) {
|
439
511
|
throw new Error(`Invalid output for '${outputName}': ${ajv.errorsText()}`)
|
@@ -445,8 +517,38 @@ export function forUnit<
|
|
445
517
|
|
446
518
|
await Promise.all(Object.values(result).map(o => outputToPromise(o)))
|
447
519
|
|
448
|
-
|
449
|
-
|
520
|
+
result.$secrets = updatedSecretValues
|
521
|
+
|
522
|
+
// collect secrets from all outputs
|
523
|
+
const secretsMap: Record<string, UnitSecretModel[]> = {}
|
524
|
+
for (const [outputName, outputValue] of Object.entries(outputs)) {
|
525
|
+
if (!outputName.startsWith("$")) {
|
526
|
+
const resolvedValue = await outputToPromise(outputValue)
|
527
|
+
const secrets = extractObjectsFromValue(unitSecretSchema, resolvedValue)
|
528
|
+
if (secrets.length > 0) {
|
529
|
+
secretsMap[outputName] = secrets
|
530
|
+
}
|
531
|
+
}
|
532
|
+
}
|
533
|
+
|
534
|
+
// collect artifacts from all outputs
|
535
|
+
const artifactsMap: Record<string, UnitArtifact[]> = {}
|
536
|
+
for (const [outputName, outputValue] of Object.entries(outputs)) {
|
537
|
+
if (!outputName.startsWith("$")) {
|
538
|
+
const resolvedValue = await outputToPromise(outputValue)
|
539
|
+
const artifacts = extractObjectsFromValue(unitArtifactSchema, resolvedValue)
|
540
|
+
if (artifacts.length > 0) {
|
541
|
+
artifactsMap[outputName] = artifacts
|
542
|
+
}
|
543
|
+
}
|
544
|
+
}
|
545
|
+
|
546
|
+
if (Object.keys(artifactsMap).length > 0) {
|
547
|
+
result.$exportedArtifacts = artifactsMap
|
548
|
+
}
|
549
|
+
|
550
|
+
if (Object.keys(secretsMap).length > 0) {
|
551
|
+
result.$exportedSecretIds = mapValues(secretsMap, v => v.map(secret => secret.id))
|
450
552
|
}
|
451
553
|
|
452
554
|
return result
|
@@ -454,20 +556,30 @@ export function forUnit<
|
|
454
556
|
}
|
455
557
|
}
|
456
558
|
|
457
|
-
export type EntityValue<T extends
|
458
|
-
export type EntityInput<T extends
|
559
|
+
export type EntityValue<T extends EntityModel> = z.infer<T["schema"]>
|
560
|
+
export type EntityInput<T extends EntityModel> = Output<EntityValue<T>>
|
459
561
|
|
460
562
|
function outputToPromise(o: unknown): Promise<unknown> {
|
461
563
|
return new Promise(resolve => (output(o) as Output<unknown>).apply(resolve))
|
462
564
|
}
|
463
565
|
|
464
|
-
function
|
566
|
+
function mapStatusFields(status: Unwrap<ExtraOutputs["$statusFields"]>): StatusField[] {
|
465
567
|
if (!status) {
|
466
568
|
return []
|
467
569
|
}
|
468
570
|
|
469
571
|
if (Array.isArray(status)) {
|
470
|
-
return status
|
572
|
+
return status
|
573
|
+
.filter(field => !!field?.value)
|
574
|
+
.map(field => {
|
575
|
+
return {
|
576
|
+
name: field!.name,
|
577
|
+
meta: {
|
578
|
+
title: field!.meta?.title ?? camelCaseToHumanReadable(field!.name),
|
579
|
+
},
|
580
|
+
value: field!.value,
|
581
|
+
} as StatusField
|
582
|
+
})
|
471
583
|
}
|
472
584
|
|
473
585
|
return Object.entries(status)
|
@@ -482,10 +594,23 @@ function mapStatus(status: Unwrap<ExtraOutputs["$status"]>): StatusField[] {
|
|
482
594
|
typeof field === "boolean" ||
|
483
595
|
Array.isArray(field)
|
484
596
|
) {
|
485
|
-
return {
|
597
|
+
return {
|
598
|
+
name,
|
599
|
+
meta: {
|
600
|
+
title: camelCaseToHumanReadable(name),
|
601
|
+
},
|
602
|
+
value: field,
|
603
|
+
}
|
486
604
|
}
|
487
605
|
|
488
|
-
return {
|
606
|
+
return {
|
607
|
+
...(field as StatusField),
|
608
|
+
meta: {
|
609
|
+
...field.meta,
|
610
|
+
title: field.meta?.title ?? camelCaseToHumanReadable(name),
|
611
|
+
},
|
612
|
+
name,
|
613
|
+
}
|
489
614
|
})
|
490
615
|
.filter(field => !!field?.value) as StatusField[]
|
491
616
|
}
|
@@ -516,7 +641,10 @@ export function fileFromString(
|
|
516
641
|
contentType,
|
517
642
|
size: Buffer.byteLength(content, "utf8"),
|
518
643
|
},
|
519
|
-
content:
|
644
|
+
content: {
|
645
|
+
type: "embedded",
|
646
|
+
value: isSecret ? secret(content) : content,
|
647
|
+
},
|
520
648
|
}
|
521
649
|
}
|
522
650
|
|
@@ -533,14 +661,13 @@ export function fileFromBuffer(
|
|
533
661
|
size: content.byteLength,
|
534
662
|
isBinary: true,
|
535
663
|
},
|
536
|
-
content:
|
664
|
+
content: {
|
665
|
+
type: "embedded",
|
666
|
+
value: isSecret ? secret(content.toString("base64")) : content.toString("base64"),
|
667
|
+
},
|
537
668
|
}
|
538
669
|
}
|
539
670
|
|
540
|
-
function mapFiles(files: Unwrap<ExtraOutputs["$files"]>): InstanceFile[] {
|
541
|
-
return files?.filter(file => !!file) ?? []
|
542
|
-
}
|
543
|
-
|
544
671
|
function mapTerminals(terminals: Unwrap<ExtraOutputs["$terminals"]>): InstanceTerminal[] {
|
545
672
|
if (!terminals) {
|
546
673
|
return []
|
@@ -559,24 +686,38 @@ function mapTerminals(terminals: Unwrap<ExtraOutputs["$terminals"]>): InstanceTe
|
|
559
686
|
return terminals
|
560
687
|
.filter(terminal => !!terminal)
|
561
688
|
.map(terminal => {
|
562
|
-
if (!terminal.files) {
|
689
|
+
if (!terminal.spec.files) {
|
563
690
|
return terminal
|
564
691
|
}
|
565
692
|
|
566
693
|
return {
|
567
694
|
...terminal,
|
568
695
|
|
569
|
-
|
570
|
-
terminal.
|
571
|
-
|
572
|
-
|
573
|
-
|
574
|
-
|
575
|
-
|
576
|
-
|
577
|
-
|
578
|
-
|
579
|
-
|
696
|
+
spec: {
|
697
|
+
...terminal.spec,
|
698
|
+
|
699
|
+
files: pipe(
|
700
|
+
terminal.spec.files,
|
701
|
+
mapValues(file => {
|
702
|
+
if (typeof file === "string") {
|
703
|
+
return {
|
704
|
+
meta: {
|
705
|
+
name: "content",
|
706
|
+
contentType: "text/plain",
|
707
|
+
size: Buffer.byteLength(file, "utf8"),
|
708
|
+
},
|
709
|
+
content: {
|
710
|
+
type: "embedded" as const,
|
711
|
+
value: file,
|
712
|
+
},
|
713
|
+
}
|
714
|
+
}
|
715
|
+
|
716
|
+
return file
|
717
|
+
}),
|
718
|
+
pickBy(value => !!value),
|
719
|
+
),
|
720
|
+
},
|
580
721
|
}
|
581
722
|
})
|
582
723
|
}
|
@@ -594,3 +735,40 @@ function mapTriggers(triggers: Unwrap<ExtraOutputs["$triggers"]>): InstanceTrigg
|
|
594
735
|
.filter(([, trigger]) => !!trigger)
|
595
736
|
.map(([name, trigger]) => ({ ...(trigger as InstanceTrigger), name }))
|
596
737
|
}
|
738
|
+
|
739
|
+
/**
|
740
|
+
* Extracts all objects with the specified schema from a value.
|
741
|
+
*/
|
742
|
+
function extractObjectsFromValue<TSchema extends z.ZodType>(
|
743
|
+
schema: TSchema,
|
744
|
+
data: unknown,
|
745
|
+
): z.infer<TSchema>[] {
|
746
|
+
const result: z.infer<TSchema>[] = []
|
747
|
+
|
748
|
+
function traverse(obj: unknown): void {
|
749
|
+
if (obj === null || obj === undefined || typeof obj !== "object") {
|
750
|
+
return
|
751
|
+
}
|
752
|
+
|
753
|
+
if (Array.isArray(obj)) {
|
754
|
+
for (const item of obj) {
|
755
|
+
traverse(item)
|
756
|
+
}
|
757
|
+
return
|
758
|
+
}
|
759
|
+
|
760
|
+
const parseResult = schema.safeParse(obj)
|
761
|
+
if (parseResult.success) {
|
762
|
+
result.push(parseResult.data)
|
763
|
+
return
|
764
|
+
}
|
765
|
+
|
766
|
+
// recursively traverse all properties
|
767
|
+
for (const value of Object.values(obj)) {
|
768
|
+
traverse(value)
|
769
|
+
}
|
770
|
+
}
|
771
|
+
|
772
|
+
traverse(data)
|
773
|
+
return result
|
774
|
+
}
|