* Apply diff operations to a workflow
(
workflow: Workflow,
request: WorkflowDiffRequest
)
| 170 | * Apply diff operations to a workflow |
| 171 | */ |
| 172 | async applyDiff( |
| 173 | workflow: Workflow, |
| 174 | request: WorkflowDiffRequest |
| 175 | ): Promise<WorkflowDiffResult> { |
| 176 | try { |
| 177 | // Reset tracking for this diff operation |
| 178 | this.renameMap.clear(); |
| 179 | this.warnings = []; |
| 180 | this.modifiedNodeIds.clear(); |
| 181 | this.removedNodeNames.clear(); |
| 182 | this.tagsToAdd = []; |
| 183 | this.tagsToRemove = []; |
| 184 | this.transferToProjectId = undefined; |
| 185 | |
| 186 | // Clone workflow to avoid modifying original |
| 187 | const workflowCopy = JSON.parse(JSON.stringify(workflow)); |
| 188 | |
| 189 | const operationEntries = buildExecutionEntries(request.operations); |
| 190 | const nodeOperationCount = request.operations.filter(isNodeOperation).length; |
| 191 | const otherOperationCount = request.operations.length - nodeOperationCount; |
| 192 | const errors: WorkflowDiffValidationError[] = []; |
| 193 | const appliedIndices: number[] = []; |
| 194 | const failedIndices: number[] = []; |
| 195 | |
| 196 | // Process based on mode |
| 197 | if (request.continueOnError) { |
| 198 | // Best-effort mode: continue even if some operations fail |
| 199 | for (const { operation, index } of operationEntries) { |
| 200 | const error = this.validateOperation(workflowCopy, operation); |
| 201 | if (error) { |
| 202 | errors.push({ |
| 203 | operation: index, |
| 204 | message: error, |
| 205 | details: operation |
| 206 | }); |
| 207 | failedIndices.push(index); |
| 208 | continue; |
| 209 | } |
| 210 | |
| 211 | try { |
| 212 | this.applyOperation(workflowCopy, operation); |
| 213 | this.flushPendingRenames(workflowCopy); |
| 214 | appliedIndices.push(index); |
| 215 | } catch (error) { |
| 216 | const errorMsg = `Failed to apply operation: ${error instanceof Error ? error.message : 'Unknown error'}`; |
| 217 | errors.push({ |
| 218 | operation: index, |
| 219 | message: errorMsg, |
| 220 | details: operation |
| 221 | }); |
| 222 | failedIndices.push(index); |
| 223 | } |
| 224 | } |
| 225 | |
| 226 | // If validateOnly flag is set, return success without applying. |
| 227 | // Include workflowCopy so the caller can run structural validation against |
| 228 | // the simulated post-diff result (#744). |
| 229 | if (request.validateOnly) { |
no test coverage detected