@arronqzy/vue-blueprint 0.1.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.
Files changed (57) hide show
  1. package/README.md +50 -0
  2. package/package.json +44 -0
  3. package/src/BlueprintCanvasContext.ts +71 -0
  4. package/src/BlueprintNodeConfigSidebar.vue +338 -0
  5. package/src/blueprint.css +327 -0
  6. package/src/blueprintNodeTypes.ts +20 -0
  7. package/src/components/BluePrintVueRoot.vue +73 -0
  8. package/src/components/BlueprintCanvas.vue +220 -0
  9. package/src/components/BlueprintContextMenu.vue +114 -0
  10. package/src/components/BlueprintExecutionLogPanel.vue +294 -0
  11. package/src/components/BlueprintMetaDialog.vue +80 -0
  12. package/src/components/BlueprintNodeSwitchTaskDialog.vue +41 -0
  13. package/src/components/ClockNodeConfigPanel.vue +124 -0
  14. package/src/components/FetchNodeConfigPanel.vue +559 -0
  15. package/src/components/FetchUrlAutocomplete.vue +174 -0
  16. package/src/components/JsonNodeConfigPanel.vue +73 -0
  17. package/src/components/LogicNodeConfigPanel.vue +73 -0
  18. package/src/components/ViewElementMultiSelect.vue +50 -0
  19. package/src/composables/useBlueprintDebugSession.ts +441 -0
  20. package/src/composables/useBlueprintFlowState.ts +486 -0
  21. package/src/composables/useBlueprintFlowViewport.ts +65 -0
  22. package/src/composables/useBlueprintNodeSelectionGuard.ts +41 -0
  23. package/src/composables/useBlueprintPageLifecycle.ts +244 -0
  24. package/src/createBlueprintEdgeTypes.ts +10 -0
  25. package/src/edges/BlueprintSmoothEdge.vue +31 -0
  26. package/src/env.d.ts +7 -0
  27. package/src/fetch-config-task-store.ts +206 -0
  28. package/src/flowCoordinates.ts +19 -0
  29. package/src/flowDefaults.ts +9 -0
  30. package/src/graph/blueprint-graph.ts +265 -0
  31. package/src/graph/document.ts +422 -0
  32. package/src/graph/index.ts +7 -0
  33. package/src/graph/node-summary.ts +88 -0
  34. package/src/graph/node-types.ts +9 -0
  35. package/src/graph/sync-edges.ts +69 -0
  36. package/src/graph/sync-nodes.ts +110 -0
  37. package/src/graph/vue-flow-adapter.ts +127 -0
  38. package/src/index.ts +37 -0
  39. package/src/library/blueprint-io.ts +108 -0
  40. package/src/library/blueprint-library-db.ts +112 -0
  41. package/src/library/execution-log-db.ts +171 -0
  42. package/src/library/execution-log-settings.ts +50 -0
  43. package/src/library/swagger-docs.ts +56 -0
  44. package/src/library/types.ts +35 -0
  45. package/src/nodes/AndFlowNode.vue +60 -0
  46. package/src/nodes/BlueprintFlowNode.vue +26 -0
  47. package/src/nodes/BlueprintNodeCard.vue +155 -0
  48. package/src/nodes/BlueprintNodeShell.vue +70 -0
  49. package/src/nodes/ClockFlowNode.vue +60 -0
  50. package/src/nodes/FetchFlowNode.vue +26 -0
  51. package/src/nodes/JsonFlowNode.vue +26 -0
  52. package/src/nodes/LifecycleFlowNode.vue +45 -0
  53. package/src/nodes/LogicFlowNode.vue +26 -0
  54. package/src/runtime/document-to-runnable-graph.ts +51 -0
  55. package/src/runtime/execution-overlay.ts +169 -0
  56. package/src/types.ts +1 -0
  57. package/src/utils/cn.ts +3 -0
@@ -0,0 +1,265 @@
1
+ import {
2
+ createBlueprintDocument,
3
+ createNodeId,
4
+ defaultBlueprintNodeLabel,
5
+ filterInvalidBlueprintEdges,
6
+ patchNodeConfigSource,
7
+ resolveNodeClockConfig,
8
+ resolveNodeFetchConfig,
9
+ resolveNodeJsonConfig,
10
+ resolveNodeLogicConfig,
11
+ resolveRunnableNodeType,
12
+ sanitizeBlueprintDocument,
13
+ type BlueprintDocument,
14
+ type BlueprintGraphEdge,
15
+ type BlueprintGraphNode,
16
+ } from "./document";
17
+ import {
18
+ BLUEPRINT_NODE_TYPE,
19
+ DEFAULT_LOGIC_NODE_TYPE,
20
+ LIFECYCLE_NODE_TYPE,
21
+ } from "./node-types";
22
+ import type { PageLifecyclePhase } from "@arronqzy/blueprint-dsl";
23
+
24
+ export class BlueprintGraph {
25
+ readonly document: BlueprintDocument;
26
+
27
+ constructor(document: BlueprintDocument = createBlueprintDocument()) {
28
+ this.document = sanitizeBlueprintDocument(document);
29
+ }
30
+
31
+ static empty(id?: string) {
32
+ return new BlueprintGraph(sanitizeBlueprintDocument(createBlueprintDocument(id)));
33
+ }
34
+
35
+ static fromDocument(document: BlueprintDocument) {
36
+ return new BlueprintGraph(document);
37
+ }
38
+
39
+ getNode(nodeId: string): BlueprintGraphNode | undefined {
40
+ return this.document.nodes.find((n) => n.id === nodeId);
41
+ }
42
+
43
+ addBlueprintNode(position: { x: number; y: number }, label?: string) {
44
+ const node: BlueprintGraphNode = {
45
+ id: createNodeId("bp"),
46
+ role: "blueprint",
47
+ nodeType: BLUEPRINT_NODE_TYPE,
48
+ label: label ?? defaultBlueprintNodeLabel("blueprint"),
49
+ position: { ...position },
50
+ nestedBlueprintId: createNodeId("nested_bp"),
51
+ };
52
+ return this.withDocument({
53
+ ...this.document,
54
+ nodes: [...this.document.nodes, node],
55
+ });
56
+ }
57
+
58
+ addLogicNode(
59
+ parentBlueprintId: string,
60
+ position: { x: number; y: number },
61
+ label?: string
62
+ ) {
63
+ const parent = this.getNode(parentBlueprintId);
64
+ if (!parent || parent.role !== "blueprint") {
65
+ throw new Error(`Parent blueprint node not found: ${parentBlueprintId}`);
66
+ }
67
+
68
+ const node: BlueprintGraphNode = {
69
+ id: createNodeId("logic"),
70
+ role: "logic",
71
+ nodeType: DEFAULT_LOGIC_NODE_TYPE,
72
+ label: label ?? defaultBlueprintNodeLabel("logic"),
73
+ position: { ...position },
74
+ parentId: parentBlueprintId,
75
+ };
76
+
77
+ return this.withDocument({
78
+ ...this.document,
79
+ nodes: [...this.document.nodes, node],
80
+ });
81
+ }
82
+
83
+ addLifecycleNode(
84
+ parentBlueprintId: string,
85
+ position: { x: number; y: number },
86
+ lifecyclePhase: PageLifecyclePhase = "mounted",
87
+ label?: string
88
+ ) {
89
+ const parent = this.getNode(parentBlueprintId);
90
+ if (!parent || parent.role !== "blueprint") {
91
+ throw new Error(`Parent blueprint node not found: ${parentBlueprintId}`);
92
+ }
93
+
94
+ const node: BlueprintGraphNode = {
95
+ id: createNodeId("lifecycle"),
96
+ role: "lifecycle",
97
+ nodeType: LIFECYCLE_NODE_TYPE,
98
+ label: label ?? defaultBlueprintNodeLabel("lifecycle"),
99
+ position: { ...position },
100
+ parentId: parentBlueprintId,
101
+ lifecyclePhase,
102
+ configSource: "lifecycle",
103
+ };
104
+
105
+ return this.withDocument({
106
+ ...this.document,
107
+ nodes: [...this.document.nodes, node],
108
+ });
109
+ }
110
+
111
+ removeNode(nodeId: string) {
112
+ const target = this.getNode(nodeId);
113
+ if (!target) return this;
114
+
115
+ const removeIds = new Set<string>([nodeId]);
116
+ if (target.role === "blueprint") {
117
+ for (const n of this.document.nodes) {
118
+ if (n.parentId === nodeId) removeIds.add(n.id);
119
+ }
120
+ }
121
+
122
+ return this.withDocument({
123
+ ...this.document,
124
+ nodes: this.document.nodes.filter((n) => !removeIds.has(n.id)),
125
+ edges: this.document.edges.filter(
126
+ (e) => !removeIds.has(e.source) && !removeIds.has(e.target)
127
+ ),
128
+ });
129
+ }
130
+
131
+ updateNodePosition(nodeId: string, position: { x: number; y: number }) {
132
+ return this.withDocument({
133
+ ...this.document,
134
+ nodes: this.document.nodes.map((n) =>
135
+ n.id === nodeId ? { ...n, position: { ...position } } : n
136
+ ),
137
+ });
138
+ }
139
+
140
+ applyNodePositions(
141
+ updates: Array<{ id: string; position: { x: number; y: number } }>
142
+ ) {
143
+ if (updates.length === 0) return this;
144
+ const positionById = new Map(updates.map((u) => [u.id, u.position]));
145
+ return this.withDocument({
146
+ ...this.document,
147
+ nodes: this.document.nodes.map((n) => {
148
+ const position = positionById.get(n.id);
149
+ return position ? { ...n, position: { ...position } } : n;
150
+ }),
151
+ });
152
+ }
153
+
154
+ addEdge(edge: Omit<BlueprintGraphEdge, "id"> & { id?: string }) {
155
+ const nextEdge: BlueprintGraphEdge = {
156
+ id: edge.id ?? createNodeId("edge"),
157
+ source: edge.source,
158
+ target: edge.target,
159
+ sourceHandle: edge.sourceHandle,
160
+ targetHandle: edge.targetHandle,
161
+ };
162
+ return this.withDocument({
163
+ ...this.document,
164
+ edges: [...this.document.edges, nextEdge],
165
+ });
166
+ }
167
+
168
+ removeEdge(edgeId: string) {
169
+ if (!this.document.edges.some((e) => e.id === edgeId)) return this;
170
+ return this.withDocument({
171
+ ...this.document,
172
+ edges: this.document.edges.filter((e) => e.id !== edgeId),
173
+ });
174
+ }
175
+
176
+ replaceEdges(edges: BlueprintGraphEdge[]) {
177
+ return this.withDocument({
178
+ ...this.document,
179
+ edges: filterInvalidBlueprintEdges(this.document, edges),
180
+ });
181
+ }
182
+
183
+ updateNode(
184
+ nodeId: string,
185
+ patch: Partial<
186
+ Pick<
187
+ BlueprintGraphNode,
188
+ | "label"
189
+ | "role"
190
+ | "nodeType"
191
+ | "configSource"
192
+ | "viewElementId"
193
+ | "viewElementIds"
194
+ | "nestedBlueprintId"
195
+ | "libraryBlueprintId"
196
+ | "lifecyclePhase"
197
+ | "fetchConfig"
198
+ | "jsonConfig"
199
+ | "logicConfig"
200
+ | "clockConfig"
201
+ >
202
+ >
203
+ ) {
204
+ const exists = this.getNode(nodeId);
205
+ if (!exists) return this;
206
+
207
+ const configPatch =
208
+ patch.configSource && patch.configSource !== exists.configSource
209
+ ? patchNodeConfigSource(exists, patch.configSource)
210
+ : {};
211
+
212
+ return this.withDocument({
213
+ ...this.document,
214
+ nodes: this.document.nodes.map((n) => {
215
+ if (n.id !== nodeId) return n;
216
+ const nextNode = { ...n, ...configPatch, ...patch };
217
+ if (
218
+ patch.viewElementIds !== undefined ||
219
+ patch.viewElementId !== undefined
220
+ ) {
221
+ const ids =
222
+ patch.viewElementIds !== undefined
223
+ ? [...new Set(patch.viewElementIds.filter(Boolean))]
224
+ : patch.viewElementId
225
+ ? [patch.viewElementId]
226
+ : [];
227
+ nextNode.viewElementIds = ids.length > 0 ? ids : undefined;
228
+ nextNode.viewElementId = undefined;
229
+ }
230
+ if (patch.fetchConfig) {
231
+ nextNode.fetchConfig = {
232
+ ...resolveNodeFetchConfig(n),
233
+ ...patch.fetchConfig,
234
+ };
235
+ }
236
+ if (patch.jsonConfig) {
237
+ nextNode.jsonConfig = {
238
+ ...resolveNodeJsonConfig(n),
239
+ ...patch.jsonConfig,
240
+ };
241
+ }
242
+ if (patch.logicConfig) {
243
+ nextNode.logicConfig = {
244
+ ...resolveNodeLogicConfig(n),
245
+ ...patch.logicConfig,
246
+ };
247
+ }
248
+ if (patch.clockConfig) {
249
+ nextNode.clockConfig = {
250
+ ...resolveNodeClockConfig(n),
251
+ ...patch.clockConfig,
252
+ };
253
+ }
254
+ return {
255
+ ...nextNode,
256
+ nodeType: resolveRunnableNodeType(nextNode),
257
+ };
258
+ }),
259
+ });
260
+ }
261
+
262
+ withDocument(document: BlueprintDocument) {
263
+ return new BlueprintGraph(document);
264
+ }
265
+ }
@@ -0,0 +1,422 @@
1
+ import type {
2
+ ClockNodeConfig,
3
+ FetchRequestConfig,
4
+ JsonNodeConfig,
5
+ LogicNodeConfig,
6
+ PageLifecyclePhase,
7
+ } from "@arronqzy/blueprint-dsl";
8
+ import {
9
+ BLUEPRINT_NODE_TYPE,
10
+ CLOCK_NODE_TYPE,
11
+ AND_NODE_TYPE,
12
+ DEFAULT_CLOCK_NODE_CONFIG,
13
+ DEFAULT_FETCH_REQUEST_CONFIG,
14
+ DEFAULT_JSON_NODE_CONFIG,
15
+ DEFAULT_LOGIC_NODE_CONFIG,
16
+ DEFAULT_LOGIC_NODE_TYPE,
17
+ FETCH_NODE_TYPE,
18
+ JSON_NODE_TYPE,
19
+ LIFECYCLE_NODE_TYPE,
20
+ normalizeClockConfig,
21
+ VIEW_NODE_TYPE,
22
+ } from "@arronqzy/blueprint-dsl";
23
+
24
+ export type { FetchRequestConfig as BlueprintFetchConfig } from "@arronqzy/blueprint-dsl";
25
+ export type { JsonNodeConfig as BlueprintJsonConfig } from "@arronqzy/blueprint-dsl";
26
+ export type { LogicNodeConfig as BlueprintLogicConfig } from "@arronqzy/blueprint-dsl";
27
+ export type { ClockNodeConfig as BlueprintClockConfig } from "@arronqzy/blueprint-dsl";
28
+
29
+ export type BlueprintNodeRole = "blueprint" | "logic" | "and" | "lifecycle" | "fetch" | "json" | "clock";
30
+
31
+ /** 决定右侧配置面板展示哪类配置 */
32
+ export type BlueprintConfigSource =
33
+ | "blueprint"
34
+ | "logic"
35
+ | "and"
36
+ | "lifecycle"
37
+ | "view"
38
+ | "fetch"
39
+ | "json"
40
+ | "clock";
41
+
42
+ export type BlueprintGraphNode = {
43
+ id: string;
44
+ role: BlueprintNodeRole;
45
+ /** 对应 @arronqzy/blueprint-dsl NodeDefinition.type */
46
+ nodeType: string;
47
+ label: string;
48
+ position: { x: number; y: number };
49
+ /** 配置面板类型;默认由 role 推断 */
50
+ configSource?: BlueprintConfigSource;
51
+ /** @deprecated 请使用 viewElementIds;读取时由 resolveViewElementIds 兼容 */
52
+ viewElementId?: string;
53
+ /** 关联的视图画布节点 id 列表,配置类型为 view 时可一次绑定多个 */
54
+ viewElementIds?: string[];
55
+ /** logic 节点所属的蓝图节点 */
56
+ parentId?: string;
57
+ /** blueprint 节点嵌套子蓝图 id(内部标识,保留兼容) */
58
+ nestedBlueprintId?: string;
59
+ /** 引用的蓝图库记录 id,执行时会等待该蓝图完成后继续 */
60
+ libraryBlueprintId?: string;
61
+ /** lifecycle 节点监听的生命周期阶段 */
62
+ lifecyclePhase?: PageLifecyclePhase;
63
+ /** 数据源获取节点的 fetch 配置 */
64
+ fetchConfig?: FetchRequestConfig;
65
+ /** JSON 节点的 JSON 字符串配置 */
66
+ jsonConfig?: JsonNodeConfig;
67
+ /** 逻辑节点的 JavaScript 脚本配置 */
68
+ logicConfig?: LogicNodeConfig;
69
+ /** 时钟节点的间隔与时间格式配置 */
70
+ clockConfig?: ClockNodeConfig;
71
+ };
72
+
73
+ export function resolveViewElementIds(
74
+ node: Pick<BlueprintGraphNode, "viewElementId" | "viewElementIds">
75
+ ): string[] {
76
+ if (node.viewElementIds?.length) return [...node.viewElementIds];
77
+ if (node.viewElementId) return [node.viewElementId];
78
+ return [];
79
+ }
80
+
81
+ /** 仅保留仍存在于视图画布中的关联 id */
82
+ export function pruneViewElementIds(
83
+ ids: readonly string[],
84
+ existingViewElementIds: ReadonlySet<string>
85
+ ): string[] {
86
+ return ids.filter((id) => existingViewElementIds.has(id));
87
+ }
88
+
89
+ export function resolveBlueprintConfigSource(
90
+ node: Pick<
91
+ BlueprintGraphNode,
92
+ "role" | "nodeType" | "configSource" | "viewElementId" | "viewElementIds"
93
+ >
94
+ ): BlueprintConfigSource {
95
+ if (node.configSource) return node.configSource;
96
+ if (node.role === "lifecycle") return "lifecycle";
97
+ if (node.role === "clock") return "clock";
98
+ if (node.role === "and") return "and";
99
+ if (node.role === "fetch") return "fetch";
100
+ if (node.role === "json") return "json";
101
+ if (node.nodeType === FETCH_NODE_TYPE) return "fetch";
102
+ if (node.nodeType === JSON_NODE_TYPE) return "json";
103
+ if (node.nodeType === CLOCK_NODE_TYPE) return "clock";
104
+ if (node.nodeType === VIEW_NODE_TYPE) return "view";
105
+ if (resolveViewElementIds(node).length > 0) return "view";
106
+ return node.role === "logic" ? "logic" : "blueprint";
107
+ }
108
+
109
+ /** 可执行图节点类型:与配置类型对齐,避免文档里残留旧 nodeType */
110
+ export function resolveRunnableNodeType(
111
+ node: Pick<
112
+ BlueprintGraphNode,
113
+ "role" | "nodeType" | "configSource" | "viewElementId" | "viewElementIds"
114
+ >
115
+ ): string {
116
+ switch (resolveBlueprintConfigSource(node)) {
117
+ case "view":
118
+ return VIEW_NODE_TYPE;
119
+ case "lifecycle":
120
+ return LIFECYCLE_NODE_TYPE;
121
+ case "clock":
122
+ return CLOCK_NODE_TYPE;
123
+ case "and":
124
+ return AND_NODE_TYPE;
125
+ case "fetch":
126
+ return FETCH_NODE_TYPE;
127
+ case "json":
128
+ return JSON_NODE_TYPE;
129
+ case "logic":
130
+ return DEFAULT_LOGIC_NODE_TYPE;
131
+ default:
132
+ return BLUEPRINT_NODE_TYPE;
133
+ }
134
+ }
135
+
136
+ /** 画布节点顶部(拖拽区)展示的配置类型文案 */
137
+ export const BLUEPRINT_CONFIG_TYPE_LABELS: Record<
138
+ BlueprintConfigSource,
139
+ string
140
+ > = {
141
+ blueprint: "蓝图",
142
+ logic: "逻辑",
143
+ and: "并运算",
144
+ lifecycle: "生命周期",
145
+ view: "视图",
146
+ fetch: "数据源获取",
147
+ json: "JSON 节点",
148
+ clock: "时钟",
149
+ };
150
+
151
+ export function resolveBlueprintNodeTypeLabel(
152
+ node: Pick<
153
+ BlueprintGraphNode,
154
+ "role" | "nodeType" | "configSource" | "viewElementId" | "viewElementIds"
155
+ >
156
+ ): string {
157
+ return BLUEPRINT_CONFIG_TYPE_LABELS[resolveBlueprintConfigSource(node)];
158
+ }
159
+
160
+ /** 新建节点时的默认显示名称(与类型文案一致) */
161
+ export function defaultBlueprintNodeLabel(
162
+ configSource: BlueprintConfigSource
163
+ ): string {
164
+ return BLUEPRINT_CONFIG_TYPE_LABELS[configSource];
165
+ }
166
+
167
+ export function resolveNodeFetchConfig(
168
+ node: Pick<BlueprintGraphNode, "fetchConfig">
169
+ ): FetchRequestConfig {
170
+ return {
171
+ ...DEFAULT_FETCH_REQUEST_CONFIG,
172
+ ...node.fetchConfig,
173
+ headers: {
174
+ ...DEFAULT_FETCH_REQUEST_CONFIG.headers,
175
+ ...node.fetchConfig?.headers,
176
+ },
177
+ swaggerEndpoints: node.fetchConfig?.swaggerEndpoints,
178
+ };
179
+ }
180
+
181
+ export function resolveNodeJsonConfig(
182
+ node: Pick<BlueprintGraphNode, "jsonConfig">
183
+ ): JsonNodeConfig {
184
+ return {
185
+ ...DEFAULT_JSON_NODE_CONFIG,
186
+ ...node.jsonConfig,
187
+ };
188
+ }
189
+
190
+ export function resolveNodeLogicConfig(
191
+ node: Pick<BlueprintGraphNode, "logicConfig">
192
+ ): LogicNodeConfig {
193
+ return {
194
+ ...DEFAULT_LOGIC_NODE_CONFIG,
195
+ ...node.logicConfig,
196
+ };
197
+ }
198
+
199
+ export function resolveNodeClockConfig(
200
+ node: Pick<BlueprintGraphNode, "clockConfig">
201
+ ): ClockNodeConfig {
202
+ return normalizeClockConfig({
203
+ ...DEFAULT_CLOCK_NODE_CONFIG,
204
+ ...node.clockConfig,
205
+ });
206
+ }
207
+
208
+ export type BlueprintGraphEdge = {
209
+ id: string;
210
+ source: string;
211
+ target: string;
212
+ sourceHandle?: string;
213
+ targetHandle?: string;
214
+ };
215
+
216
+ export type BlueprintDocument = {
217
+ id: string;
218
+ name?: string;
219
+ nodes: BlueprintGraphNode[];
220
+ edges: BlueprintGraphEdge[];
221
+ /** 为 true 时,假信号不阻塞任务链,会继续向下游传递 */
222
+ allowFalseSignalPropagation?: boolean;
223
+ };
224
+
225
+ export function createBlueprintDocument(id = "default"): BlueprintDocument {
226
+ return { id, nodes: [], edges: [] };
227
+ }
228
+
229
+ export function createNodeId(prefix = "node"): string {
230
+ return `${prefix}_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`;
231
+ }
232
+
233
+ const noInputTargetNodeIds = (document: BlueprintDocument) =>
234
+ new Set(
235
+ document.nodes
236
+ .filter((node) => node.role === "lifecycle")
237
+ .map((node) => node.id)
238
+ );
239
+
240
+ /** 生命周期节点无输入口:移除连入边,并修正节点配置 */
241
+ export function sanitizeBlueprintDocument(
242
+ document: BlueprintDocument
243
+ ): BlueprintDocument {
244
+ const blockedTargets = noInputTargetNodeIds(document);
245
+
246
+ const nodes = document.nodes.map((node) => {
247
+ let next = node;
248
+ if (node.viewElementId && !node.viewElementIds?.length) {
249
+ next = {
250
+ ...next,
251
+ viewElementIds: [node.viewElementId],
252
+ viewElementId: undefined,
253
+ };
254
+ }
255
+ const runnableNodeType = resolveRunnableNodeType(next);
256
+ if (next.nodeType !== runnableNodeType) {
257
+ next = { ...next, nodeType: runnableNodeType };
258
+ }
259
+ if (next.role === "lifecycle") {
260
+ return { ...next, configSource: "lifecycle" as const };
261
+ }
262
+ if (next.role === "clock") {
263
+ return { ...next, configSource: "clock" as const };
264
+ }
265
+ if (next.role === "fetch") {
266
+ return { ...next, configSource: "fetch" as const };
267
+ }
268
+ if (next.role === "json") {
269
+ return { ...next, configSource: "json" as const };
270
+ }
271
+ if (next.role === "and") {
272
+ return { ...next, configSource: "and" as const };
273
+ }
274
+ if (next.configSource === "lifecycle" || next.configSource === "clock") {
275
+ return { ...next, configSource: undefined };
276
+ }
277
+ return next;
278
+ });
279
+
280
+ const edges = document.edges.filter((edge) => !blockedTargets.has(edge.target));
281
+
282
+ return { ...document, nodes, edges };
283
+ }
284
+
285
+ export function filterInvalidBlueprintEdges(
286
+ document: BlueprintDocument,
287
+ edges: BlueprintGraphEdge[]
288
+ ): BlueprintGraphEdge[] {
289
+ const blockedTargets = noInputTargetNodeIds(document);
290
+ return edges.filter((edge) => !blockedTargets.has(edge.target));
291
+ }
292
+
293
+ /** 切换配置类型时同步节点 role / nodeType,确保生命周期节点渲染为无输入口 */
294
+ export function patchNodeConfigSource(
295
+ node: BlueprintGraphNode,
296
+ configSource: BlueprintConfigSource
297
+ ): Partial<BlueprintGraphNode> {
298
+ if (configSource === "lifecycle") {
299
+ const phase = node.lifecyclePhase ?? "mounted";
300
+ return {
301
+ role: "lifecycle",
302
+ nodeType: LIFECYCLE_NODE_TYPE,
303
+ configSource: "lifecycle",
304
+ lifecyclePhase: phase,
305
+ viewElementId: undefined,
306
+ viewElementIds: undefined,
307
+ libraryBlueprintId: undefined,
308
+ fetchConfig: undefined,
309
+ jsonConfig: undefined,
310
+ logicConfig: undefined,
311
+ clockConfig: undefined,
312
+ };
313
+ }
314
+
315
+ if (configSource === "clock") {
316
+ return {
317
+ role: "clock",
318
+ nodeType: CLOCK_NODE_TYPE,
319
+ configSource: "clock",
320
+ clockConfig: resolveNodeClockConfig(node),
321
+ lifecyclePhase: undefined,
322
+ viewElementId: undefined,
323
+ viewElementIds: undefined,
324
+ libraryBlueprintId: undefined,
325
+ fetchConfig: undefined,
326
+ jsonConfig: undefined,
327
+ logicConfig: undefined,
328
+ };
329
+ }
330
+
331
+ if (configSource === "fetch") {
332
+ return {
333
+ role: "fetch",
334
+ nodeType: FETCH_NODE_TYPE,
335
+ configSource: "fetch",
336
+ lifecyclePhase: undefined,
337
+ viewElementId: undefined,
338
+ viewElementIds: undefined,
339
+ libraryBlueprintId: undefined,
340
+ fetchConfig: resolveNodeFetchConfig(node),
341
+ jsonConfig: undefined,
342
+ logicConfig: undefined,
343
+ clockConfig: undefined,
344
+ };
345
+ }
346
+
347
+ if (configSource === "json") {
348
+ return {
349
+ role: "json",
350
+ nodeType: JSON_NODE_TYPE,
351
+ configSource: "json",
352
+ lifecyclePhase: undefined,
353
+ viewElementId: undefined,
354
+ viewElementIds: undefined,
355
+ libraryBlueprintId: undefined,
356
+ fetchConfig: undefined,
357
+ jsonConfig: resolveNodeJsonConfig(node),
358
+ logicConfig: undefined,
359
+ clockConfig: undefined,
360
+ };
361
+ }
362
+
363
+ if (configSource === "blueprint") {
364
+ return {
365
+ role: "blueprint",
366
+ nodeType: BLUEPRINT_NODE_TYPE,
367
+ configSource: "blueprint",
368
+ lifecyclePhase: undefined,
369
+ nestedBlueprintId: node.nestedBlueprintId ?? createNodeId("nested_bp"),
370
+ viewElementId: undefined,
371
+ viewElementIds: undefined,
372
+ fetchConfig: undefined,
373
+ jsonConfig: undefined,
374
+ logicConfig: undefined,
375
+ clockConfig: undefined,
376
+ };
377
+ }
378
+
379
+ if (configSource === "logic") {
380
+ return {
381
+ role: "logic",
382
+ nodeType: DEFAULT_LOGIC_NODE_TYPE,
383
+ configSource: "logic",
384
+ lifecyclePhase: undefined,
385
+ viewElementId: undefined,
386
+ viewElementIds: undefined,
387
+ libraryBlueprintId: undefined,
388
+ fetchConfig: undefined,
389
+ jsonConfig: undefined,
390
+ logicConfig: resolveNodeLogicConfig(node),
391
+ clockConfig: undefined,
392
+ };
393
+ }
394
+
395
+ if (configSource === "and") {
396
+ return {
397
+ role: "and",
398
+ nodeType: AND_NODE_TYPE,
399
+ configSource: "and",
400
+ lifecyclePhase: undefined,
401
+ viewElementId: undefined,
402
+ viewElementIds: undefined,
403
+ libraryBlueprintId: undefined,
404
+ fetchConfig: undefined,
405
+ jsonConfig: undefined,
406
+ logicConfig: undefined,
407
+ clockConfig: undefined,
408
+ };
409
+ }
410
+
411
+ return {
412
+ role: "blueprint",
413
+ nodeType: VIEW_NODE_TYPE,
414
+ configSource: "view",
415
+ lifecyclePhase: undefined,
416
+ libraryBlueprintId: undefined,
417
+ fetchConfig: undefined,
418
+ jsonConfig: undefined,
419
+ logicConfig: undefined,
420
+ clockConfig: undefined,
421
+ };
422
+ }
@@ -0,0 +1,7 @@
1
+ export * from "./document";
2
+ export * from "./node-types";
3
+ export * from "./blueprint-graph";
4
+ export * from "./vue-flow-adapter";
5
+ export * from "./sync-edges";
6
+ export * from "./sync-nodes";
7
+ export * from "./node-summary";