UNPKG

@flowlab/all

Version:

A cool library focusing on handling various flows

183 lines (165 loc) 6.66 kB
import { IStepConfig, StepType, IWorkflowContext, NodeStatus } from '../types/index.js'; import { Step } from './step.js'; import { v4 as uuidv4 } from 'uuid'; /** * MARK: 工作流定义 * @class WorkflowDefinition * @description 定义一个工作流的结构和步骤 */ export class WorkflowDefinition { public readonly id: string; // 工作流定义的唯一 ID public readonly name?: string; // 工作流名称 (可选) public readonly description?: string; // 工作流描述 (可选) private steps: Map<string, Step> = new Map(); // 存储所有步骤,以 ID 为键 private startStepId?: string; // 工作流的起始步骤 ID constructor(id: string, name?: string, description?: string) { this.id = id || uuidv4(); // 如果未提供 ID,则生成一个 this.name = name; this.description = description; } /** * MARK: addStep * 添加一个顺序执行的任务节点步骤 * @param config - 步骤配置 (必须包含 id, type=TASK, nodeId) * @returns 返回 WorkflowDefinition 实例,支持链式调用 * @throws 如果步骤 ID 重复,则抛出错误 */ addStep(config: Omit<IStepConfig, 'type'> & { nodeId: string }): this { const fullConfig: IStepConfig = { ...config, type: StepType.TASK }; return this.addStepInternal(fullConfig); } /** * MARK: 添加条件步骤 * 添加一个条件分支步骤 * @param config - 步骤配置 (必须包含 id, type=CONDITION, condition, branches) * @returns 返回 WorkflowDefinition 实例,支持链式调用 */ addConditionStep(config: Omit<IStepConfig, 'type'> & { condition: (context: IWorkflowContext) => boolean | string; branches: { [key: string]: string } }): this { const fullConfig: IStepConfig = { ...config, type: StepType.CONDITION }; return this.addStepInternal(fullConfig); } /** * MARK: 添加并行步骤 * 添加一个并行执行步骤 * @param config - 步骤配置 (必须包含 id, type=PARALLEL, parallelSteps) * @returns 返回 WorkflowDefinition 实例,支持链式调用 */ addParallelStep(config: Omit<IStepConfig, 'type'> & { parallelSteps: IStepConfig[] }): this { const fullConfig: IStepConfig = { ...config, type: StepType.PARALLEL }; // 可以在这里递归验证 parallelSteps 内部的配置 return this.addStepInternal(fullConfig); } /** * MARK: 添加子工作流 * 添加一个子工作流步骤 * @param config - 步骤配置 (必须包含 id, type=SUB_WORKFLOW, subWorkflowId) * @returns 返回 WorkflowDefinition 实例,支持链式调用 */ addSubWorkflowStep(config: Omit<IStepConfig, 'type'> & { subWorkflowId: string }): this { const fullConfig: IStepConfig = { ...config, type: StepType.SUB_WORKFLOW }; return this.addStepInternal(fullConfig); } /** * MARK: 添加事件触发 * 添加一个事件触发步骤 * @param config - 步骤配置 (必须包含 id, type=EVENT_TRIGGER, event) * @returns 返回 WorkflowDefinition 实例,支持链式调用 */ addEventTriggerStep(config: Omit<IStepConfig, 'type'> & { event: string }): this { const fullConfig: IStepConfig = { ...config, type: StepType.EVENT_TRIGGER }; return this.addStepInternal(fullConfig); } /** * MARK: 添加事件监听 * 添加一个事件监听步骤 (通常作为起始步骤或并行分支) * @param config - 步骤配置 (必须包含 id, type=EVENT_LISTENER, event) * @returns 返回 WorkflowDefinition 实例,支持链式调用 */ addEventListenerStep(config: Omit<IStepConfig, 'type'> & { event: string }): this { const fullConfig: IStepConfig = { ...config, type: StepType.EVENT_LISTENER }; return this.addStepInternal(fullConfig); } /** * MARK: addStepInternal * NOTE: 内部方法,用于添加步骤并进行基本验证 * @param config - 完整的步骤配置 * @returns 返回 WorkflowDefinition 实例 */ addStepInternal(config: IStepConfig): this { if (this.steps.has(config.id)) { throw new Error(`工作流 "${this.id}" 中已存在 ID 为 "${config.id}" 的步骤。`); } const step = new Step(config); this.steps.set(step.id, step); // 如果是第一个添加的步骤,则设为起始步骤 (除非是事件监听器) if (!this.startStepId && step.type !== StepType.EVENT_LISTENER) { this.startStepId = step.id; } return this; } /** * MARK: setStartStep * 设置工作流的起始步骤 ID * @param stepId - 起始步骤的 ID * @returns 返回 WorkflowDefinition 实例 * @throws 如果步骤 ID 不存在,则抛出错误 */ setStartStep(stepId: string): this { if (!this.steps.has(stepId)) { throw new Error(`工作流 "${this.id}" 中不存在 ID 为 "${stepId}" 的步骤。`); } this.startStepId = stepId; return this; } /** * MARK: getStartStepId * 获取起始步骤 ID * @returns 起始步骤 ID * @throws 如果未设置起始步骤,则抛出错误 */ getStartStepId(): string { if (!this.startStepId) { throw new Error(`工作流 "${this.id}" 未设置起始步骤。`); } return this.startStepId; } /** * MARK: getStepById * 根据 ID 获取步骤实例 * @param stepId - 步骤 ID * @returns 返回步骤实例,如果未找到则返回 undefined */ getStep(stepId: string): Step | undefined { return this.steps.get(stepId); } /** * MARK: getSteps * 获取所有步骤的 Map */ getSteps(): Map<string, Step> { return this.steps; } /** * MARK: validate * 验证工作流定义的有效性 (例如,检查链接是否断开,是否存在循环等) * @returns 如果有效则返回 true,否则返回 false 或抛出错误 */ validate(): boolean { if (!this.startStepId) { console.error(`[Workflow: ${this.id}] 验证失败:未设置起始步骤。`); return false; } if (!this.steps.has(this.startStepId)) { console.error(`[Workflow: ${this.id}] 验证失败:起始步骤 "${this.startStepId}" 不存在。`); return false; } // TODO: 实现更复杂的验证逻辑 // 1. 检查所有 nextStepId, branch targets, subWorkflowId 是否指向存在的步骤/工作流 // 2. 检查是否存在无法到达的步骤 // 3. (可选) 检测简单循环 (复杂的循环检测可能需要图算法) // 4. 检查并行步骤的配置是否正确 // 5. 检查 TASK 步骤是否关联了已注册的 nodeId console.log(`[Workflow: ${this.id}] 验证通过 (基本检查)。`); return true; } }