UNPKG

@redocly/openapi-core

Version:

See https://github.com/Redocly/redocly-cli

139 lines 5.37 kB
import { isPlainObject } from '../../utils/is-plain-object.js'; export const OutputsDefined = () => { const definedWorkflowOutputs = new Map(); const definedStepOutputs = new Map(); // Store validation tasks to run after all outputs are collected const deferredValidationTasks = []; // Match $workflows.{id}.outputs.{key} pattern function matchWorkflowOutput({ value, report, location, path, definedWorkflowOutputs, }) { const workflowOutputPattern = /\$workflows\.([^.\s]+)\.outputs\.([^.\s}\]#]+)/g; let match; while ((match = workflowOutputPattern.exec(value)) !== null) { const [fullMatch, workflowId, outputKey] = match; const definedKeys = definedWorkflowOutputs.get(workflowId); if (!definedKeys) { report({ message: `Workflow "${workflowId}" referenced in runtime expression "${fullMatch}" is not defined or has no outputs.`, location: location.child(path), }); } else if (!definedKeys.includes(outputKey)) { report({ message: `Output key "${outputKey}" is not defined in workflow "${workflowId}". Available outputs: [ ${definedKeys.join(', ')} ].`, location: location.child(path), }); } } } // Match $steps.{id}.outputs.{key} pattern function matchStepOutput({ value, report, location, path, definedStepOutputs, }) { const stepOutputPattern = /\$steps\.([^.\s]+)\.outputs\.([^.\s}\]#]+)/g; let match; while ((match = stepOutputPattern.exec(value)) !== null) { const [fullMatch, stepId, outputKey] = match; const definedKeys = definedStepOutputs.get(stepId); if (!definedKeys) { report({ message: `Step "${stepId}" referenced in runtime expression "${fullMatch}" is not defined or has no outputs.`, location: location.child(path), }); } else if (!definedKeys.includes(outputKey)) { report({ message: `Output key "${outputKey}" is not defined in step "${stepId}". Available outputs: [ ${definedKeys.join(', ')} ].`, location: location.child(path), }); } } } function checkRuntimeExpressions(value, ctx, path = []) { if (typeof value === 'string') { matchWorkflowOutput({ value, report: ctx.report, location: ctx.location, path, definedWorkflowOutputs, }); matchStepOutput({ value, report: ctx.report, location: ctx.location, path, definedStepOutputs, }); } else if (isPlainObject(value) || Array.isArray(value)) { for (const [key, val] of Object.entries(value)) { checkRuntimeExpressions(val, ctx, [...path, key]); } } } return { Step: { enter(step, { report: _report, location: _location }) { if (!step.outputs) return; definedStepOutputs.set(step.stepId, Object.keys(step.outputs)); }, }, Workflow: { enter(workflow, { report: _report, location: _location }) { if (!workflow.outputs) return; definedWorkflowOutputs.set(workflow.workflowId, Object.keys(workflow.outputs)); }, }, Parameters: { enter(parameters, ctx) { deferredValidationTasks.push(() => { checkRuntimeExpressions(parameters, ctx); }); }, }, RequestBody: { enter(requestBody, ctx) { deferredValidationTasks.push(() => { checkRuntimeExpressions(requestBody, ctx); }); }, }, CriterionObject: { enter(criteria, ctx) { deferredValidationTasks.push(() => { checkRuntimeExpressions(criteria.condition, ctx); }); }, }, Outputs: { enter(outputs, ctx) { deferredValidationTasks.push(() => { checkRuntimeExpressions(outputs, ctx); }); }, }, ExtendedSecurity: { enter(extendedSecurity, ctx) { deferredValidationTasks.push(() => { checkRuntimeExpressions(extendedSecurity, ctx); }); }, }, ExtendedOperation: { enter(extendedOperation, ctx) { deferredValidationTasks.push(() => { checkRuntimeExpressions(extendedOperation, ctx); }); }, }, Root: { leave() { // Run all deferred validations after all outputs are collected for (const task of deferredValidationTasks) { task(); } }, }, }; }; //# sourceMappingURL=outputs-defined.js.map