@shipfox/workflow-document 3.0.1 → 3.2.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/README.md +6 -1
- package/dist/document/index.d.ts +1 -1
- package/dist/document/index.d.ts.map +1 -1
- package/dist/document/index.js +1 -1
- package/dist/document/index.js.map +1 -1
- package/dist/document/workflow-document-parser.d.ts +2 -2
- package/dist/document/workflow-document-parser.d.ts.map +1 -1
- package/dist/document/workflow-document-parser.js +18 -5
- package/dist/document/workflow-document-parser.js.map +1 -1
- package/dist/document/workflow-document.d.ts +279 -21
- package/dist/document/workflow-document.d.ts.map +1 -1
- package/dist/document/workflow-document.js +293 -22
- package/dist/document/workflow-document.js.map +1 -1
- package/dist/document/workflow-json-schema.d.ts.map +1 -1
- package/dist/document/workflow-json-schema.js +16 -1
- package/dist/document/workflow-json-schema.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/tsconfig.test.tsbuildinfo +1 -1
- package/package.json +1 -1
|
@@ -52,6 +52,8 @@ export const WORKFLOW_DOCUMENT_JOB_OUTPUTS_MAX_ENTRIES = WORKFLOW_DOCUMENT_ENV_M
|
|
|
52
52
|
export const WORKFLOW_DOCUMENT_STEP_OUTPUTS_MAX_ENTRIES = WORKFLOW_DOCUMENT_ENV_MAX_ENTRIES;
|
|
53
53
|
export const WORKFLOW_DOCUMENT_STEP_OUTPUT_SCHEMA_MAX_SERIALIZED_BYTES = WORKFLOW_DOCUMENT_ENV_MAX_SERIALIZED_BYTES;
|
|
54
54
|
export const WORKFLOW_DOCUMENT_STEP_OUTPUT_SCHEMA_MAX_DEPTH = 64;
|
|
55
|
+
export const WORKFLOW_DOCUMENT_TOOL_WITH_MAX_SERIALIZED_BYTES = 32 * 1024;
|
|
56
|
+
export const WORKFLOW_DOCUMENT_TOOL_WITH_MAX_DEPTH = 16;
|
|
55
57
|
const utf8Encoder = new TextEncoder();
|
|
56
58
|
export const workflowDocumentEnvSchema = z.record(envNameSchema, z.union([
|
|
57
59
|
envStringValueSchema,
|
|
@@ -76,10 +78,186 @@ export const workflowDocumentEnvSchema = z.record(envNameSchema, z.union([
|
|
|
76
78
|
description: `Environment variables as string, number, or boolean values. Each map allows up to ${WORKFLOW_DOCUMENT_ENV_MAX_ENTRIES} entries and ${WORKFLOW_DOCUMENT_ENV_MAX_SERIALIZED_BYTES} serialized bytes.`
|
|
77
79
|
});
|
|
78
80
|
const workflowDocumentStepOutputKeyPattern = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
|
|
81
|
+
export const workflowDocumentToolStepWithSchema = z// Validate nested values in an iterative refinement. A recursive Zod schema
|
|
82
|
+
// would traverse hostile depth before the tool-input limits can reject it.
|
|
83
|
+
.record(z.string().min(1), z.unknown()).superRefine((withValue, ctx)=>{
|
|
84
|
+
// The server injects `method` for `family.method` tools, so the author can
|
|
85
|
+
// never set it.
|
|
86
|
+
if ('method' in withValue) {
|
|
87
|
+
ctx.addIssue({
|
|
88
|
+
code: 'custom',
|
|
89
|
+
path: [
|
|
90
|
+
'method'
|
|
91
|
+
],
|
|
92
|
+
message: '`method` is not a valid tool input; the server injects it for `family.method` tools.'
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
validateWorkflowDocumentToolWith(withValue, ctx);
|
|
96
|
+
}).transform((withValue)=>withValue).meta({
|
|
97
|
+
description: 'Tool inputs as a JSON tree. The map allows up to ' + WORKFLOW_DOCUMENT_TOOL_WITH_MAX_SERIALIZED_BYTES + ' serialized bytes and ' + WORKFLOW_DOCUMENT_TOOL_WITH_MAX_DEPTH + ' nesting levels. Tool steps are not available yet.'
|
|
98
|
+
});
|
|
99
|
+
function validateWorkflowDocumentToolWith(withValue, ctx) {
|
|
100
|
+
const activeObjects = new Set();
|
|
101
|
+
const tasks = [
|
|
102
|
+
{
|
|
103
|
+
kind: 'value',
|
|
104
|
+
value: withValue,
|
|
105
|
+
depth: 1,
|
|
106
|
+
path: []
|
|
107
|
+
}
|
|
108
|
+
];
|
|
109
|
+
let serializedBytes = 0;
|
|
110
|
+
const addSerializedBytes = (byteLength)=>{
|
|
111
|
+
serializedBytes += byteLength;
|
|
112
|
+
if (serializedBytes <= WORKFLOW_DOCUMENT_TOOL_WITH_MAX_SERIALIZED_BYTES) return true;
|
|
113
|
+
ctx.addIssue({
|
|
114
|
+
code: 'custom',
|
|
115
|
+
message: `Tool \`with\` cannot serialize to more than ${WORKFLOW_DOCUMENT_TOOL_WITH_MAX_SERIALIZED_BYTES} bytes.`
|
|
116
|
+
});
|
|
117
|
+
return false;
|
|
118
|
+
};
|
|
119
|
+
while(tasks.length > 0){
|
|
120
|
+
const task = tasks.pop();
|
|
121
|
+
if (task === undefined) continue;
|
|
122
|
+
if (task.kind === 'bytes') {
|
|
123
|
+
if (!addSerializedBytes(task.byteLength)) return;
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
if (task.kind === 'end') {
|
|
127
|
+
activeObjects.delete(task.value);
|
|
128
|
+
if (!addSerializedBytes(task.byteLength)) return;
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
const { value, depth, path } = task;
|
|
132
|
+
if (value === null) {
|
|
133
|
+
if (!addSerializedBytes(4)) return;
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
if (typeof value === 'string' || typeof value === 'boolean') {
|
|
137
|
+
if (!addSerializedBytes(jsonPrimitiveByteLength(value))) return;
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
if (typeof value === 'number') {
|
|
141
|
+
if (!Number.isFinite(value)) {
|
|
142
|
+
ctx.addIssue({
|
|
143
|
+
code: 'custom',
|
|
144
|
+
path,
|
|
145
|
+
message: 'Tool `with` values must be JSON-compatible.'
|
|
146
|
+
});
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
if (!addSerializedBytes(jsonPrimitiveByteLength(value))) return;
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
if (typeof value !== 'object' || value === null) {
|
|
153
|
+
ctx.addIssue({
|
|
154
|
+
code: 'custom',
|
|
155
|
+
path,
|
|
156
|
+
message: 'Tool `with` values must be JSON-compatible.'
|
|
157
|
+
});
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
if (depth > WORKFLOW_DOCUMENT_TOOL_WITH_MAX_DEPTH) {
|
|
161
|
+
ctx.addIssue({
|
|
162
|
+
code: 'custom',
|
|
163
|
+
message: `Tool \`with\` cannot be nested deeper than ${WORKFLOW_DOCUMENT_TOOL_WITH_MAX_DEPTH} levels.`
|
|
164
|
+
});
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
if (activeObjects.has(value)) {
|
|
168
|
+
ctx.addIssue({
|
|
169
|
+
code: 'custom',
|
|
170
|
+
path,
|
|
171
|
+
message: 'Tool `with` values must be a JSON tree.'
|
|
172
|
+
});
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
activeObjects.add(value);
|
|
176
|
+
if (Array.isArray(value)) {
|
|
177
|
+
if (!addSerializedBytes(1)) return;
|
|
178
|
+
tasks.push({
|
|
179
|
+
kind: 'end',
|
|
180
|
+
value,
|
|
181
|
+
byteLength: 1
|
|
182
|
+
});
|
|
183
|
+
for(let index = value.length - 1; index >= 0; index -= 1){
|
|
184
|
+
tasks.push({
|
|
185
|
+
kind: 'value',
|
|
186
|
+
value: value[index],
|
|
187
|
+
depth: depth + 1,
|
|
188
|
+
path: [
|
|
189
|
+
...path,
|
|
190
|
+
index
|
|
191
|
+
]
|
|
192
|
+
});
|
|
193
|
+
if (index > 0) tasks.push({
|
|
194
|
+
kind: 'bytes',
|
|
195
|
+
byteLength: 1
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
200
|
+
if (!isJsonRecord(value)) {
|
|
201
|
+
ctx.addIssue({
|
|
202
|
+
code: 'custom',
|
|
203
|
+
path,
|
|
204
|
+
message: 'Tool `with` values must be a JSON tree.'
|
|
205
|
+
});
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
if (!addSerializedBytes(1)) return;
|
|
209
|
+
tasks.push({
|
|
210
|
+
kind: 'end',
|
|
211
|
+
value,
|
|
212
|
+
byteLength: 1
|
|
213
|
+
});
|
|
214
|
+
const entries = Object.entries(value);
|
|
215
|
+
for(let index = entries.length - 1; index >= 0; index -= 1){
|
|
216
|
+
const entry = entries[index];
|
|
217
|
+
if (entry === undefined) continue;
|
|
218
|
+
const [key, child] = entry;
|
|
219
|
+
tasks.push({
|
|
220
|
+
kind: 'value',
|
|
221
|
+
value: child,
|
|
222
|
+
depth: depth + 1,
|
|
223
|
+
path: [
|
|
224
|
+
...path,
|
|
225
|
+
key
|
|
226
|
+
]
|
|
227
|
+
});
|
|
228
|
+
tasks.push({
|
|
229
|
+
kind: 'bytes',
|
|
230
|
+
byteLength: jsonPrimitiveByteLength(key) + 1
|
|
231
|
+
});
|
|
232
|
+
if (index > 0) tasks.push({
|
|
233
|
+
kind: 'bytes',
|
|
234
|
+
byteLength: 1
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
function jsonPrimitiveByteLength(value) {
|
|
240
|
+
return utf8Encoder.encode(JSON.stringify(value)).byteLength;
|
|
241
|
+
}
|
|
242
|
+
function jsonSerializedByteLength(value) {
|
|
243
|
+
try {
|
|
244
|
+
const serialized = JSON.stringify(value);
|
|
245
|
+
return serialized === undefined ? undefined : utf8Encoder.encode(serialized).byteLength;
|
|
246
|
+
} catch {
|
|
247
|
+
return undefined;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
function isJsonRecord(value) {
|
|
251
|
+
const prototype = Object.getPrototypeOf(value);
|
|
252
|
+
return prototype === Object.prototype || prototype === null;
|
|
253
|
+
}
|
|
79
254
|
const workflowDocumentStepOutputTypeSchema = z.enum(workflowDocumentStepOutputTypes).meta({
|
|
80
255
|
description: 'Declared output type. Use `json` when the output has a JSON Schema.'
|
|
81
256
|
});
|
|
82
|
-
const
|
|
257
|
+
const workflowDocumentToolStepOutputMappingValueSchema = z.string().min(1).refine((value)=>value.includes('$' + '{{'), {
|
|
258
|
+
message: 'Tool-step output mappings must use a $' + '{{ }} expression.'
|
|
259
|
+
});
|
|
260
|
+
export const workflowDocumentStepOutputDeclarationSchema = z.union([
|
|
83
261
|
workflowDocumentStepOutputTypeSchema.transform((type)=>({
|
|
84
262
|
type
|
|
85
263
|
})),
|
|
@@ -112,7 +290,17 @@ const workflowDocumentStepOutputDeclarationSchema = z.union([
|
|
|
112
290
|
});
|
|
113
291
|
return;
|
|
114
292
|
}
|
|
115
|
-
const serializedBytes =
|
|
293
|
+
const serializedBytes = jsonSerializedByteLength(schema);
|
|
294
|
+
if (serializedBytes === undefined) {
|
|
295
|
+
ctx.addIssue({
|
|
296
|
+
code: 'custom',
|
|
297
|
+
path: [
|
|
298
|
+
'schema'
|
|
299
|
+
],
|
|
300
|
+
message: 'Schema must be a serializable JSON Schema document.'
|
|
301
|
+
});
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
116
304
|
if (serializedBytes > WORKFLOW_DOCUMENT_STEP_OUTPUT_SCHEMA_MAX_SERIALIZED_BYTES) {
|
|
117
305
|
ctx.addIssue({
|
|
118
306
|
code: 'custom',
|
|
@@ -133,7 +321,12 @@ const workflowDocumentStepOutputDeclarationSchema = z.union([
|
|
|
133
321
|
});
|
|
134
322
|
}
|
|
135
323
|
});
|
|
136
|
-
|
|
324
|
+
function stepOutputsAreMappingForm(outputs) {
|
|
325
|
+
const interpolationOpen = '$' + '{{';
|
|
326
|
+
const values = Object.values(outputs);
|
|
327
|
+
return values.some((value)=>typeof value === 'string' && value.includes(interpolationOpen));
|
|
328
|
+
}
|
|
329
|
+
function stepOutputsRecordChecks(outputs, ctx) {
|
|
137
330
|
const entries = Object.keys(outputs).length;
|
|
138
331
|
if (entries > WORKFLOW_DOCUMENT_STEP_OUTPUTS_MAX_ENTRIES) {
|
|
139
332
|
ctx.addIssue({
|
|
@@ -151,21 +344,38 @@ export const workflowDocumentStepOutputsSchema = z.record(z.string(), workflowDo
|
|
|
151
344
|
message: 'Output keys must be CEL identifiers.'
|
|
152
345
|
});
|
|
153
346
|
}
|
|
154
|
-
}
|
|
347
|
+
}
|
|
348
|
+
export const workflowDocumentStepOutputsSchema = z.record(z.string(), workflowDocumentStepOutputDeclarationSchema).superRefine((outputs, ctx)=>stepOutputsRecordChecks(outputs, ctx)).meta({
|
|
155
349
|
description: `Named step outputs. Keys must be CEL identifiers and each step allows up to ${WORKFLOW_DOCUMENT_STEP_OUTPUTS_MAX_ENTRIES} declarations.`
|
|
156
350
|
});
|
|
351
|
+
// The tool-step `outputs` form maps output keys to a single `${{ }}` expression
|
|
352
|
+
// over `result`. The expression layer validates the interpolation; the mapping
|
|
353
|
+
// form is rejected with the reserved tool step fields until tool steps exist.
|
|
354
|
+
export const workflowDocumentToolStepOutputsSchema = z.record(z.string(), workflowDocumentToolStepOutputMappingValueSchema).superRefine((outputs, ctx)=>stepOutputsRecordChecks(outputs, ctx)).meta({
|
|
355
|
+
description: 'Tool-step output mappings over `result`. Each value is exactly one $' + '{{ }} expression. Tool steps are not available yet.'
|
|
356
|
+
});
|
|
357
|
+
// `outputs` carries the declaration form on run, agent, and checkout steps and
|
|
358
|
+
// the expression mapping form on tool steps. One value union accepts both so a
|
|
359
|
+
// reserved tool step parses and is rejected by the step `superRefine`; zod
|
|
360
|
+
// reports the declaration branch's own issues for malformed declarations, and
|
|
361
|
+
// the step `superRefine` rejects the mapping form on every other step kind.
|
|
362
|
+
const workflowDocumentStepOutputValueSchema = z.union([
|
|
363
|
+
workflowDocumentStepOutputDeclarationSchema,
|
|
364
|
+
workflowDocumentToolStepOutputMappingValueSchema
|
|
365
|
+
]);
|
|
366
|
+
const workflowDocumentStepOutputsFieldSchema = z.record(z.string(), workflowDocumentStepOutputValueSchema).superRefine((outputs, ctx)=>stepOutputsRecordChecks(outputs, ctx));
|
|
157
367
|
const workflowDocumentTriggerBaseSchema = {
|
|
158
368
|
source: z.string().min(1).meta({
|
|
159
|
-
description: 'Integration connection slug or built-in trigger source. See [
|
|
369
|
+
description: 'Integration connection slug or built-in trigger source. See [Integrations](/integrations) for provider sources.'
|
|
160
370
|
}),
|
|
161
371
|
with: z.record(z.string(), z.unknown()).optional().meta({
|
|
162
|
-
description: 'Provider-specific values used to match or configure the trigger. See [
|
|
372
|
+
description: 'Provider-specific values used to match or configure the trigger. See the provider event catalog in [Integrations](/integrations).'
|
|
163
373
|
}),
|
|
164
374
|
filter: z.string().min(1).optional().meta({
|
|
165
375
|
description: 'CEL condition that filters matching events. It is not supported for `manual` or `cron` triggers. See [Expressions](/reference/expressions) and [Contexts](/reference/contexts#context-availability).'
|
|
166
376
|
}),
|
|
167
377
|
config: z.record(z.string(), z.unknown()).optional().meta({
|
|
168
|
-
description: 'Source-specific configuration. It is supported only for top-level triggers with a known built-in source. See [
|
|
378
|
+
description: 'Source-specific configuration. It is supported only for top-level triggers with a known built-in source. See [Schedule workflows](/how-to/author-workflows/schedule-workflows).'
|
|
169
379
|
})
|
|
170
380
|
};
|
|
171
381
|
export const triggerSourceConfigSchemas = {
|
|
@@ -181,8 +391,8 @@ export const triggerSourceConfigSchemas = {
|
|
|
181
391
|
const triggerSourceConfigSchemaRegistry = triggerSourceConfigSchemas;
|
|
182
392
|
export const workflowDocumentTriggerSchema = z.strictObject({
|
|
183
393
|
...workflowDocumentTriggerBaseSchema,
|
|
184
|
-
event: z.string().min(1).meta({
|
|
185
|
-
description: '
|
|
394
|
+
event: z.string().min(1).optional().meta({
|
|
395
|
+
description: 'Event name that starts the workflow. Omit it to accept every event the source delivers. Sources that deliver one event, such as `manual`, `cron`, and custom webhooks, do not need it.'
|
|
186
396
|
})
|
|
187
397
|
}).superRefine((trigger, ctx)=>{
|
|
188
398
|
if (trigger.config === undefined) return;
|
|
@@ -368,8 +578,11 @@ export const workflowDocumentAgentStepFields = [
|
|
|
368
578
|
// payload keys are present and emits one targeted issue per failure mode (a
|
|
369
579
|
// plain union would surface every branch's errors at once). The `agent`
|
|
370
580
|
// keyword is declared only so the reserved-keyword case produces a clear
|
|
371
|
-
// message instead of a generic "unrecognized key".
|
|
372
|
-
|
|
581
|
+
// message instead of a generic "unrecognized key". The `tool`, `connection`,
|
|
582
|
+
// `with`, and tool-step `outputs` mapping form are declared the same way: they
|
|
583
|
+
// parse so their shape is checked, then any step carrying a reserved tool field
|
|
584
|
+
// is rejected until the tool step kind exists.
|
|
585
|
+
const workflowDocumentStepBaseSchema = z.strictObject({
|
|
373
586
|
key: z.string().min(1).optional().meta({
|
|
374
587
|
description: 'Stable step key for dependencies and outputs.'
|
|
375
588
|
}),
|
|
@@ -410,16 +623,24 @@ export const workflowDocumentStepSchema = z.strictObject({
|
|
|
410
623
|
agent: z.unknown().optional().meta({
|
|
411
624
|
description: 'Reserved keyword. It is rejected; use `prompt` to define an agent step.'
|
|
412
625
|
}),
|
|
626
|
+
tool: z.string().min(1).optional().meta({
|
|
627
|
+
description: 'Literal integration tool id for a tool step. It is rejected; tool steps are not available yet.'
|
|
628
|
+
}),
|
|
629
|
+
connection: z.string().min(1).optional().meta({
|
|
630
|
+
description: 'Literal integration connection slug for a tool step. It is rejected; tool steps are not available yet.'
|
|
631
|
+
}),
|
|
632
|
+
with: workflowDocumentToolStepWithSchema.optional(),
|
|
413
633
|
gate: workflowDocumentStepGateSchema.optional().meta({
|
|
414
634
|
description: 'Success gate and optional restart behavior after the step runs.'
|
|
415
635
|
}),
|
|
416
636
|
env: workflowDocumentEnvSchema.optional().meta({
|
|
417
637
|
description: 'Environment variables for a run step. They are not valid on an agent step.'
|
|
418
638
|
}),
|
|
419
|
-
outputs:
|
|
420
|
-
description: 'Named output declarations produced by this step.'
|
|
639
|
+
outputs: workflowDocumentStepOutputsFieldSchema.optional().meta({
|
|
640
|
+
description: 'Named output declarations produced by this step, or on a tool step a mapping of output keys to exactly one $' + '{{ }} expression over `result`. Tool steps are not available yet.'
|
|
421
641
|
})
|
|
422
|
-
})
|
|
642
|
+
});
|
|
643
|
+
export const workflowDocumentStepSchema = workflowDocumentStepBaseSchema.superRefine((step, ctx)=>{
|
|
423
644
|
if (step.agent !== undefined) {
|
|
424
645
|
ctx.addIssue({
|
|
425
646
|
code: 'custom',
|
|
@@ -430,6 +651,26 @@ export const workflowDocumentStepSchema = z.strictObject({
|
|
|
430
651
|
});
|
|
431
652
|
return;
|
|
432
653
|
}
|
|
654
|
+
const reservedToolField = step.tool !== undefined ? 'tool' : step.connection !== undefined ? 'connection' : undefined;
|
|
655
|
+
if (reservedToolField !== undefined || step.with !== undefined) {
|
|
656
|
+
ctx.addIssue({
|
|
657
|
+
code: 'custom',
|
|
658
|
+
path: [
|
|
659
|
+
reservedToolField ?? 'with'
|
|
660
|
+
],
|
|
661
|
+
message: 'Tool steps are not available yet.'
|
|
662
|
+
});
|
|
663
|
+
return;
|
|
664
|
+
}
|
|
665
|
+
if (step.outputs !== undefined && stepOutputsAreMappingForm(step.outputs)) {
|
|
666
|
+
ctx.addIssue({
|
|
667
|
+
code: 'custom',
|
|
668
|
+
path: [
|
|
669
|
+
'outputs'
|
|
670
|
+
],
|
|
671
|
+
message: 'The `outputs` mapping form is reserved for tool steps.'
|
|
672
|
+
});
|
|
673
|
+
}
|
|
433
674
|
if (step.checkout !== undefined) {
|
|
434
675
|
if (step.run !== undefined) {
|
|
435
676
|
ctx.addIssue({
|
|
@@ -502,7 +743,10 @@ export const workflowDocumentStepSchema = z.strictObject({
|
|
|
502
743
|
message: 'An agent step requires "prompt".'
|
|
503
744
|
});
|
|
504
745
|
}
|
|
505
|
-
})
|
|
746
|
+
}).transform(({ outputs, ...step })=>outputs === undefined ? step : {
|
|
747
|
+
...step,
|
|
748
|
+
outputs: outputs
|
|
749
|
+
});
|
|
506
750
|
const workflowDocumentJobOutputsSchema = nonEmptyRecordSchema(z.string().min(1)).superRefine((outputs, ctx)=>{
|
|
507
751
|
const entries = Object.keys(outputs).length;
|
|
508
752
|
if (entries > WORKFLOW_DOCUMENT_JOB_OUTPUTS_MAX_ENTRIES) {
|
|
@@ -565,14 +809,41 @@ export const workflowDocumentSchema = z.strictObject({
|
|
|
565
809
|
})
|
|
566
810
|
});
|
|
567
811
|
function maxJsonDepth(value) {
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
812
|
+
let maximumDepth = 0;
|
|
813
|
+
const activeObjects = new Set();
|
|
814
|
+
const pending = [
|
|
815
|
+
{
|
|
816
|
+
kind: 'enter',
|
|
817
|
+
value,
|
|
818
|
+
depth: 0
|
|
819
|
+
}
|
|
820
|
+
];
|
|
821
|
+
while(pending.length > 0){
|
|
822
|
+
const current = pending.pop();
|
|
823
|
+
if (current === undefined) continue;
|
|
824
|
+
if (current.kind === 'leave') {
|
|
825
|
+
activeObjects.delete(current.value);
|
|
826
|
+
continue;
|
|
827
|
+
}
|
|
828
|
+
if (current.value === null || typeof current.value !== 'object') continue;
|
|
829
|
+
if (activeObjects.has(current.value)) continue;
|
|
830
|
+
activeObjects.add(current.value);
|
|
831
|
+
const depth = current.depth + 1;
|
|
832
|
+
maximumDepth = Math.max(maximumDepth, depth);
|
|
833
|
+
pending.push({
|
|
834
|
+
kind: 'leave',
|
|
835
|
+
value: current.value
|
|
836
|
+
});
|
|
837
|
+
const children = Object.values(current.value);
|
|
838
|
+
for(let index = children.length - 1; index >= 0; index -= 1){
|
|
839
|
+
pending.push({
|
|
840
|
+
kind: 'enter',
|
|
841
|
+
value: children[index],
|
|
842
|
+
depth
|
|
843
|
+
});
|
|
844
|
+
}
|
|
572
845
|
}
|
|
573
|
-
|
|
574
|
-
if (entries.length === 0) return 1;
|
|
575
|
-
return 1 + Math.max(...entries.map(maxJsonDepth));
|
|
846
|
+
return maximumDepth;
|
|
576
847
|
}
|
|
577
848
|
function isJsonSchemaDocument(value) {
|
|
578
849
|
return typeof value === 'boolean' || typeof value === 'object' && value !== null && !Array.isArray(value);
|