hangwin--mcp-chrome
142 行
4.0 KiB
TypeScript
142 行
4.0 KiB
TypeScript
/**
|
|
* @fileoverview V2/V3 Flow 双向转换工具
|
|
* @description 桥接 Builder V2 Flow 类型与 V3 RPC FlowV3 类型
|
|
*
|
|
* 设计说明:
|
|
* - Builder store 目前仍使用 V2 类型 (type, version, steps)
|
|
* - RPC 层使用 V3 类型 (kind, schemaVersion, entryNodeId)
|
|
* - 本模块提供 UI 层的类型转换,封装底层转换器
|
|
*/
|
|
|
|
import type { Flow as FlowV2 } from '@/entrypoints/background/record-replay/types';
|
|
import type { FlowV3 } from '@/entrypoints/background/record-replay-v3/domain/flow';
|
|
import {
|
|
convertFlowV2ToV3,
|
|
convertFlowV3ToV2,
|
|
} from '@/entrypoints/background/record-replay-v3/storage/import/v2-to-v3';
|
|
|
|
// ==================== Types ====================
|
|
|
|
export interface FlowConversionResult<T> {
|
|
flow: T;
|
|
warnings: string[];
|
|
}
|
|
|
|
// ==================== V2 -> V3 (for RPC calls) ====================
|
|
|
|
/**
|
|
* 将 V2 Flow 转换为 V3 格式,用于 RPC 保存
|
|
* @param flowV2 Builder store 中的 V2 Flow
|
|
* @returns V3 Flow 和警告信息
|
|
* @throws 转换失败时抛出错误
|
|
*/
|
|
export function flowV2ToV3ForRpc(flowV2: FlowV2): FlowConversionResult<FlowV3> {
|
|
const result = convertFlowV2ToV3(flowV2 as unknown as Parameters<typeof convertFlowV2ToV3>[0]);
|
|
|
|
if (!result.success || !result.data) {
|
|
const errorMsg =
|
|
result.errors.length > 0 ? result.errors.join('; ') : 'Unknown conversion error';
|
|
throw new Error(`V2→V3 conversion failed: ${errorMsg}`);
|
|
}
|
|
|
|
return {
|
|
flow: result.data,
|
|
warnings: result.warnings,
|
|
};
|
|
}
|
|
|
|
// ==================== V3 -> V2 (for Builder display) ====================
|
|
|
|
/**
|
|
* 将 V3 Flow 转换为 V2 格式,用于 Builder 显示和编辑
|
|
* @param flowV3 从 RPC 获取的 V3 Flow
|
|
* @returns V2 Flow 和警告信息
|
|
* @throws 转换失败时抛出错误
|
|
*/
|
|
export function flowV3ToV2ForBuilder(flowV3: FlowV3): FlowConversionResult<FlowV2> {
|
|
const result = convertFlowV3ToV2(flowV3);
|
|
|
|
if (!result.success || !result.data) {
|
|
const errorMsg =
|
|
result.errors.length > 0 ? result.errors.join('; ') : 'Unknown conversion error';
|
|
throw new Error(`V3→V2 conversion failed: ${errorMsg}`);
|
|
}
|
|
|
|
return {
|
|
flow: result.data as unknown as FlowV2,
|
|
warnings: result.warnings,
|
|
};
|
|
}
|
|
|
|
// ==================== Type Guards ====================
|
|
|
|
/**
|
|
* 判断是否为 V3 Flow
|
|
* @description 用于导入时判断 JSON 格式
|
|
*/
|
|
export function isFlowV3(value: unknown): value is FlowV3 {
|
|
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
return false;
|
|
}
|
|
|
|
const obj = value as Record<string, unknown>;
|
|
return (
|
|
obj.schemaVersion === 3 &&
|
|
typeof obj.id === 'string' &&
|
|
typeof obj.name === 'string' &&
|
|
typeof obj.entryNodeId === 'string' &&
|
|
Array.isArray(obj.nodes)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* 判断是否为 V2 Flow
|
|
* @description 用于导入时判断 JSON 格式
|
|
*/
|
|
export function isFlowV2(value: unknown): value is FlowV2 {
|
|
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
return false;
|
|
}
|
|
|
|
const obj = value as Record<string, unknown>;
|
|
return (
|
|
typeof obj.id === 'string' &&
|
|
typeof obj.name === 'string' &&
|
|
// V2 有 version 字段(数字),且没有 schemaVersion
|
|
typeof obj.version === 'number' &&
|
|
obj.schemaVersion === undefined &&
|
|
// V2 可能有 steps 或 nodes
|
|
(Array.isArray(obj.steps) || Array.isArray(obj.nodes))
|
|
);
|
|
}
|
|
|
|
// ==================== Import Helpers ====================
|
|
|
|
/**
|
|
* 从导入的 JSON 中提取 Flow 候选列表
|
|
* @description 支持单个 Flow、Flow 数组、或 { flows: Flow[] } 格式
|
|
*/
|
|
export function extractFlowCandidates(parsed: unknown): unknown[] {
|
|
// 数组格式
|
|
if (Array.isArray(parsed)) {
|
|
return parsed;
|
|
}
|
|
|
|
// 对象格式
|
|
if (parsed && typeof parsed === 'object') {
|
|
const obj = parsed as Record<string, unknown>;
|
|
|
|
// { flows: [...] } 格式
|
|
if (Array.isArray(obj.flows)) {
|
|
return obj.flows;
|
|
}
|
|
|
|
// 单个 Flow 对象
|
|
if (obj.id && (Array.isArray(obj.steps) || Array.isArray(obj.nodes))) {
|
|
return [obj];
|
|
}
|
|
}
|
|
|
|
return [];
|
|
}
|