Преглед изворни кода

chore: hide useless api in variable engine (#180)

* chore: hide useless api in variable engine

* fix: variable-core mock chain ts check
Yiwei Mao пре 8 месеци
родитељ
комит
311180b2c8
23 измењених фајлова са 206 додато и 174 уклоњено
  1. 3 3
      packages/plugins/redux-devtool-plugin/src/connectors/variable-connector.ts
  2. 8 4
      packages/variable-engine/variable-core/__mocks__/mock-chain.ts
  3. 7 17
      packages/variable-engine/variable-core/src/ast/declaration/variable-declaration.ts
  4. 11 11
      packages/variable-engine/variable-core/src/ast/expression/base-expression.ts
  5. 13 13
      packages/variable-engine/variable-core/src/scope/datas/scope-available-data.ts
  6. 5 5
      packages/variable-engine/variable-core/src/scope/datas/scope-event-data.ts
  7. 31 17
      packages/variable-engine/variable-core/src/scope/datas/scope-output-data.ts
  8. 1 1
      packages/variable-engine/variable-core/src/scope/index.ts
  9. 8 8
      packages/variable-engine/variable-core/src/scope/scope.ts
  10. 20 2
      packages/variable-engine/variable-core/src/scope/types.ts
  11. 16 14
      packages/variable-engine/variable-core/src/scope/variable-table.ts
  12. 13 12
      packages/variable-engine/variable-core/src/variable-engine.ts
  13. 3 0
      packages/variable-engine/variable-layout/__mocks__/fixed-layout-specs.ts
  14. 4 4
      packages/variable-engine/variable-layout/__mocks__/run-fixed-layout-test.ts
  15. 4 4
      packages/variable-engine/variable-layout/__mocks__/run-free-layout-test.ts
  16. 5 5
      packages/variable-engine/variable-layout/__tests__/__snapshots__/variable-fix-layout-group.test.ts.snap
  17. 28 28
      packages/variable-engine/variable-layout/__tests__/__snapshots__/variable-fix-layout-no-config.test.ts.snap
  18. 1 1
      packages/variable-engine/variable-layout/__tests__/__snapshots__/variable-fix-layout.test.ts.snap
  19. 2 3
      packages/variable-engine/variable-layout/__tests__/variable-fix-layout-filter-start-end.test.ts
  20. 4 3
      packages/variable-engine/variable-layout/__tests__/variable-fix-layout-group.test.ts
  21. 1 2
      packages/variable-engine/variable-layout/__tests__/variable-fix-layout.test.ts
  22. 18 13
      packages/variable-engine/variable-layout/src/fixed-layout-scope-chain.ts
  23. 0 4
      packages/variable-engine/variable-layout/src/variable-layout-config.ts

+ 3 - 3
packages/plugins/redux-devtool-plugin/src/connectors/variable-connector.ts

@@ -29,11 +29,11 @@ export class VariableConnector extends BaseConnector {
   }
 
   onInit() {
-    this.variableEngine.onScopeChange(action => {
+    this.variableEngine.onScopeChange((action) => {
       const { scope, type } = action;
 
       if (type === 'delete') {
-        delete this.scopes[scope.id];
+        delete this.scopes[String(scope.id)];
       } else {
         this.scopes = {
           ...this.scopes,
@@ -41,7 +41,7 @@ export class VariableConnector extends BaseConnector {
         };
       }
 
-      this.send(`${type}/${scope.id}`);
+      this.send(`${type}/${String(scope.id)}`);
     });
   }
 }

+ 8 - 4
packages/variable-engine/variable-core/__mocks__/mock-chain.ts

@@ -19,8 +19,10 @@ export class MockScopeChain extends ScopeChain {
     }
 
     // 模拟循环依赖场景
-    if(scope.id.startsWith('cycle')) {
-      return this.variableEngine.getAllScopes().filter(_scope => _scope.id.startsWith('cycle') && _scope.id!== scope.id);
+    if (String(scope.id).startsWith('cycle')) {
+      return this.variableEngine
+        .getAllScopes()
+        .filter((_scope) => String(_scope.id).startsWith('cycle') && _scope.id !== scope.id);
     }
 
     return res;
@@ -32,8 +34,10 @@ export class MockScopeChain extends ScopeChain {
     }
 
     // 模拟循环依赖场景
-    if(scope.id.startsWith('cycle')) {
-      return this.variableEngine.getAllScopes().filter(_scope => _scope.id.startsWith('cycle') && _scope.id!== scope.id);
+    if (String(scope.id).startsWith('cycle')) {
+      return this.variableEngine
+        .getAllScopes()
+        .filter((_scope) => String(_scope.id).startsWith('cycle') && _scope.id !== scope.id);
     }
 
     return [];

+ 7 - 17
packages/variable-engine/variable-core/src/ast/declaration/variable-declaration.ts

@@ -1,6 +1,4 @@
-import { Disposable } from '@flowgram.ai/utils';
-
-import { ASTKind, type CreateASTParams } from '../types';
+import { ASTKind, GlobalEventActionType, type CreateASTParams } from '../types';
 import { ASTNode } from '../ast-node';
 import { BaseVariableField, BaseVariableFieldJSON } from './base-variable-field';
 
@@ -11,6 +9,8 @@ export type VariableDeclarationJSON<VariableMeta = any> = BaseVariableFieldJSON<
   order?: number; // 变量排序
 };
 
+export type ReSortVariableDeclarationsAction = GlobalEventActionType<'ReSortVariableDeclarations'>;
+
 export class VariableDeclaration<VariableMeta = any> extends BaseVariableField<VariableMeta> {
   static kind: string = ASTKind.VariableDeclaration;
 
@@ -22,18 +22,6 @@ export class VariableDeclaration<VariableMeta = any> extends BaseVariableField<V
 
   constructor(params: CreateASTParams) {
     super(params);
-
-    // 添加变量到变量表中
-    this.scope.output.addVariableToTable(this);
-    this.toDispose.push(
-      Disposable.create(() => {
-        // 输出变量列表发生变化
-        this.scope.output.setHasChanges();
-
-        // 从变量表中移除变量
-        this.scope.output.removeVariableFromTable(this.key);
-      }),
-    );
   }
 
   /**
@@ -50,13 +38,15 @@ export class VariableDeclaration<VariableMeta = any> extends BaseVariableField<V
   updateOrder(order: number = 0): void {
     if (order !== this._order) {
       this._order = order;
-      this.scope.output.setHasChanges();
+      this.dispatchGlobalEvent<ReSortVariableDeclarationsAction>({
+        type: 'ReSortVariableDeclarations',
+      });
       this.fireChange();
     }
   }
 
   // 监听类型变化
   onTypeChange(observer: (type: ASTNode | undefined) => void) {
-    return this.subscribe(observer, { selector: curr => curr.type });
+    return this.subscribe(observer, { selector: (curr) => curr.type });
   }
 }

+ 11 - 11
packages/variable-engine/variable-core/src/ast/expression/base-expression.ts

@@ -17,20 +17,20 @@ import { ASTNodeFlags } from '../flags';
 import { type BaseVariableField } from '../declaration';
 import { ASTNode } from '../ast-node';
 import { subsToDisposable } from '../../utils/toDisposable';
-import { VariableTable } from '../../scope';
+import { IVariableTable } from '../../scope/types';
 
 type ExpressionRefs = (BaseVariableField | undefined)[];
 
 export abstract class BaseExpression<
   JSON extends ASTNodeJSON = any,
-  InjectOpts = any,
+  InjectOpts = any
 > extends ASTNode<JSON, InjectOpts> {
   public flags: ASTNodeFlags = ASTNodeFlags.Expression;
 
   /**
    * 获取全局变量表,方便表达式获取引用变量
    */
-  get globalVariableTable(): VariableTable {
+  get globalVariableTable(): IVariableTable {
     return this.scope.variableEngine.globalVariableTable;
   }
 
@@ -77,18 +77,18 @@ export abstract class BaseExpression<
   refs$: Observable<ExpressionRefs> = this.refreshRefs$.pipe(
     map(() => this.getRefFields()),
     distinctUntilChanged<ExpressionRefs>(shallowEqual),
-    switchMap(refs =>
+    switchMap((refs) =>
       !refs?.length
         ? of([])
         : combineLatest(
-            refs.map(ref =>
+            refs.map((ref) =>
               ref
                 ? (ref.value$ as unknown as Observable<BaseVariableField | undefined>)
-                : of(undefined),
-            ),
-          ),
+                : of(undefined)
+            )
+          )
     ),
-    share(),
+    share()
   );
 
   constructor(params: CreateASTParams, opts?: InjectOpts) {
@@ -99,8 +99,8 @@ export abstract class BaseExpression<
         this.refs$.subscribe((_refs: ExpressionRefs) => {
           this._refs = _refs;
           this.fireChange();
-        }),
-      ),
+        })
+      )
     );
   }
 }

+ 13 - 13
packages/variable-engine/variable-core/src/scope/datas/scope-available-data.ts

@@ -13,7 +13,7 @@ import { shallowEqual } from 'fast-equals';
 import { Disposable } from '@flowgram.ai/utils';
 import { Emitter } from '@flowgram.ai/utils';
 
-import { VariableTable } from '../variable-table';
+import { IVariableTable } from '../types';
 import { type Scope } from '../scope';
 import { subsToDisposable } from '../../utils/toDisposable';
 import { createMemo } from '../../utils/memo';
@@ -24,7 +24,7 @@ import { Property, VariableDeclaration } from '../../ast';
 export class ScopeAvailableData {
   protected memo = createMemo();
 
-  get globalVariableTable(): VariableTable {
+  get globalVariableTable(): IVariableTable {
     return this.scope.variableEngine.globalVariableTable;
   }
 
@@ -46,25 +46,25 @@ export class ScopeAvailableData {
    */
   protected variables$: Observable<VariableDeclaration[]> = this.refresh$.pipe(
     // 输出变量是否 version 发生变化
-    map(() => flatten(this.depScopes.map(scope => scope.output.variables || []))),
+    map(() => flatten(this.depScopes.map((scope) => scope.output.variables || []))),
     // 变量列表浅比较
     distinctUntilChanged<VariableDeclaration[]>(shallowEqual),
-    share(),
+    share()
   );
 
   // 监听变量列表中的单个变量变化
   protected anyVariableChange$: Observable<VariableDeclaration> = this.variables$.pipe(
-    switchMap(_variables =>
+    switchMap((_variables) =>
       merge(
-        ..._variables.map(_v =>
+        ..._variables.map((_v) =>
           _v.value$.pipe<any>(
             // 跳过 BehaviorSubject 第一个
-            skip(1),
-          ),
-        ),
-      ),
+            skip(1)
+          )
+        )
+      )
     ),
-    share(),
+    share()
   );
 
   /**
@@ -94,7 +94,7 @@ export class ScopeAvailableData {
 
   constructor(public readonly scope: Scope) {
     this.scope.toDispose.pushAll([
-      this.onVariableListChange(_variables => {
+      this.onVariableListChange((_variables) => {
         this._variables = _variables;
         this.memo.clear();
         this.onDataChangeEmitter.fire(this._variables);
@@ -120,7 +120,7 @@ export class ScopeAvailableData {
    * 获取可访问的变量 keys
    */
   get variableKeys(): string[] {
-    return this.memo('availableKeys', () => this._variables.map(_v => _v.key));
+    return this.memo('availableKeys', () => this._variables.map((_v) => _v.key));
   }
 
   /**

+ 5 - 5
packages/variable-engine/variable-core/src/scope/datas/scope-event-data.ts

@@ -6,7 +6,7 @@ import { subsToDisposable } from '../../utils/toDisposable';
 import { type GlobalEventActionType } from '../../ast';
 
 type Observer<ActionType extends GlobalEventActionType = GlobalEventActionType> = (
-  action: ActionType,
+  action: ActionType
 ) => void;
 
 export class ScopeEventData {
@@ -20,23 +20,23 @@ export class ScopeEventData {
   }
 
   subscribe<ActionType extends GlobalEventActionType = GlobalEventActionType>(
-    observer: Observer<ActionType>,
+    observer: Observer<ActionType>
   ): Disposable {
     return subsToDisposable(this.event$.subscribe(observer as Observer));
   }
 
   on<ActionType extends GlobalEventActionType = GlobalEventActionType>(
     type: ActionType['type'],
-    observer: Observer<ActionType>,
+    observer: Observer<ActionType>
   ): Disposable {
     return subsToDisposable(
-      this.event$.pipe(filter(_action => _action.type === type)).subscribe(observer as Observer),
+      this.event$.pipe(filter((_action) => _action.type === type)).subscribe(observer as Observer)
     );
   }
 
   constructor(public readonly scope: Scope) {
     scope.toDispose.pushAll([
-      this.subscribe(_action => {
+      this.subscribe((_action) => {
         scope.variableEngine.fireGlobalEvent(_action);
       }),
     ]);

+ 31 - 17
packages/variable-engine/variable-core/src/scope/datas/scope-output-data.ts

@@ -1,14 +1,18 @@
 import { VariableTable } from '../variable-table';
+import { IVariableTable } from '../types';
 import { type Scope } from '../scope';
 import { type VariableEngine } from '../../variable-engine';
 import { createMemo } from '../../utils/memo';
-import { type VariableDeclaration } from '../../ast';
+import { NewASTAction } from '../../ast/types';
+import { DisposeASTAction } from '../../ast/types';
+import { ReSortVariableDeclarationsAction } from '../../ast/declaration/variable-declaration';
+import { ASTKind, type VariableDeclaration } from '../../ast';
 
 /**
  * 作用域输出
  */
 export class ScopeOutputData {
-  protected variableTable: VariableTable;
+  protected variableTable: IVariableTable;
 
   protected memo = createMemo();
 
@@ -16,7 +20,7 @@ export class ScopeOutputData {
     return this.scope.variableEngine;
   }
 
-  get globalVariableTable(): VariableTable {
+  get globalVariableTable(): IVariableTable {
     return this.scope.variableEngine.globalVariableTable;
   }
 
@@ -31,11 +35,11 @@ export class ScopeOutputData {
   protected _hasChanges = false;
 
   constructor(public readonly scope: Scope) {
-    // 基于 globalVariableTable 设置子变量表
+    // setup scope variable table based on globalVariableTable
     this.variableTable = new VariableTable(scope.variableEngine.globalVariableTable);
 
     this.scope.toDispose.pushAll([
-      // AST 根节点更新时,检查在这次 AST 变化期间节点是否有变化
+      // When root AST node is updated, check if there are any changes during this AST change
       this.scope.ast.subscribe(() => {
         if (this._hasChanges) {
           this.memo.clear();
@@ -44,21 +48,34 @@ export class ScopeOutputData {
           this._hasChanges = false;
         }
       }),
+      this.scope.event.on<DisposeASTAction>('DisposeAST', (_action) => {
+        if (_action.ast?.kind === ASTKind.VariableDeclaration) {
+          this.removeVariableFromTable(_action.ast.key);
+        }
+      }),
+      this.scope.event.on<NewASTAction>('NewAST', (_action) => {
+        if (_action.ast?.kind === ASTKind.VariableDeclaration) {
+          this.addVariableToTable(_action.ast as VariableDeclaration);
+        }
+      }),
+      this.scope.event.on<ReSortVariableDeclarationsAction>('ReSortVariableDeclarations', () => {
+        this._hasChanges = true;
+      }),
       this.variableTable,
     ]);
   }
 
   /**
-   * 作用域输出变量
+   * Scope Output Variable Declarations
    */
   get variables(): VariableDeclaration[] {
     return this.memo('variables', () =>
-      this.variableTable.variables.sort((a, b) => a.order - b.order),
+      this.variableTable.variables.sort((a, b) => a.order - b.order)
     );
   }
 
   /**
-   * 输出的变量 keys
+   * Output Variable Keys
    */
   get variableKeys(): string[] {
     return this.memo('variableKeys', () => this.variableTable.variableKeys);
@@ -69,17 +86,12 @@ export class ScopeOutputData {
       throw Error('VariableDeclaration must be a ast node in scope');
     }
 
-    this.variableTable.addVariableToTable(variable);
-    this._hasChanges = true;
-  }
-
-  // 标记为发生了变化,用于变量排序变化的场景
-  setHasChanges() {
+    (this.variableTable as VariableTable).addVariableToTable(variable);
     this._hasChanges = true;
   }
 
   removeVariableFromTable(key: string) {
-    this.variableTable.removeVariableFromTable(key);
+    (this.variableTable as VariableTable).removeVariableFromTable(key);
     this._hasChanges = true;
   }
 
@@ -87,8 +99,10 @@ export class ScopeOutputData {
     return this.variableTable.getVariableByKey(key);
   }
 
-  // 通知覆盖作用域更新可用变量
+  /**
+   *
+   */
   notifyCoversChange(): void {
-    this.scope.coverScopes.forEach(scope => scope.available.refresh());
+    this.scope.coverScopes.forEach((scope) => scope.available.refresh());
   }
 }

+ 1 - 1
packages/variable-engine/variable-core/src/scope/index.ts

@@ -1,4 +1,4 @@
 export { ScopeChain } from './scope-chain';
 export { Scope } from './scope';
 export { ScopeOutputData } from './datas';
-export { VariableTable } from './variable-table';
+export { type IVariableTable } from './types';

+ 8 - 8
packages/variable-engine/variable-core/src/scope/scope.ts

@@ -9,7 +9,7 @@ export class Scope<ScopeMeta extends Record<string, any> = Record<string, any>>
   /**
    * Scope 唯一索引
    */
-  readonly id: string;
+  readonly id: string | symbol;
 
   /**
    * Scope 依赖变量引擎
@@ -49,7 +49,7 @@ export class Scope<ScopeMeta extends Record<string, any> = Record<string, any>>
 
   public toDispose: DisposableCollection = new DisposableCollection();
 
-  constructor(options: { id: string; variableEngine: VariableEngine; meta?: ScopeMeta }) {
+  constructor(options: { id: string | symbol; variableEngine: VariableEngine; meta?: ScopeMeta }) {
     this.id = options.id;
     this.meta = options.meta || ({} as any);
     this.variableEngine = options.variableEngine;
@@ -59,11 +59,11 @@ export class Scope<ScopeMeta extends Record<string, any> = Record<string, any>>
     this.ast = this.variableEngine.astRegisters.createAST(
       {
         kind: ASTKind.MapNode,
-        key: this.id,
+        key: String(this.id),
       },
       {
         scope: this,
-      },
+      }
     ) as MapNode;
 
     this.output = new ScopeOutputData(this);
@@ -83,7 +83,7 @@ export class Scope<ScopeMeta extends Record<string, any> = Record<string, any>>
     return this.memo('deps', () =>
       this.variableEngine.chain
         .getDeps(this)
-        .filter(_scope => Boolean(_scope) && !_scope?.disposed),
+        .filter((_scope) => Boolean(_scope) && !_scope?.disposed)
     );
   }
 
@@ -91,7 +91,7 @@ export class Scope<ScopeMeta extends Record<string, any> = Record<string, any>>
     return this.memo('covers', () =>
       this.variableEngine.chain
         .getCovers(this)
-        .filter(_scope => Boolean(_scope) && !_scope?.disposed),
+        .filter((_scope) => Boolean(_scope) && !_scope?.disposed)
     );
   }
 
@@ -100,8 +100,8 @@ export class Scope<ScopeMeta extends Record<string, any> = Record<string, any>>
     this.toDispose.dispose();
 
     // 删除作用域时,触发上下游作用域依赖覆盖更新
-    this.coverScopes.forEach(_scope => _scope.refreshDeps());
-    this.depScopes.forEach(_scope => _scope.refreshCovers());
+    this.coverScopes.forEach((_scope) => _scope.refreshDeps());
+    this.depScopes.forEach((_scope) => _scope.refreshCovers());
   }
 
   onDispose = this.toDispose.onDispose;

+ 20 - 2
packages/variable-engine/variable-core/src/scope/types.ts

@@ -1,12 +1,30 @@
-import { type Scope } from './scope';
+import { Event, Disposable } from '@flowgram.ai/utils';
 
+import { BaseVariableField, VariableDeclaration } from '../ast';
+import { type Scope } from './scope';
 // 获取所有作用域的参数
 export interface GetAllScopeParams {
   // 是否排序
   sort?: boolean;
 }
-
 export interface ScopeChangeAction {
   type: 'add' | 'delete' | 'update' | 'available';
   scope: Scope;
 }
+
+export interface IVariableTable extends Disposable {
+  parentTable?: IVariableTable; // 父变量表,会包含所有子表的变量
+  onDataChange: Event<void>;
+  version: number;
+  variables: VariableDeclaration[];
+  variableKeys: string[];
+  fireChange(): void;
+  getByKeyPath(keyPath: string[]): BaseVariableField | undefined;
+  getVariableByKey(key: string): VariableDeclaration | undefined;
+  // 方法不对外透出,仅内部使用
+  // addVariableToTable(variable: VariableDeclaration): void;
+  // removeVariableFromTable(key: string): void;
+  dispose(): void;
+  onAnyVariableChange(observer: (changedVariable: VariableDeclaration) => void): Disposable;
+  onAnyChange(observer: () => void): Disposable;
+}

+ 16 - 14
packages/variable-engine/variable-core/src/scope/variable-table.ts

@@ -1,12 +1,12 @@
 import { Observable, Subject, merge, share, skip, switchMap } from 'rxjs';
-import { Disposable, Emitter } from '@flowgram.ai/utils';
-import { DisposableCollection } from '@flowgram.ai/utils';
+import { DisposableCollection, Emitter } from '@flowgram.ai/utils';
 
 import { subsToDisposable } from '../utils/toDisposable';
 import { BaseVariableField } from '../ast/declaration/base-variable-field';
 import { VariableDeclaration } from '../ast';
+import { IVariableTable } from './types';
 
-export class VariableTable implements Disposable {
+export class VariableTable implements IVariableTable {
   protected table: Map<string, VariableDeclaration> = new Map();
 
   protected onDataChangeEmitter = new Emitter<void>();
@@ -15,17 +15,17 @@ export class VariableTable implements Disposable {
 
   // 监听变量列表中的单个变量变化
   protected anyVariableChange$: Observable<VariableDeclaration> = this.variables$.pipe(
-    switchMap(_variables =>
+    switchMap((_variables) =>
       merge(
-        ..._variables.map(_v =>
+        ..._variables.map((_v) =>
           _v.value$.pipe<any>(
             // 跳过 BehaviorSubject 第一个
-            skip(1),
-          ),
-        ),
-      ),
+            skip(1)
+          )
+        )
+      )
     ),
-    share(),
+    share()
   );
 
   /**
@@ -62,7 +62,7 @@ export class VariableTable implements Disposable {
   }
 
   constructor(
-    public parentTable?: VariableTable, // 父变量表,会包含所有子表的变量
+    public parentTable?: IVariableTable // 父变量表,会包含所有子表的变量
   ) {}
 
   get variables(): VariableDeclaration[] {
@@ -106,7 +106,7 @@ export class VariableTable implements Disposable {
   addVariableToTable(variable: VariableDeclaration) {
     this.table.set(variable.key, variable);
     if (this.parentTable) {
-      this.parentTable.addVariableToTable(variable);
+      (this.parentTable as VariableTable).addVariableToTable(variable);
     }
     this.variables$.next(this.variables);
   }
@@ -118,13 +118,15 @@ export class VariableTable implements Disposable {
   removeVariableFromTable(key: string) {
     this.table.delete(key);
     if (this.parentTable) {
-      this.parentTable.removeVariableFromTable(key);
+      (this.parentTable as VariableTable).removeVariableFromTable(key);
     }
     this.variables$.next(this.variables);
   }
 
   dispose(): void {
-    this.variableKeys.forEach(_key => this.parentTable?.removeVariableFromTable(_key));
+    this.variableKeys.forEach((_key) =>
+      (this.parentTable as VariableTable)?.removeVariableFromTable(_key)
+    );
     this.onDataChangeEmitter.dispose();
   }
 }

+ 13 - 12
packages/variable-engine/variable-core/src/variable-engine.ts

@@ -5,8 +5,9 @@ import { Emitter } from '@flowgram.ai/utils';
 
 import { subsToDisposable } from './utils/toDisposable';
 import { createMemo } from './utils/memo';
+import { VariableTable } from './scope/variable-table';
 import { ScopeChangeAction } from './scope/types';
-import { Scope, ScopeChain, VariableTable } from './scope';
+import { Scope, ScopeChain, type IVariableTable } from './scope';
 import { ContainerProvider } from './providers';
 import { ASTRegisters, type GlobalEventActionType } from './ast';
 
@@ -16,13 +17,13 @@ export class VariableEngine implements Disposable {
 
   protected memo = createMemo();
 
-  protected scopeMap = new Map<string, Scope>();
+  protected scopeMap = new Map<string | symbol, Scope>();
 
   globalEvent$: Subject<GlobalEventActionType> = new Subject<GlobalEventActionType>();
 
   protected onScopeChangeEmitter = new Emitter<ScopeChangeAction>();
 
-  public globalVariableTable = new VariableTable();
+  public globalVariableTable: IVariableTable = new VariableTable();
 
   public onScopeChange = this.onScopeChangeEmitter.event;
 
@@ -35,13 +36,13 @@ export class VariableEngine implements Disposable {
 
   constructor(
     @inject(ScopeChain) public readonly chain: ScopeChain, // 作用域依赖关系偏序集
-    @inject(ASTRegisters) public readonly astRegisters: ASTRegisters, // AST 节点注册管理器
+    @inject(ASTRegisters) public readonly astRegisters: ASTRegisters // AST 节点注册管理器
   ) {
     this.toDispose.pushAll([
       chain,
       Disposable.create(() => {
         // 清空所有作用域
-        this.getAllScopes().forEach(scope => scope.dispose());
+        this.getAllScopes().forEach((scope) => scope.dispose());
         this.globalVariableTable.dispose();
       }),
     ]);
@@ -53,17 +54,17 @@ export class VariableEngine implements Disposable {
   }
 
   // 根据 scopeId 找到作用域
-  getScopeById(scopeId: string): Scope | undefined {
+  getScopeById(scopeId: string | symbol): Scope | undefined {
     return this.scopeMap.get(scopeId);
   }
 
   // 移除作用域
-  removeScopeById(scopeId: string): void {
+  removeScopeById(scopeId: string | symbol): void {
     this.getScopeById(scopeId)?.dispose();
   }
 
   // 获取 Scope,如果 Scope 存在且类型相同,则会直接使用
-  createScope(id: string, meta?: Record<string, any>): Scope {
+  createScope(id: string | symbol, meta?: Record<string, any>): Scope {
     let scope = this.getScopeById(id);
 
     if (!scope) {
@@ -102,7 +103,7 @@ export class VariableEngine implements Disposable {
     if (sort) {
       const sortScopes = this.chain.sortAll();
       const remainScopes = new Set(allScopes);
-      sortScopes.forEach(_scope => remainScopes.delete(_scope));
+      sortScopes.forEach((_scope) => remainScopes.delete(_scope));
 
       return [...sortScopes, ...Array.from(remainScopes)];
     }
@@ -117,14 +118,14 @@ export class VariableEngine implements Disposable {
 
   onGlobalEvent<ActionType extends GlobalEventActionType = GlobalEventActionType>(
     type: ActionType['type'],
-    observer: (action: ActionType) => void,
+    observer: (action: ActionType) => void
   ): Disposable {
     return subsToDisposable(
-      this.globalEvent$.subscribe(_action => {
+      this.globalEvent$.subscribe((_action) => {
         if (_action.type === type) {
           observer(_action as ActionType);
         }
-      }),
+      })
     );
   }
 }

+ 3 - 0
packages/variable-engine/variable-layout/__mocks__/fixed-layout-specs.ts

@@ -4,6 +4,9 @@ export const fixLayout1: FlowDocumentJSON = {
   nodes: [
     {
       type: 'start',
+      meta: {
+        isStart: true,
+      },
       id: 'start',
     },
     {

+ 4 - 4
packages/variable-engine/variable-layout/__mocks__/run-fixed-layout-test.ts

@@ -22,7 +22,7 @@ export const runFixedLayoutTest = (testName:string, spec: FlowDocumentJSON, conf
         scope.ast.set('/', {
           kind: ASTKind.VariableDeclaration,
           type: ASTKind.String,
-          key: scope.id,
+          key: String(scope.id),
         });
       }
     });
@@ -49,7 +49,7 @@ export const runFixedLayoutTest = (testName:string, spec: FlowDocumentJSON, conf
     const printAllNodeAvailableMapping = (_scopeType: 'public' | 'private' = 'public') =>
       traverseVariableDatas().reduce((acm, _data) => {
         const scope = _data[_scopeType]!;
-        acm.set(scope.id, scope.available.variableKeys);
+        acm.set(String(scope.id), scope.available.variableKeys);
 
         return acm;
       }, new Map<string, string[]>());
@@ -59,8 +59,8 @@ export const runFixedLayoutTest = (testName:string, spec: FlowDocumentJSON, conf
       traverseVariableDatas().reduce((acm, _data) => {
         const scope = _data[_scopeType]!;
         acm.set(
-          scope.id,
-          scope.coverScopes.map(_scope => _scope.id),
+          String(scope.id),
+          scope.coverScopes.map(_scope => String(_scope.id)),
         );
 
         return acm;

+ 4 - 4
packages/variable-engine/variable-layout/__mocks__/run-free-layout-test.ts

@@ -22,7 +22,7 @@ export const runFreeLayoutTest = (testName: string, spec: WorkflowJSON, config?:
         scope.ast.set('/', {
           kind: ASTKind.VariableDeclaration,
           type: ASTKind.String,
-          key: scope.id,
+          key: String(scope.id),
         });
       }
     });
@@ -49,7 +49,7 @@ export const runFreeLayoutTest = (testName: string, spec: WorkflowJSON, config?:
     const printAllNodeAvailableMapping = (_scopeType: 'public' | 'private' = 'public') =>
       traverseVariableDatas().reduce((acm, _data) => {
         const scope = _data[_scopeType]!;
-        acm.set(scope.id, scope.available.variableKeys);
+        acm.set(String(scope.id), scope.available.variableKeys);
 
         return acm;
       }, new Map<string, string[]>());
@@ -59,8 +59,8 @@ export const runFreeLayoutTest = (testName: string, spec: WorkflowJSON, config?:
       traverseVariableDatas().reduce((acm, _data) => {
         const scope = _data[_scopeType]!;
         acm.set(
-          scope.id,
-          scope.coverScopes.map(_scope => _scope.id),
+          String(scope.id),
+          scope.coverScopes.map(_scope => String(_scope.id)),
         );
 
         return acm;

+ 5 - 5
packages/variable-engine/variable-layout/__tests__/__snapshots__/variable-fix-layout-group.test.ts.snap

@@ -120,15 +120,15 @@ Map {
 
 exports[`Variable Fix Layout Group > test sort 1`] = `
 [
-  "globalScope",
+  "start_0_private",
   "start_0",
-  "$group_test$",
   "node_0",
-  "node_1",
-  "end_0",
-  "start_0_private",
   "node_0_private",
+  "node_1",
   "node_1_private",
+  "end_0",
   "end_0_private",
+  "globalScope",
+  "$group_test$",
 ]
 `;

+ 28 - 28
packages/variable-engine/variable-layout/__tests__/__snapshots__/variable-fix-layout-no-config.test.ts.snap

@@ -681,50 +681,50 @@ Map {
 
 exports[`Variable Fix Layout Without Config > test sort 1`] = `
 [
-  "globalScope",
+  "start_private",
   "start",
   "getRecords_07e97c55832",
-  "forEach_260a8f85ff2",
-  "createRecord_8f85ff2c11d",
-  "exclusiveSplit_ff2c11d0fb4",
-  "$blockIcon$exclusiveSplit_ff2c11d0fb4",
-  "$inlineBlocks$exclusiveSplit_ff2c11d0fb4",
-  "branch_f2c11d0fb42",
-  "$blockOrderIcon$branch_f2c11d0fb42",
-  "branch_2c11d0fb42c",
-  "$blockOrderIcon$branch_2c11d0fb42c",
-  "exclusiveSplit_88dbf2c60ae",
-  "$blockIcon$exclusiveSplit_88dbf2c60ae",
-  "$inlineBlocks$exclusiveSplit_88dbf2c60ae",
-  "branch_8dbf2c60aee",
-  "$blockOrderIcon$branch_8dbf2c60aee",
-  "exclusiveSplit_a59afaadc9a",
-  "$blockIcon$exclusiveSplit_a59afaadc9a",
-  "$inlineBlocks$exclusiveSplit_a59afaadc9a",
-  "branch_59afaadc9ac",
-  "$blockOrderIcon$branch_59afaadc9ac",
-  "branch_9afaadc9acd",
-  "$blockOrderIcon$branch_9afaadc9acd",
-  "deleteRecords_c32807e97c5",
-  "branch_dbf2c60aee4",
-  "$blockOrderIcon$branch_dbf2c60aee4",
-  "updateRecords_7ed2a172c32",
-  "end",
-  "start_private",
   "getRecords_07e97c55832_private",
+  "forEach_260a8f85ff2",
   "forEach_260a8f85ff2_private",
+  "createRecord_8f85ff2c11d",
   "createRecord_8f85ff2c11d_private",
+  "exclusiveSplit_ff2c11d0fb4",
   "exclusiveSplit_ff2c11d0fb4_private",
+  "branch_f2c11d0fb42",
   "branch_f2c11d0fb42_private",
+  "branch_2c11d0fb42c",
   "branch_2c11d0fb42c_private",
+  "exclusiveSplit_88dbf2c60ae",
   "exclusiveSplit_88dbf2c60ae_private",
+  "branch_8dbf2c60aee",
   "branch_8dbf2c60aee_private",
+  "exclusiveSplit_a59afaadc9a",
   "exclusiveSplit_a59afaadc9a_private",
+  "branch_59afaadc9ac",
   "branch_59afaadc9ac_private",
+  "branch_9afaadc9acd",
   "branch_9afaadc9acd_private",
+  "deleteRecords_c32807e97c5",
   "deleteRecords_c32807e97c5_private",
+  "branch_dbf2c60aee4",
   "branch_dbf2c60aee4_private",
+  "updateRecords_7ed2a172c32",
   "updateRecords_7ed2a172c32_private",
+  "end",
   "end_private",
+  "globalScope",
+  "$blockIcon$exclusiveSplit_ff2c11d0fb4",
+  "$inlineBlocks$exclusiveSplit_ff2c11d0fb4",
+  "$blockOrderIcon$branch_f2c11d0fb42",
+  "$blockOrderIcon$branch_2c11d0fb42c",
+  "$blockIcon$exclusiveSplit_88dbf2c60ae",
+  "$inlineBlocks$exclusiveSplit_88dbf2c60ae",
+  "$blockOrderIcon$branch_8dbf2c60aee",
+  "$blockIcon$exclusiveSplit_a59afaadc9a",
+  "$inlineBlocks$exclusiveSplit_a59afaadc9a",
+  "$blockOrderIcon$branch_59afaadc9ac",
+  "$blockOrderIcon$branch_9afaadc9acd",
+  "$blockOrderIcon$branch_dbf2c60aee4",
 ]
 `;

+ 1 - 1
packages/variable-engine/variable-layout/__tests__/__snapshots__/variable-fix-layout.test.ts.snap

@@ -799,6 +799,7 @@ Map {
 
 exports[`Variable Fix Layout > test sort 1`] = `
 [
+  "start_private",
   "start",
   "getRecords_07e97c55832",
   "getRecords_07e97c55832_private",
@@ -843,6 +844,5 @@ exports[`Variable Fix Layout > test sort 1`] = `
   "$blockOrderIcon$branch_59afaadc9ac",
   "$blockOrderIcon$branch_9afaadc9acd",
   "$blockOrderIcon$branch_dbf2c60aee4",
-  "start_private",
 ]
 `;

+ 2 - 3
packages/variable-engine/variable-layout/__tests__/variable-fix-layout-filter-start-end.test.ts

@@ -7,7 +7,6 @@ const filterStart = (_scope: FlowNodeScope) => !['start'].includes(_scope.meta?.
 const filterEnd = (_scope: FlowNodeScope) => !['end'].includes(_scope.meta?.node?.id || '');
 
 runFixedLayoutTest('Variable Fix Layout Filter Start End', fixLayout1, {
-  startNodeId: 'start',
-  transformCovers: scopes => scopes.filter(filterEnd),
-  transformDeps: scopes => scopes.filter(filterStart),
+  transformCovers: (scopes) => scopes.filter(filterEnd),
+  transformDeps: (scopes) => scopes.filter(filterStart),
 });

+ 4 - 3
packages/variable-engine/variable-layout/__tests__/variable-fix-layout-group.test.ts

@@ -7,6 +7,9 @@ runFixedLayoutTest(
       {
         id: 'start_0',
         type: 'start',
+        meta: {
+          isStart: true,
+        },
         blocks: [],
       },
       {
@@ -32,7 +35,5 @@ runFixedLayoutTest(
       },
     ],
   },
-  {
-    startNodeId: 'start',
-  }
+  {}
 );

+ 1 - 2
packages/variable-engine/variable-layout/__tests__/variable-fix-layout.test.ts

@@ -2,8 +2,7 @@ import { runFixedLayoutTest } from '../__mocks__/run-fixed-layout-test';
 import { fixLayout1 } from '../__mocks__/fixed-layout-specs';
 
 runFixedLayoutTest('Variable Fix Layout', fixLayout1, {
-  startNodeId: 'start',
-  isNodeChildrenPrivate: node =>
+  isNodeChildrenPrivate: (node) =>
     // 只有循环是 private
     node.flowNodeType === 'loop',
 });

+ 18 - 13
packages/variable-engine/variable-layout/src/fixed-layout-scope-chain.ts

@@ -19,7 +19,7 @@ export class FixedLayoutScopeChain extends ScopeChain {
     protected flowDocument: FlowDocument,
     @optional()
     @inject(VariableLayoutConfig)
-    protected configs?: VariableLayoutConfig,
+    protected configs?: VariableLayoutConfig
   ) {
     super();
 
@@ -31,7 +31,7 @@ export class FixedLayoutScopeChain extends ScopeChain {
       // REFRACTOR: onTreeChange 触发时机精细化
       flowDocument.originTree.onTreeChange(() => {
         this.refreshAllChange();
-      }),
+      })
     );
   }
 
@@ -71,7 +71,7 @@ export class FixedLayoutScopeChain extends ScopeChain {
         deps.unshift(
           ...this.getAllSortedChildScope(curr, {
             ignoreNodeChildrenPrivate: true,
-          }),
+          })
         );
       }
 
@@ -135,7 +135,7 @@ export class FixedLayoutScopeChain extends ScopeChain {
       covers.push(
         ...this.getAllSortedChildScope(node, {
           addNodePrivateScope: true,
-        }),
+        })
       );
       return this.transformCovers(covers, { scope });
     }
@@ -152,7 +152,7 @@ export class FixedLayoutScopeChain extends ScopeChain {
           covers.push(
             ...this.getAllSortedChildScope(curr, {
               addNodePrivateScope: true,
-            }),
+            })
           );
         } else if (currData) {
           covers.push(...currData.allScopes);
@@ -220,15 +220,20 @@ export class FixedLayoutScopeChain extends ScopeChain {
 
   // 排序所有作用域
   sortAll(): Scope[] {
-    const { startNodeId } = this.configs || {};
-
-    const startNode = startNodeId ? this.flowDocument?.getNode(startNodeId) : undefined;
+    const startNode = this.flowDocument.getAllNodes().find((_node) => _node.isStart);
     if (!startNode) {
       return [];
     }
+
     const startVariableData = startNode.getData(FlowNodeVariableData);
+    const startPublicScope = startVariableData.public;
+    const deps = this.getDeps(startPublicScope);
+
+    const covers = this.getCovers(startPublicScope).filter(
+      (_scope) => !deps.includes(_scope) && _scope !== startPublicScope
+    );
 
-    return [startVariableData.public, ...this.getCovers(startVariableData.public)];
+    return [...deps, startPublicScope, ...covers];
   }
 
   // 获取变量 Data 数据
@@ -265,7 +270,7 @@ export class FixedLayoutScopeChain extends ScopeChain {
     {
       ignoreNodeChildrenPrivate,
       addNodePrivateScope,
-    }: { ignoreNodeChildrenPrivate?: boolean; addNodePrivateScope?: boolean } = {},
+    }: { ignoreNodeChildrenPrivate?: boolean; addNodePrivateScope?: boolean } = {}
   ): FlowNodeScope[] {
     const scopes: FlowNodeScope[] = [];
 
@@ -288,10 +293,10 @@ export class FixedLayoutScopeChain extends ScopeChain {
     const children = this.tree?.getChildren(node) || [];
     scopes.push(
       ...children
-        .map(child =>
-          this.getAllSortedChildScope(child, { ignoreNodeChildrenPrivate, addNodePrivateScope }),
+        .map((child) =>
+          this.getAllSortedChildScope(child, { ignoreNodeChildrenPrivate, addNodePrivateScope })
         )
-        .flat(),
+        .flat()
     );
 
     return scopes;

+ 0 - 4
packages/variable-engine/variable-layout/src/variable-layout-config.ts

@@ -11,10 +11,6 @@ interface TransformerContext {
 }
 
 export interface VariableLayoutConfig {
-  /**
-   * 开始节点的节点 Id
-   */
-  startNodeId?: string;
   /**
    * 节点的子节点输出变量,不能被后续节点所访问,用于固定布局场景
    * @param node