@arronqzy/blueprint-dsl 1.0.8 → 1.0.10
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.
- package/dist/index.cjs +2885 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +778 -0
- package/dist/index.d.ts +778 -0
- package/dist/index.js +2747 -0
- package/dist/index.js.map +1 -0
- package/package.json +14 -5
- package/src/behaviors/default.ts +0 -64
- package/src/blueprint-signal.ts +0 -14
- package/src/clock-config.ts +0 -232
- package/src/core/behavior-registry.ts +0 -32
- package/src/core/behavior.ts +0 -13
- package/src/core/executor.ts +0 -51
- package/src/core/scheduler-loop.ts +0 -13
- package/src/event-config.ts +0 -211
- package/src/fetch-config.ts +0 -281
- package/src/incoming-node-scope.ts +0 -411
- package/src/index.ts +0 -33
- package/src/json-config.ts +0 -37
- package/src/lifecycle.ts +0 -45
- package/src/logic-config.ts +0 -53
- package/src/node-signal.ts +0 -83
- package/src/nodes/definitions.ts +0 -105
- package/src/runtime/blueprint-cycle.ts +0 -136
- package/src/runtime/clock-active-registry.ts +0 -30
- package/src/runtime/clock-scheduler.ts +0 -49
- package/src/runtime/execution-trace.ts +0 -55
- package/src/runtime/graph-runner.ts +0 -1523
- package/src/scope-autocomplete.ts +0 -114
- package/src/scope-template.ts +0 -93
- package/src/swagger.ts +0 -192
- package/src/type.ts +0 -34
package/src/index.ts
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
export * from "./type.js";
|
|
2
|
-
export * from "./lifecycle.js";
|
|
3
|
-
export * from "./node-signal.js";
|
|
4
|
-
export * from "./blueprint-signal.js";
|
|
5
|
-
export * from "./core/behavior.js";
|
|
6
|
-
export * from "./core/behavior-registry.js";
|
|
7
|
-
export * from "./core/executor.js";
|
|
8
|
-
export * from "./behaviors/default.js";
|
|
9
|
-
export * from "./fetch-config.js";
|
|
10
|
-
export * from "./scope-template.js";
|
|
11
|
-
export * from "./scope-autocomplete.js";
|
|
12
|
-
export * from "./incoming-node-scope.js";
|
|
13
|
-
export * from "./json-config.js";
|
|
14
|
-
export * from "./logic-config.js";
|
|
15
|
-
export * from "./clock-config.js";
|
|
16
|
-
export * from "./event-config.js";
|
|
17
|
-
export * from "./swagger.js";
|
|
18
|
-
export * from "./runtime/execution-trace.js";
|
|
19
|
-
export * from "./runtime/blueprint-cycle.js";
|
|
20
|
-
export * from "./runtime/graph-runner.js";
|
|
21
|
-
export {
|
|
22
|
-
buildClockSessionKey,
|
|
23
|
-
scheduleClockOutputs,
|
|
24
|
-
createClockOutputValue,
|
|
25
|
-
abortClockNode,
|
|
26
|
-
isClockScheduleActive,
|
|
27
|
-
} from "./runtime/clock-scheduler.js";
|
|
28
|
-
export {
|
|
29
|
-
isClockNodeActive,
|
|
30
|
-
getActiveClockNodeIds,
|
|
31
|
-
subscribeActiveClockNodes,
|
|
32
|
-
} from "./runtime/clock-active-registry.js";
|
|
33
|
-
export * from "./nodes/definitions.js";
|
package/src/json-config.ts
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
export type JsonNodeConfig = {
|
|
2
|
-
jsonString: string;
|
|
3
|
-
};
|
|
4
|
-
|
|
5
|
-
export const DEFAULT_JSON_NODE_CONFIG: JsonNodeConfig = {
|
|
6
|
-
jsonString: "{}",
|
|
7
|
-
};
|
|
8
|
-
|
|
9
|
-
export function validateJsonString(
|
|
10
|
-
jsonString: string
|
|
11
|
-
): { ok: true; value: unknown } | { ok: false; error: string } {
|
|
12
|
-
const raw = jsonString?.trim();
|
|
13
|
-
if (!raw) {
|
|
14
|
-
return { ok: false, error: "JSON 不能为空" };
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
try {
|
|
18
|
-
const value = JSON.parse(raw) as unknown;
|
|
19
|
-
if (value === null || typeof value !== "object") {
|
|
20
|
-
return { ok: false, error: "JSON 根节点必须是 object 或 array" };
|
|
21
|
-
}
|
|
22
|
-
return { ok: true, value };
|
|
23
|
-
} catch (error) {
|
|
24
|
-
return {
|
|
25
|
-
ok: false,
|
|
26
|
-
error: error instanceof Error ? error.message : "JSON 格式无效",
|
|
27
|
-
};
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export function parseJsonConfig(config: JsonNodeConfig): unknown {
|
|
32
|
-
const result = validateJsonString(config.jsonString ?? "");
|
|
33
|
-
if (!result.ok) {
|
|
34
|
-
throw new Error(result.error);
|
|
35
|
-
}
|
|
36
|
-
return result.value;
|
|
37
|
-
}
|
package/src/lifecycle.ts
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
/** 页面生命周期阶段 */
|
|
2
|
-
export const PAGE_LIFECYCLE_PHASES = [
|
|
3
|
-
"created",
|
|
4
|
-
"beforeMount",
|
|
5
|
-
"mounted",
|
|
6
|
-
"updated",
|
|
7
|
-
"beforeDestroy",
|
|
8
|
-
"destroy",
|
|
9
|
-
"activated",
|
|
10
|
-
"deactivated",
|
|
11
|
-
/** 被其他蓝图的蓝图配置节点引用且收到真信号时触发 */
|
|
12
|
-
"blueprintActivated",
|
|
13
|
-
] as const;
|
|
14
|
-
|
|
15
|
-
export type PageLifecyclePhase = (typeof PAGE_LIFECYCLE_PHASES)[number];
|
|
16
|
-
|
|
17
|
-
export type LifecycleSignal = {
|
|
18
|
-
/** 触发的生命周期枚举 */
|
|
19
|
-
phase: PageLifecyclePhase;
|
|
20
|
-
/** 触发时刻(Unix 毫秒时间戳) */
|
|
21
|
-
timestamp: number;
|
|
22
|
-
/** ISO 8601 时间字符串,便于下游节点展示或日志 */
|
|
23
|
-
isoTime: string;
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
export const PAGE_LIFECYCLE_LABELS: Record<PageLifecyclePhase, string> = {
|
|
27
|
-
created: "已创建 (created)",
|
|
28
|
-
beforeMount: "挂载前 (beforeMount)",
|
|
29
|
-
mounted: "已挂载 (mounted)",
|
|
30
|
-
updated: "已更新 (updated)",
|
|
31
|
-
beforeDestroy: "销毁前 (beforeDestroy)",
|
|
32
|
-
destroy: "已销毁 (destroy)",
|
|
33
|
-
activated: "已激活 (activated)",
|
|
34
|
-
deactivated: "已停用 (deactivated)",
|
|
35
|
-
blueprintActivated: "蓝图激活 (blueprintActivated)",
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
export function createLifecycleSignal(phase: PageLifecyclePhase): LifecycleSignal {
|
|
39
|
-
const timestamp = Date.now();
|
|
40
|
-
return {
|
|
41
|
-
phase,
|
|
42
|
-
timestamp,
|
|
43
|
-
isoTime: new Date(timestamp).toISOString(),
|
|
44
|
-
};
|
|
45
|
-
}
|
package/src/logic-config.ts
DELETED
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
export type LogicNodeConfig = {
|
|
2
|
-
/** 用户手写的 update(input) 函数脚本 */
|
|
3
|
-
sourceCode: string;
|
|
4
|
-
};
|
|
5
|
-
|
|
6
|
-
export const DEFAULT_LOGIC_NODE_CONFIG: LogicNodeConfig = {
|
|
7
|
-
sourceCode: `function update(input) {
|
|
8
|
-
return input;
|
|
9
|
-
}`,
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
function compileLogicRunner(sourceCode: string): (input: unknown) => unknown {
|
|
13
|
-
return new Function(
|
|
14
|
-
"input",
|
|
15
|
-
`"use strict";
|
|
16
|
-
${sourceCode}
|
|
17
|
-
if (typeof update !== "function") {
|
|
18
|
-
throw new TypeError("需要定义 update(input) 函数");
|
|
19
|
-
}
|
|
20
|
-
return update(input);`
|
|
21
|
-
) as (input: unknown) => unknown;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export function validateLogicSourceCode(
|
|
25
|
-
sourceCode: string
|
|
26
|
-
): { ok: true } | { ok: false; error: string } {
|
|
27
|
-
const raw = sourceCode?.trim();
|
|
28
|
-
if (!raw) {
|
|
29
|
-
return { ok: false, error: "代码不能为空" };
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
try {
|
|
33
|
-
compileLogicRunner(raw);
|
|
34
|
-
return { ok: true };
|
|
35
|
-
} catch (error) {
|
|
36
|
-
return {
|
|
37
|
-
ok: false,
|
|
38
|
-
error: error instanceof Error ? error.message : "JavaScript 语法错误",
|
|
39
|
-
};
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export function executeLogicConfig(
|
|
44
|
-
config: LogicNodeConfig,
|
|
45
|
-
input: unknown
|
|
46
|
-
): unknown {
|
|
47
|
-
const validation = validateLogicSourceCode(config.sourceCode ?? "");
|
|
48
|
-
if (!validation.ok) {
|
|
49
|
-
throw new Error(validation.error);
|
|
50
|
-
}
|
|
51
|
-
const run = compileLogicRunner(config.sourceCode);
|
|
52
|
-
return run(input);
|
|
53
|
-
}
|
package/src/node-signal.ts
DELETED
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
import type { Value } from "./type.js";
|
|
2
|
-
|
|
3
|
-
/** 真信号:节点正常执行完成时的输出 */
|
|
4
|
-
export type TrueSignal<T = Value> = {
|
|
5
|
-
kind: "true";
|
|
6
|
-
value: T;
|
|
7
|
-
timestamp: number;
|
|
8
|
-
isoTime: string;
|
|
9
|
-
};
|
|
10
|
-
|
|
11
|
-
/** 假信号:节点执行出错时的输出 */
|
|
12
|
-
export type FalseSignal = {
|
|
13
|
-
kind: "false";
|
|
14
|
-
error: string;
|
|
15
|
-
timestamp: number;
|
|
16
|
-
isoTime: string;
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
export type NodeSignal<T = Value> = TrueSignal<T> | FalseSignal;
|
|
20
|
-
|
|
21
|
-
export function isTrueSignal<T = Value>(
|
|
22
|
-
signal: unknown
|
|
23
|
-
): signal is TrueSignal<T> {
|
|
24
|
-
return (
|
|
25
|
-
!!signal &&
|
|
26
|
-
typeof signal === "object" &&
|
|
27
|
-
(signal as TrueSignal).kind === "true"
|
|
28
|
-
);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export function isFalseSignal(signal: unknown): signal is FalseSignal {
|
|
32
|
-
return (
|
|
33
|
-
!!signal &&
|
|
34
|
-
typeof signal === "object" &&
|
|
35
|
-
(signal as FalseSignal).kind === "false"
|
|
36
|
-
);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export function createTrueSignal<T = Value>(value: T): TrueSignal<T> {
|
|
40
|
-
const timestamp = Date.now();
|
|
41
|
-
return {
|
|
42
|
-
kind: "true",
|
|
43
|
-
value,
|
|
44
|
-
timestamp,
|
|
45
|
-
isoTime: new Date(timestamp).toISOString(),
|
|
46
|
-
};
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export function createFalseSignal(error: string): FalseSignal {
|
|
50
|
-
const timestamp = Date.now();
|
|
51
|
-
return {
|
|
52
|
-
kind: "false",
|
|
53
|
-
error,
|
|
54
|
-
timestamp,
|
|
55
|
-
isoTime: new Date(timestamp).toISOString(),
|
|
56
|
-
};
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* 同一输入端口连 n 条线时的「或」语义:任一为真则端口为真;
|
|
61
|
-
* 若无真且存在假信号则合并为假;全无连线则为 undefined。
|
|
62
|
-
*/
|
|
63
|
-
export function combinePortSignalsOr(
|
|
64
|
-
values: Array<Value | undefined>
|
|
65
|
-
): Value | undefined {
|
|
66
|
-
const defined = values.filter((value) => value !== undefined);
|
|
67
|
-
if (defined.length === 0) return undefined;
|
|
68
|
-
|
|
69
|
-
for (const value of defined) {
|
|
70
|
-
if (isTrueSignal(value)) {
|
|
71
|
-
return createTrueSignal(value.value);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
const falseSignals = defined.filter(isFalseSignal);
|
|
76
|
-
if (falseSignals.length > 0) {
|
|
77
|
-
return createFalseSignal(
|
|
78
|
-
falseSignals.map((signal) => signal.error).join(";")
|
|
79
|
-
);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
return undefined;
|
|
83
|
-
}
|
package/src/nodes/definitions.ts
DELETED
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
import type { NodeDefinition } from "../type.js";
|
|
2
|
-
|
|
3
|
-
export const BLUEPRINT_NODE_TYPE = "Blueprint";
|
|
4
|
-
export const DEFAULT_LOGIC_NODE_TYPE = "Logic";
|
|
5
|
-
export const LIFECYCLE_NODE_TYPE = "Lifecycle";
|
|
6
|
-
export const FETCH_NODE_TYPE = "DataFetch";
|
|
7
|
-
export const JSON_NODE_TYPE = "JsonNode";
|
|
8
|
-
export const CLOCK_NODE_TYPE = "Clock";
|
|
9
|
-
export const AND_NODE_TYPE = "AndLogic";
|
|
10
|
-
export const VIEW_NODE_TYPE = "ViewBind";
|
|
11
|
-
export const EVENT_NODE_TYPE = "ViewEvent";
|
|
12
|
-
|
|
13
|
-
/** 蓝图引用节点:单入单出,收到真信号后执行嵌套蓝图 */
|
|
14
|
-
export const blueprintNodeDefinition: NodeDefinition = {
|
|
15
|
-
type: BLUEPRINT_NODE_TYPE,
|
|
16
|
-
inputs: [{ name: "in", kind: "data" }],
|
|
17
|
-
outputs: [{ name: "out", kind: "data" }],
|
|
18
|
-
behavior: { kind: "js", ref: "blueprint-ref-run" },
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
export const defaultLogicNodeDefinition: NodeDefinition = {
|
|
22
|
-
type: DEFAULT_LOGIC_NODE_TYPE,
|
|
23
|
-
inputs: [{ name: "in", kind: "data", optional: true }],
|
|
24
|
-
outputs: [{ name: "out", kind: "data" }],
|
|
25
|
-
behavior: { kind: "js", ref: "logic-run" },
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
/** 生命周期钩子节点:无入流,触发后向下游发出真/假信号 */
|
|
29
|
-
export const lifecycleNodeDefinition: NodeDefinition = {
|
|
30
|
-
type: LIFECYCLE_NODE_TYPE,
|
|
31
|
-
inputs: [],
|
|
32
|
-
outputs: [{ name: "out", kind: "data" }],
|
|
33
|
-
behavior: { kind: "js", ref: "lifecycle-emit" },
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
/** 数据源获取:收到真信号后发起 fetch,成功/失败输出对应信号 */
|
|
37
|
-
export const fetchNodeDefinition: NodeDefinition = {
|
|
38
|
-
type: FETCH_NODE_TYPE,
|
|
39
|
-
inputs: [{ name: "in", kind: "data" }],
|
|
40
|
-
outputs: [{ name: "out", kind: "data" }],
|
|
41
|
-
behavior: { kind: "js", ref: "fetch-run" },
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
/** JSON 解析:收到真信号后将配置的 JSON 转为 object 输出 */
|
|
45
|
-
export const jsonNodeDefinition: NodeDefinition = {
|
|
46
|
-
type: JSON_NODE_TYPE,
|
|
47
|
-
inputs: [{ name: "in", kind: "data" }],
|
|
48
|
-
outputs: [{ name: "out", kind: "data" }],
|
|
49
|
-
behavior: { kind: "js", ref: "json-parse-run" },
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
/** 时钟节点:收到真信号后按间隔输出含当前时间的真信号 */
|
|
53
|
-
export const clockNodeDefinition: NodeDefinition = {
|
|
54
|
-
type: CLOCK_NODE_TYPE,
|
|
55
|
-
inputs: [{ name: "in", kind: "data" }],
|
|
56
|
-
outputs: [{ name: "out", kind: "data" }],
|
|
57
|
-
behavior: { kind: "js", ref: "clock-run" },
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
/** 并运算:两路输入均为真时输出真信号;单端口多连线按「或」合并 */
|
|
61
|
-
export const andLogicNodeDefinition: NodeDefinition = {
|
|
62
|
-
type: AND_NODE_TYPE,
|
|
63
|
-
inputs: [
|
|
64
|
-
{ name: "inA", kind: "data" },
|
|
65
|
-
{ name: "inB", kind: "data" },
|
|
66
|
-
],
|
|
67
|
-
outputs: [{ name: "out", kind: "data" }],
|
|
68
|
-
behavior: { kind: "js", ref: "and-run" },
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
/** 视图绑定节点:收到信号后为关联视图元素写入 scope,并向下游透传 */
|
|
72
|
-
export const viewBindNodeDefinition: NodeDefinition = {
|
|
73
|
-
type: VIEW_NODE_TYPE,
|
|
74
|
-
inputs: [{ name: "in", kind: "data" }],
|
|
75
|
-
outputs: [{ name: "out", kind: "data" }],
|
|
76
|
-
behavior: { kind: "js", ref: "view-bind-run" },
|
|
77
|
-
};
|
|
78
|
-
|
|
79
|
-
/** 视图事件节点:输入必须来自生命周期节点,对应阶段触发后注册事件,视图事件发生时输出真信号 */
|
|
80
|
-
export const viewEventNodeDefinition: NodeDefinition = {
|
|
81
|
-
type: EVENT_NODE_TYPE,
|
|
82
|
-
inputs: [{ name: "in", kind: "data" }],
|
|
83
|
-
outputs: [{ name: "out", kind: "data" }],
|
|
84
|
-
behavior: { kind: "js", ref: "event-emit" },
|
|
85
|
-
};
|
|
86
|
-
|
|
87
|
-
export const nodeDefinitionRegistry: Record<string, NodeDefinition> = {
|
|
88
|
-
[BLUEPRINT_NODE_TYPE]: blueprintNodeDefinition,
|
|
89
|
-
[DEFAULT_LOGIC_NODE_TYPE]: defaultLogicNodeDefinition,
|
|
90
|
-
[LIFECYCLE_NODE_TYPE]: lifecycleNodeDefinition,
|
|
91
|
-
[FETCH_NODE_TYPE]: fetchNodeDefinition,
|
|
92
|
-
[JSON_NODE_TYPE]: jsonNodeDefinition,
|
|
93
|
-
[CLOCK_NODE_TYPE]: clockNodeDefinition,
|
|
94
|
-
[AND_NODE_TYPE]: andLogicNodeDefinition,
|
|
95
|
-
[VIEW_NODE_TYPE]: viewBindNodeDefinition,
|
|
96
|
-
[EVENT_NODE_TYPE]: viewEventNodeDefinition,
|
|
97
|
-
};
|
|
98
|
-
|
|
99
|
-
export function getNodeDefinition(type: string): NodeDefinition {
|
|
100
|
-
const def = nodeDefinitionRegistry[type];
|
|
101
|
-
if (!def) {
|
|
102
|
-
throw new Error(`NodeDefinition not found: ${type}`);
|
|
103
|
-
}
|
|
104
|
-
return def;
|
|
105
|
-
}
|
|
@@ -1,136 +0,0 @@
|
|
|
1
|
-
import { BLUEPRINT_NODE_TYPE } from "../nodes/definitions.js";
|
|
2
|
-
import type { LibraryBlueprintResolver, RunnableGraph } from "./graph-runner.js";
|
|
3
|
-
|
|
4
|
-
export const BLUEPRINT_CALL_STACK_KEY = "__blueprintCallStack";
|
|
5
|
-
|
|
6
|
-
export class BlueprintCycleError extends Error {
|
|
7
|
-
readonly cycleIds: string[];
|
|
8
|
-
|
|
9
|
-
constructor(cycleIds: string[], message: string) {
|
|
10
|
-
super(message);
|
|
11
|
-
this.name = "BlueprintCycleError";
|
|
12
|
-
this.cycleIds = cycleIds;
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export function collectBlueprintLibraryRefs(graph: RunnableGraph): string[] {
|
|
17
|
-
const ids = new Set<string>();
|
|
18
|
-
for (const node of graph.nodes) {
|
|
19
|
-
if (node.nodeType === BLUEPRINT_NODE_TYPE && node.libraryBlueprintId) {
|
|
20
|
-
ids.add(node.libraryBlueprintId);
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
return [...ids];
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export function formatBlueprintCycleMessage(
|
|
27
|
-
cycleIds: string[],
|
|
28
|
-
resolveName?: (libraryBlueprintId: string) => string
|
|
29
|
-
): string {
|
|
30
|
-
const labels = cycleIds.map((id) => resolveName?.(id) ?? id);
|
|
31
|
-
return `蓝图引用存在死循环:${labels.join(" → ")}`;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export type BlueprintCycleCheckOptions = {
|
|
35
|
-
rootGraph: RunnableGraph;
|
|
36
|
-
/** 当前蓝图对应的蓝图库 id;工作区蓝图未入库时为 null */
|
|
37
|
-
rootLibraryBlueprintId?: string | null;
|
|
38
|
-
resolveLibraryBlueprint: LibraryBlueprintResolver;
|
|
39
|
-
resolveBlueprintName?: (libraryBlueprintId: string) => string;
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
export type BlueprintCycleCheckResult =
|
|
43
|
-
| { ok: true }
|
|
44
|
-
| { ok: false; cycleIds: string[]; message: string };
|
|
45
|
-
|
|
46
|
-
export async function detectBlueprintReferenceCycle(
|
|
47
|
-
options: BlueprintCycleCheckOptions
|
|
48
|
-
): Promise<BlueprintCycleCheckResult> {
|
|
49
|
-
const {
|
|
50
|
-
rootGraph,
|
|
51
|
-
rootLibraryBlueprintId,
|
|
52
|
-
resolveLibraryBlueprint,
|
|
53
|
-
resolveBlueprintName,
|
|
54
|
-
} = options;
|
|
55
|
-
|
|
56
|
-
const graphCache = new Map<string, RunnableGraph | null>();
|
|
57
|
-
|
|
58
|
-
const loadGraph = async (libraryBlueprintId: string) => {
|
|
59
|
-
if (!graphCache.has(libraryBlueprintId)) {
|
|
60
|
-
graphCache.set(
|
|
61
|
-
libraryBlueprintId,
|
|
62
|
-
await resolveLibraryBlueprint(libraryBlueprintId)
|
|
63
|
-
);
|
|
64
|
-
}
|
|
65
|
-
return graphCache.get(libraryBlueprintId) ?? null;
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
const visit = async (
|
|
69
|
-
path: string[],
|
|
70
|
-
graph: RunnableGraph
|
|
71
|
-
): Promise<string[] | null> => {
|
|
72
|
-
for (const refId of collectBlueprintLibraryRefs(graph)) {
|
|
73
|
-
const cycleIndex = path.indexOf(refId);
|
|
74
|
-
if (cycleIndex !== -1) {
|
|
75
|
-
return [...path.slice(cycleIndex), refId];
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
const nested = await loadGraph(refId);
|
|
79
|
-
if (!nested) continue;
|
|
80
|
-
|
|
81
|
-
const cycle = await visit([...path, refId], nested);
|
|
82
|
-
if (cycle) return cycle;
|
|
83
|
-
}
|
|
84
|
-
return null;
|
|
85
|
-
};
|
|
86
|
-
|
|
87
|
-
const initialPath = rootLibraryBlueprintId ? [rootLibraryBlueprintId] : [];
|
|
88
|
-
const cycleIds = await visit(initialPath, rootGraph);
|
|
89
|
-
|
|
90
|
-
if (!cycleIds) {
|
|
91
|
-
return { ok: true };
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
return {
|
|
95
|
-
ok: false,
|
|
96
|
-
cycleIds,
|
|
97
|
-
message: formatBlueprintCycleMessage(cycleIds, resolveBlueprintName),
|
|
98
|
-
};
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
export async function assertNoBlueprintReferenceCycle(
|
|
102
|
-
options: BlueprintCycleCheckOptions
|
|
103
|
-
): Promise<void> {
|
|
104
|
-
const result = await detectBlueprintReferenceCycle(options);
|
|
105
|
-
if (!result.ok) {
|
|
106
|
-
throw new BlueprintCycleError(result.cycleIds, result.message);
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
export function resolveBlueprintCallStack(
|
|
111
|
-
scopeStack: string[] | undefined,
|
|
112
|
-
rootLibraryBlueprintId?: string | null
|
|
113
|
-
): string[] {
|
|
114
|
-
if (scopeStack) return scopeStack;
|
|
115
|
-
return rootLibraryBlueprintId ? [rootLibraryBlueprintId] : [];
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
export function detectRuntimeBlueprintCycle(
|
|
119
|
-
callStack: string[],
|
|
120
|
-
targetLibraryBlueprintId: string,
|
|
121
|
-
resolveName?: (libraryBlueprintId: string) => string
|
|
122
|
-
): BlueprintCycleCheckResult {
|
|
123
|
-
const cycleIndex = callStack.indexOf(targetLibraryBlueprintId);
|
|
124
|
-
if (cycleIndex === -1) {
|
|
125
|
-
return { ok: true };
|
|
126
|
-
}
|
|
127
|
-
const cycleIds = [
|
|
128
|
-
...callStack.slice(cycleIndex),
|
|
129
|
-
targetLibraryBlueprintId,
|
|
130
|
-
];
|
|
131
|
-
return {
|
|
132
|
-
ok: false,
|
|
133
|
-
cycleIds,
|
|
134
|
-
message: formatBlueprintCycleMessage(cycleIds, resolveName),
|
|
135
|
-
};
|
|
136
|
-
}
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
const activeClockNodeIds = new Set<string>();
|
|
2
|
-
const listeners = new Set<() => void>();
|
|
3
|
-
|
|
4
|
-
function emit() {
|
|
5
|
-
listeners.forEach((listener) => listener());
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
export function markClockNodeActive(nodeId: string) {
|
|
9
|
-
if (activeClockNodeIds.has(nodeId)) return;
|
|
10
|
-
activeClockNodeIds.add(nodeId);
|
|
11
|
-
emit();
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export function markClockNodeInactive(nodeId: string) {
|
|
15
|
-
if (!activeClockNodeIds.delete(nodeId)) return;
|
|
16
|
-
emit();
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export function isClockNodeActive(nodeId: string): boolean {
|
|
20
|
-
return activeClockNodeIds.has(nodeId);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export function getActiveClockNodeIds(): string[] {
|
|
24
|
-
return [...activeClockNodeIds];
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export function subscribeActiveClockNodes(listener: () => void) {
|
|
28
|
-
listeners.add(listener);
|
|
29
|
-
return () => listeners.delete(listener);
|
|
30
|
-
}
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
buildClockSignalValue,
|
|
3
|
-
normalizeClockConfig,
|
|
4
|
-
startClockSchedule,
|
|
5
|
-
stopAllClockSchedules,
|
|
6
|
-
stopClockSchedule,
|
|
7
|
-
abortClockSession,
|
|
8
|
-
isClockScheduleActive,
|
|
9
|
-
isClockSessionAborted,
|
|
10
|
-
clearClockSessionAbort,
|
|
11
|
-
validateClockScheduleConfig,
|
|
12
|
-
type ClockNodeConfig,
|
|
13
|
-
} from "../clock-config.js";
|
|
14
|
-
import {
|
|
15
|
-
markClockNodeActive,
|
|
16
|
-
markClockNodeInactive,
|
|
17
|
-
} from "./clock-active-registry.js";
|
|
18
|
-
|
|
19
|
-
export function buildClockSessionKey(scopeId: string, nodeId: string): string {
|
|
20
|
-
return `${scopeId}:${nodeId}`;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export function abortClockNode(scopeId: string, nodeId: string): void {
|
|
24
|
-
abortClockSession(buildClockSessionKey(scopeId, nodeId));
|
|
25
|
-
markClockNodeInactive(nodeId);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export function scheduleClockOutputs(options: {
|
|
29
|
-
sessionKey: string;
|
|
30
|
-
config: ClockNodeConfig;
|
|
31
|
-
onTick: () => void | Promise<void>;
|
|
32
|
-
onSettled?: () => void;
|
|
33
|
-
}): () => void {
|
|
34
|
-
return startClockSchedule(options);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export {
|
|
38
|
-
stopClockSchedule,
|
|
39
|
-
stopAllClockSchedules,
|
|
40
|
-
abortClockSession,
|
|
41
|
-
isClockScheduleActive,
|
|
42
|
-
isClockSessionAborted,
|
|
43
|
-
clearClockSessionAbort,
|
|
44
|
-
validateClockScheduleConfig,
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
export function createClockOutputValue(config: ClockNodeConfig) {
|
|
48
|
-
return buildClockSignalValue(normalizeClockConfig(config));
|
|
49
|
-
}
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import type { Value } from "../type.js";
|
|
2
|
-
import { isFalseSignal, isTrueSignal } from "../node-signal.js";
|
|
3
|
-
|
|
4
|
-
export type ExecutionTraceEntry = {
|
|
5
|
-
id: string;
|
|
6
|
-
nodeId: string;
|
|
7
|
-
nodeLabel?: string;
|
|
8
|
-
nodeType: string;
|
|
9
|
-
startedAt: number;
|
|
10
|
-
finishedAt: number;
|
|
11
|
-
isoTime: string;
|
|
12
|
-
inputs: Record<string, unknown>;
|
|
13
|
-
outputs: Record<string, unknown>;
|
|
14
|
-
error?: string;
|
|
15
|
-
/** 时钟节点第几次 tick(从 1 开始) */
|
|
16
|
-
clockTickIndex?: number;
|
|
17
|
-
/** 时钟节点本次调度总 tick 数 */
|
|
18
|
-
clockTickTotal?: number;
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
export type ExecutionRunRecord = {
|
|
22
|
-
runId: string;
|
|
23
|
-
blueprintId: string | null;
|
|
24
|
-
blueprintName: string;
|
|
25
|
-
lifecycleNodeId: string;
|
|
26
|
-
lifecycleNodeLabel?: string;
|
|
27
|
-
lifecyclePhase?: string;
|
|
28
|
-
startedAt: number;
|
|
29
|
-
finishedAt?: number;
|
|
30
|
-
status: "running" | "completed" | "paused" | "failed";
|
|
31
|
-
entries: ExecutionTraceEntry[];
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
export function serializeTraceValue(value: Value | undefined): unknown {
|
|
35
|
-
if (value === undefined) return undefined;
|
|
36
|
-
if (isTrueSignal(value)) {
|
|
37
|
-
return {
|
|
38
|
-
kind: "true",
|
|
39
|
-
value: value.value,
|
|
40
|
-
isoTime: value.isoTime,
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
|
-
if (isFalseSignal(value)) {
|
|
44
|
-
return {
|
|
45
|
-
kind: "false",
|
|
46
|
-
error: value.error,
|
|
47
|
-
isoTime: value.isoTime,
|
|
48
|
-
};
|
|
49
|
-
}
|
|
50
|
-
return value;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
export function createTraceEntryId() {
|
|
54
|
-
return `trace_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`;
|
|
55
|
-
}
|