@arronqzy/blueprint-dsl 0.0.1 → 1.0.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arronqzy/blueprint-dsl",
3
- "version": "0.0.1",
3
+ "version": "1.0.3",
4
4
  "description": "Abuilder blueprint DSL runtime",
5
5
  "license": "MIT",
6
6
  "files": [
@@ -17,7 +17,7 @@
17
17
  "eslint": "^8.57.0",
18
18
  "typescript": "^5.5.4",
19
19
  "@arronqzy/eslint-config": "0.0.0",
20
- "@arronqzy/typescript-config": "0.0.0"
20
+ "@arronqzy/typescript-config": "1.0.0"
21
21
  },
22
22
  "publishConfig": {
23
23
  "access": "public"
package/readme.md CHANGED
@@ -1,4 +1,31 @@
1
+ # @arronqzy/blueprint-dsl
2
+
3
+ Abuilder 蓝图的 **DSL 与运行时** 包,定义节点类型、行为注册与图执行逻辑,不依赖 React。
4
+
5
+ ## 功能概览
6
+
7
+ - **节点定义**:Flow / Data 端口、节点元数据与默认行为
8
+ - **行为注册表**:`BehaviorRegistry` 注册 JS / 内置行为
9
+ - **执行器**:按 Token 驱动节点执行、Scope 传递
10
+ - **图运行器**:整图调度、循环检测、执行轨迹
11
+ - **内置节点**:逻辑判断、HTTP 请求、JSON 处理、时钟定时、Swagger 等
12
+ - **时钟调度**:多 tick 输出、中止与活跃状态订阅
13
+
14
+ ## 安装
15
+
16
+ ```bash
17
+ pnpm add @arronqzy/blueprint-dsl rxjs
18
+ ```
19
+
20
+ ## 快速开始
21
+
1
22
  ```ts
23
+ import {
24
+ BehaviorRegistry,
25
+ Executor,
26
+ type NodeDefinition,
27
+ } from "@arronqzy/blueprint-dsl";
28
+
2
29
  const behaviors = new BehaviorRegistry();
3
30
 
4
31
  behaviors.registerJS("print-js", async ({ io }) => {
@@ -6,27 +33,42 @@ behaviors.registerJS("print-js", async ({ io }) => {
6
33
  io.emitFlow("out");
7
34
  });
8
35
 
9
- behaviors.registerJS("sleep-js", async ({ io }) => {
10
- const ms = await io.getInput("ms");
11
- await new Promise((r) => setTimeout(r, ms));
12
- io.emitFlow("out");
13
- });
14
-
15
-
16
36
  const PrintNode: NodeDefinition = {
17
37
  type: "Print",
18
38
  inputs: [{ name: "msg", kind: "data" }],
19
39
  outputs: [{ name: "out", kind: "flow" }],
20
- behavior: { kind: "js", ref: "print-js" }
21
- }
40
+ behavior: { kind: "js", ref: "print-js" },
41
+ };
22
42
 
43
+ const executor = new Executor(behaviors);
23
44
 
24
45
  await executor.executeToken({
25
46
  tokenId: "t1",
26
47
  nodeId: "n1",
27
48
  nodeType: "Print",
28
49
  inPort: "in",
29
- scope: { vars: new Map() }
30
- })
31
-
50
+ scope: { vars: new Map() },
51
+ });
32
52
  ```
53
+
54
+ ## 核心模块
55
+
56
+ | 路径 | 说明 |
57
+ |------|------|
58
+ | `core/behavior.js` | 行为接口与 IO |
59
+ | `core/behavior-registry.js` | 行为注册 |
60
+ | `core/executor.js` | 单节点执行 |
61
+ | `runtime/graph-runner.js` | 整图运行 |
62
+ | `runtime/execution-trace.js` | 执行轨迹 |
63
+ | `runtime/clock-scheduler.js` | 时钟节点调度 |
64
+ | `nodes/definitions.js` | 内置节点定义 |
65
+ | `fetch-config.js` / `logic-config.js` 等 | 各类型节点配置结构 |
66
+
67
+ ## 与上层包的关系
68
+
69
+ - **`@arronqzy/react-blueprint`**:React Flow 画布、调试 UI、蓝图库
70
+ - **`@arronqzy/react-view`**:蓝图执行结果通过 Scope 驱动视图节点
71
+
72
+ ## 许可证
73
+
74
+ MIT
@@ -67,6 +67,21 @@ export const DEFAULT_FETCH_REQUEST_CONFIG: FetchRequestConfig = {
67
67
  timeoutMs: 30000,
68
68
  };
69
69
 
70
+ /** 合并默认项与节点上保存的 fetch 配置,供运行时发起请求 */
71
+ export function normalizeFetchRequestConfig(
72
+ config?: FetchRequestConfig
73
+ ): FetchRequestConfig {
74
+ return {
75
+ ...DEFAULT_FETCH_REQUEST_CONFIG,
76
+ ...config,
77
+ headers: {
78
+ ...DEFAULT_FETCH_REQUEST_CONFIG.headers,
79
+ ...config?.headers,
80
+ },
81
+ swaggerEndpoints: config?.swaggerEndpoints,
82
+ };
83
+ }
84
+
70
85
  export type FetchResultValue = {
71
86
  status: number;
72
87
  statusText: string;
@@ -1,5 +1,9 @@
1
1
  import type { FetchRequestConfig } from "../fetch-config.js";
2
- import { executeFetch, DEFAULT_FETCH_REQUEST_CONFIG } from "../fetch-config.js";
2
+ import {
3
+ executeFetch,
4
+ normalizeFetchRequestConfig,
5
+ resolveFetchRequestUrl,
6
+ } from "../fetch-config.js";
3
7
  import type { JsonNodeConfig } from "../json-config.js";
4
8
  import {
5
9
  DEFAULT_JSON_NODE_CONFIG,
@@ -275,6 +279,7 @@ export class BlueprintGraphRunner {
275
279
  return;
276
280
  }
277
281
  const token = queue.shift()!;
282
+ if (!this.syncTokenFromGraph(token)) continue;
278
283
  try {
279
284
  await executor.executeToken(token);
280
285
  } catch (error) {
@@ -357,6 +362,7 @@ export class BlueprintGraphRunner {
357
362
  /** 调试过程中同步最新蓝图拓扑与节点配置 */
358
363
  updateGraph(graph: RunnableGraph): void {
359
364
  this.graph = graph;
365
+ this.syncDebugQueueWithLatestGraph();
360
366
  }
361
367
 
362
368
  /**
@@ -367,14 +373,7 @@ export class BlueprintGraphRunner {
367
373
  if (!this.debugQueue) return;
368
374
 
369
375
  if (this.traceEntries.length === 0) {
370
- const head = this.debugQueue[0];
371
- if (!head) return;
372
- const node = this.graph.nodes.find((n) => n.id === head.nodeId);
373
- if (!node) {
374
- this.debugQueue = [];
375
- return;
376
- }
377
- head.nodeType = node.nodeType;
376
+ this.syncDebugQueueWithLatestGraph();
378
377
  return;
379
378
  }
380
379
 
@@ -423,7 +422,9 @@ export class BlueprintGraphRunner {
423
422
  });
424
423
  }
425
424
  }
426
- this.debugQueue = newQueue;
425
+ if (newQueue.length > 0 || this.debugQueue.length === 0) {
426
+ this.debugQueue = newQueue;
427
+ }
427
428
  }
428
429
 
429
430
  canDebugStepBack(): boolean {
@@ -479,7 +480,9 @@ export class BlueprintGraphRunner {
479
480
 
480
481
  this.applyLifecycleScopeForNode(lifecycleNodeId);
481
482
 
482
- this.debugExecutor = this.createExecutor(this.debugQueue);
483
+ this.debugExecutor = this.createExecutor(this.debugQueue, {
484
+ emitToActiveDebugQueue: true,
485
+ });
483
486
  this.debugHistory = [this.captureDebugSnapshot()];
484
487
  }
485
488
 
@@ -542,6 +545,13 @@ export class BlueprintGraphRunner {
542
545
  try {
543
546
  this.pushDebugSnapshot();
544
547
  const token = this.debugQueue.shift()!;
548
+ if (!this.syncTokenFromGraph(token)) {
549
+ this.pushDebugSnapshot();
550
+ return {
551
+ done: this.debugQueue.length === 0,
552
+ haltedByFalseSignal: false,
553
+ };
554
+ }
545
555
 
546
556
  if (token.nodeType === CLOCK_NODE_TYPE) {
547
557
  const node = this.requireNode(token.nodeId);
@@ -721,6 +731,12 @@ export class BlueprintGraphRunner {
721
731
  this.debugExecutor ??
722
732
  this.createExecutor(this.debugQueue ?? []);
723
733
 
734
+ if (!this.syncTokenFromGraph(token)) {
735
+ return this.appendDebugTraceEntry(token, nodeLabelById, startedAt, {
736
+ error: "节点已从蓝图中移除",
737
+ });
738
+ }
739
+
724
740
  try {
725
741
  await runExecutor.executeToken(token);
726
742
  } catch (err) {
@@ -941,6 +957,7 @@ export class BlueprintGraphRunner {
941
957
  resolveLibraryBlueprint: runner.resolveLibraryBlueprint,
942
958
  rootLibraryBlueprintId: libraryId,
943
959
  resolveBlueprintName: runner.resolveBlueprintName,
960
+ onViewScopeUpdate: runner.onViewScopeUpdate,
944
961
  });
945
962
  await subRunner.emitBlueprintActivated(input.value, nestedScope);
946
963
  const nestedOutputs = await subRunner.runFromFlowEntries(nestedScope, {
@@ -975,22 +992,32 @@ export class BlueprintGraphRunner {
975
992
  const node = runner.requireNode(token.nodeId);
976
993
 
977
994
  try {
978
- const input = await io.getInput("in");
995
+ const inputPort = token.inPort || "in";
996
+ const input = await io.getInput(inputPort);
997
+ if (isFalseSignal(input)) {
998
+ io.setOutput("out", input);
999
+ io.emitFlow("out");
1000
+ return;
1001
+ }
979
1002
  if (!isTrueSignal(input)) {
980
1003
  io.setOutput(
981
1004
  "out",
982
- createFalseSignal("数据源节点需要收到真信号后才会发起请求")
1005
+ createFalseSignal(
1006
+ input === undefined
1007
+ ? "数据源节点未收到上游输出,请确认连线与上游节点已执行"
1008
+ : "数据源节点需要收到真信号后才会发起请求"
1009
+ )
983
1010
  );
984
1011
  io.emitFlow("out");
985
1012
  return;
986
1013
  }
987
1014
 
988
- const config: FetchRequestConfig = {
989
- ...DEFAULT_FETCH_REQUEST_CONFIG,
990
- ...node.fetchConfig,
991
- };
1015
+ const config = normalizeFetchRequestConfig(node.fetchConfig);
1016
+ resolveFetchRequestUrl(config);
992
1017
 
993
- const result = await executeFetch(config);
1018
+ const result = await executeFetch(config, {
1019
+ allowHttpError: runner.isDebugSessionActive(),
1020
+ });
994
1021
  io.setOutput("out", createTrueSignal(result));
995
1022
  } catch (error) {
996
1023
  io.setOutput(
@@ -1190,6 +1217,7 @@ export class BlueprintGraphRunner {
1190
1217
 
1191
1218
  while (queue.length > 0) {
1192
1219
  const token = queue.shift()!;
1220
+ if (!this.syncTokenFromGraph(token)) continue;
1193
1221
  try {
1194
1222
  await executor.executeToken(token);
1195
1223
  } catch (error) {
@@ -1215,7 +1243,10 @@ export class BlueprintGraphRunner {
1215
1243
  }
1216
1244
  }
1217
1245
 
1218
- private createExecutor(queue: ExecutionToken[]) {
1246
+ private createExecutor(
1247
+ queue: ExecutionToken[],
1248
+ options?: { emitToActiveDebugQueue?: boolean }
1249
+ ) {
1219
1250
  return new Executor({
1220
1251
  getNodeDefinition,
1221
1252
  behaviors: this.behaviors,
@@ -1223,15 +1254,20 @@ export class BlueprintGraphRunner {
1223
1254
  // 由 blueprint-ref-run 行为处理
1224
1255
  },
1225
1256
  getInputValue: async (token, port) => {
1226
- return this.resolvePortInputValue(token.nodeId, port);
1257
+ const resolvedPort = port || token.inPort || "in";
1258
+ return this.resolvePortInputValue(token.nodeId, resolvedPort);
1227
1259
  },
1228
1260
  setNodeOutput: (token, port, value) => {
1229
1261
  this.setNodeOutput(token.nodeId, port, value);
1230
1262
  },
1231
1263
  emitFlow: (token, outPort) => {
1264
+ const targetQueue =
1265
+ options?.emitToActiveDebugQueue && this.debugQueue
1266
+ ? this.debugQueue
1267
+ : queue;
1232
1268
  const nextNodes = this.findSignalTargets(token.nodeId, outPort);
1233
1269
  for (const target of nextNodes) {
1234
- queue.push({
1270
+ targetQueue.push({
1235
1271
  tokenId: createTokenId(),
1236
1272
  nodeId: target.nodeId,
1237
1273
  nodeType: target.nodeType,
@@ -1271,6 +1307,21 @@ export class BlueprintGraphRunner {
1271
1307
  return node;
1272
1308
  }
1273
1309
 
1310
+ /** 队列中的 token 可能是在节点改类型(如视图绑定)之前入队的,执行前与最新图对齐 */
1311
+ private syncTokenFromGraph(token: ExecutionToken): boolean {
1312
+ const node = this.graph.nodes.find((n) => n.id === token.nodeId);
1313
+ if (!node) return false;
1314
+ token.nodeType = node.nodeType;
1315
+ return true;
1316
+ }
1317
+
1318
+ private syncDebugQueueWithLatestGraph(): void {
1319
+ if (!this.debugQueue) return;
1320
+ this.debugQueue = this.debugQueue.filter((token) =>
1321
+ this.syncTokenFromGraph(token)
1322
+ );
1323
+ }
1324
+
1274
1325
  private findSignalTargets(nodeId: string, outPort: string) {
1275
1326
  return this.graph.edges
1276
1327
  .filter(