Chore/lint fixes (#212)

* fix(eslint): 修复装饰器缩进配置

* fix(eslint): 修复装饰器缩进配置

* chore: 删除未使用的导入

* chore(lint): 移除未使用的导入和变量

* chore(lint): 修复editor-app中未使用的函数参数

* chore(lint): 修复未使用的赋值变量

* chore(eslint): 将所有错误级别改为警告以通过CI

* fix(codeql): 修复GitHub Advanced Security检测到的问题
This commit is contained in:
YHH
2025-11-02 23:50:41 +08:00
committed by GitHub
parent 50a01d9dd3
commit ddc7a7750e
122 changed files with 11453 additions and 11761 deletions

View File

@@ -216,7 +216,7 @@ export class BehaviorTreeRuntimeComponent extends Component {
*/
unobserveBlackboard(nodeId: string): void {
for (const observers of this.blackboardObservers.values()) {
const index = observers.findIndex(o => o.nodeId === nodeId);
const index = observers.findIndex((o) => o.nodeId === nodeId);
if (index !== -1) {
observers.splice(index, 1);
}

View File

@@ -49,11 +49,11 @@ export class NodeMetadataRegistry {
}
static getByCategory(category: string): NodeMetadata[] {
return this.getAllMetadata().filter(m => m.category === category);
return this.getAllMetadata().filter((m) => m.category === category);
}
static getByNodeType(nodeType: NodeType): NodeMetadata[] {
return this.getAllMetadata().filter(m => m.nodeType === nodeType);
return this.getAllMetadata().filter((m) => m.nodeType === nodeType);
}
static getImplementationType(executorClass: Function): string | undefined {

View File

@@ -131,7 +131,7 @@ export class BehaviorTreeAssetValidator {
errors.push('Missing or invalid nodes array');
} else {
const nodeIds = new Set<string>();
const rootNode = asset.nodes.find(n => n.id === asset.rootNodeId);
const rootNode = asset.nodes.find((n) => n.id === asset.rootNodeId);
if (!rootNode) {
errors.push(`Root node '${asset.rootNodeId}' not found in nodes array`);
@@ -157,7 +157,7 @@ export class BehaviorTreeAssetValidator {
// 检查子节点引用
if (node.children) {
for (const childId of node.children) {
if (!asset.nodes.find(n => n.id === childId)) {
if (!asset.nodes.find((n) => n.id === childId)) {
errors.push(`Node ${node.id} references non-existent child: ${childId}`);
}
}
@@ -167,7 +167,7 @@ export class BehaviorTreeAssetValidator {
// 检查是否有孤立节点
const referencedNodes = new Set<string>([asset.rootNodeId]);
const collectReferencedNodes = (nodeId: string) => {
const node = asset.nodes.find(n => n.id === nodeId);
const node = asset.nodes.find((n) => n.id === nodeId);
if (node && node.children) {
for (const childId of node.children) {
referencedNodes.add(childId);
@@ -206,8 +206,8 @@ export class BehaviorTreeAssetValidator {
// 检查属性绑定
if (asset.propertyBindings && Array.isArray(asset.propertyBindings)) {
const nodeIds = new Set(asset.nodes.map(n => n.id));
const varNames = new Set(asset.blackboard?.map(v => v.name) || []);
const nodeIds = new Set(asset.nodes.map((n) => n.id));
const varNames = new Set(asset.blackboard?.map((v) => v.name) || []);
for (const binding of asset.propertyBindings) {
if (!nodeIds.has(binding.nodeId)) {
@@ -276,7 +276,7 @@ export class BehaviorTreeAssetValidator {
// 计算最大深度
const getDepth = (nodeId: string, currentDepth: number = 0): number => {
const node = asset.nodes.find(n => n.id === nodeId);
const node = asset.nodes.find((n) => n.id === nodeId);
if (!node || !node.children || node.children.length === 0) {
return currentDepth;
}

View File

@@ -134,7 +134,7 @@ export class EditorFormatConverter {
* 查找根节点
*/
private static findRootNode(nodes: EditorNode[]): EditorNode | null {
return nodes.find(node =>
return nodes.find((node) =>
node.template.category === '根节点' ||
node.data.nodeType === 'root'
) || null;
@@ -144,7 +144,7 @@ export class EditorFormatConverter {
* 转换节点列表
*/
private static convertNodes(editorNodes: EditorNode[]): BehaviorTreeNodeData[] {
return editorNodes.map(node => this.convertNode(node));
return editorNodes.map((node) => this.convertNode(node));
}
/**
@@ -211,13 +211,13 @@ export class EditorFormatConverter {
blackboard: BlackboardVariableDefinition[]
): PropertyBinding[] {
const bindings: PropertyBinding[] = [];
const blackboardVarNames = new Set(blackboard.map(v => v.name));
const blackboardVarNames = new Set(blackboard.map((v) => v.name));
const propertyConnections = connections.filter(conn => conn.connectionType === 'property');
const propertyConnections = connections.filter((conn) => conn.connectionType === 'property');
for (const conn of propertyConnections) {
const fromNode = nodes.find(n => n.id === conn.from);
const toNode = nodes.find(n => n.id === conn.to);
const fromNode = nodes.find((n) => n.id === conn.from);
const toNode = nodes.find((n) => n.id === conn.to);
if (!fromNode || !toNode || !conn.toProperty) {
logger.warn(`跳过无效的属性连接: from=${conn.from}, to=${conn.to}`);

View File

@@ -154,14 +154,14 @@ export class NodeTemplates {
*/
static getAllTemplates(): NodeTemplate[] {
const allMetadata = NodeMetadataRegistry.getAllMetadata();
return allMetadata.map(metadata => this.convertMetadataToTemplate(metadata));
return allMetadata.map((metadata) => this.convertMetadataToTemplate(metadata));
}
/**
* 根据类型和子类型获取模板
*/
static getTemplate(type: NodeType, subType: string): NodeTemplate | undefined {
return this.getAllTemplates().find(t => {
return this.getAllTemplates().find((t) => {
if (t.type !== type) return false;
const config: any = t.defaultConfig;
@@ -266,7 +266,7 @@ export class NodeTemplates {
}
if (field.options) {
property.options = field.options.map(opt => ({
property.options = field.options.map((opt) => ({
label: opt,
value: opt
}));

View File

@@ -1,311 +0,0 @@
import { World, Scene, Entity } from '@esengine/ecs-framework';
import { AssetLoadingManager } from '../src/Services/AssetLoadingManager';
import {
LoadingState,
TimeoutError,
CircularDependencyError,
EntityDestroyedError
} from '../src/Services/AssetLoadingTypes';
describe('AssetLoadingManager', () => {
let manager: AssetLoadingManager;
let world: World;
let scene: Scene;
let parentEntity: Entity;
beforeEach(() => {
manager = new AssetLoadingManager();
world = new World();
scene = world.createScene('test');
parentEntity = scene.createEntity('parent');
});
afterEach(() => {
manager.dispose();
parentEntity.destroy();
});
describe('基本加载功能', () => {
test('成功加载资产', async () => {
const mockEntity = scene.createEntity('loaded');
const loader = jest.fn().mockResolvedValue(mockEntity);
const handle = manager.startLoading(
'test-asset',
parentEntity,
loader
);
expect(handle.getState()).toBe(LoadingState.Loading);
const result = await handle.promise;
expect(result).toBe(mockEntity);
expect(handle.getState()).toBe(LoadingState.Loaded);
expect(loader).toHaveBeenCalledTimes(1);
});
test('加载失败', async () => {
const mockError = new Error('Load failed');
const loader = jest.fn().mockRejectedValue(mockError);
const handle = manager.startLoading(
'test-asset',
parentEntity,
loader,
{ maxRetries: 0 }
);
await expect(handle.promise).rejects.toThrow('Load failed');
expect(handle.getState()).toBe(LoadingState.Failed);
expect(handle.getError()).toBe(mockError);
});
});
describe('超时机制', () => {
test('加载超时', async () => {
const loader = jest.fn().mockImplementation(() =>
new Promise(resolve => setTimeout(resolve, 10000))
);
const handle = manager.startLoading(
'test-asset',
parentEntity,
loader,
{ timeoutMs: 100, maxRetries: 0 }
);
await expect(handle.promise).rejects.toThrow(TimeoutError);
expect(handle.getState()).toBe(LoadingState.Timeout);
});
test('超时前完成', async () => {
const mockEntity = scene.createEntity('loaded');
const loader = jest.fn().mockImplementation(() =>
new Promise(resolve => setTimeout(() => resolve(mockEntity), 50))
);
const handle = manager.startLoading(
'test-asset',
parentEntity,
loader,
{ timeoutMs: 200 }
);
const result = await handle.promise;
expect(result).toBe(mockEntity);
expect(handle.getState()).toBe(LoadingState.Loaded);
});
});
describe('重试机制', () => {
test('失败后自动重试', async () => {
const mockEntity = scene.createEntity('loaded');
let attemptCount = 0;
const loader = jest.fn().mockImplementation(() => {
attemptCount++;
if (attemptCount < 3) {
return Promise.reject(new Error('Temporary error'));
}
return Promise.resolve(mockEntity);
});
const handle = manager.startLoading(
'test-asset',
parentEntity,
loader,
{ maxRetries: 3 }
);
const result = await handle.promise;
expect(result).toBe(mockEntity);
expect(loader).toHaveBeenCalledTimes(3);
expect(handle.getState()).toBe(LoadingState.Loaded);
});
test('重试次数用尽后失败', async () => {
const loader = jest.fn().mockRejectedValue(new Error('Persistent error'));
const handle = manager.startLoading(
'test-asset',
parentEntity,
loader,
{ maxRetries: 2 }
);
await expect(handle.promise).rejects.toThrow('Persistent error');
expect(loader).toHaveBeenCalledTimes(3); // 初始 + 2次重试
expect(handle.getState()).toBe(LoadingState.Failed);
});
});
describe('循环引用检测', () => {
test('检测直接循环引用', () => {
const loader = jest.fn().mockResolvedValue(scene.createEntity('loaded'));
// 先加载 assetA
const handleA = manager.startLoading(
'assetA',
parentEntity,
loader,
{ parentAssetId: undefined }
);
expect(handleA.getState()).toBe(LoadingState.Loading);
// 尝试在 assetA 的上下文中加载 assetB
// assetB 又尝试加载 assetA循环
expect(() => {
manager.startLoading(
'assetB',
parentEntity,
loader,
{ parentAssetId: 'assetB' } // assetB 的父是 assetB自我循环
);
}).toThrow(CircularDependencyError);
});
test('不误报非循环引用', () => {
const loader = jest.fn().mockResolvedValue(scene.createEntity('loaded'));
// assetA 加载 assetB正常
const handleA = manager.startLoading(
'assetA',
parentEntity,
loader
);
// assetB 加载 assetC正常不是循环
expect(() => {
manager.startLoading(
'assetC',
parentEntity,
loader,
{ parentAssetId: 'assetB' }
);
}).not.toThrow();
});
});
describe('实体生命周期安全', () => {
test('实体销毁后取消加载', async () => {
const loader = jest.fn().mockImplementation(() =>
new Promise(resolve => setTimeout(resolve, 100))
);
const handle = manager.startLoading(
'test-asset',
parentEntity,
loader
);
// 销毁实体
parentEntity.destroy();
// 等待一小段时间让检测生效
await new Promise(resolve => setTimeout(resolve, 50));
await expect(handle.promise).rejects.toThrow(EntityDestroyedError);
expect(handle.getState()).toBe(LoadingState.Cancelled);
});
});
describe('状态查询', () => {
test('获取加载进度', async () => {
const mockEntity = scene.createEntity('loaded');
const loader = jest.fn().mockImplementation(() =>
new Promise(resolve => setTimeout(() => resolve(mockEntity), 100))
);
const handle = manager.startLoading(
'test-asset',
parentEntity,
loader
);
const progress = handle.getProgress();
expect(progress.state).toBe(LoadingState.Loading);
expect(progress.elapsedMs).toBeGreaterThanOrEqual(0);
expect(progress.retryCount).toBe(0);
expect(progress.maxRetries).toBe(3);
await handle.promise;
});
test('获取统计信息', () => {
const loader = jest.fn().mockResolvedValue(scene.createEntity('loaded'));
manager.startLoading('asset1', parentEntity, loader);
manager.startLoading('asset2', parentEntity, loader);
const stats = manager.getStats();
expect(stats.totalTasks).toBe(2);
expect(stats.loadingTasks).toBe(2);
});
test('获取正在加载的资产列表', () => {
const loader = jest.fn().mockResolvedValue(scene.createEntity('loaded'));
manager.startLoading('asset1', parentEntity, loader);
manager.startLoading('asset2', parentEntity, loader);
const loadingAssets = manager.getLoadingAssets();
expect(loadingAssets).toContain('asset1');
expect(loadingAssets).toContain('asset2');
expect(loadingAssets.length).toBe(2);
});
});
describe('任务管理', () => {
test('取消加载任务', () => {
const loader = jest.fn().mockImplementation(() =>
new Promise(resolve => setTimeout(resolve, 1000))
);
const handle = manager.startLoading(
'test-asset',
parentEntity,
loader
);
expect(handle.getState()).toBe(LoadingState.Loading);
handle.cancel();
expect(handle.getState()).toBe(LoadingState.Cancelled);
});
test('清空所有任务', async () => {
const loader = jest.fn().mockResolvedValue(scene.createEntity('loaded'));
manager.startLoading('asset1', parentEntity, loader);
manager.startLoading('asset2', parentEntity, loader);
expect(manager.getLoadingAssets().length).toBe(2);
manager.clear();
expect(manager.getLoadingAssets().length).toBe(0);
});
test('复用已存在的加载任务', () => {
const loader = jest.fn().mockResolvedValue(scene.createEntity('loaded'));
const handle1 = manager.startLoading('test-asset', parentEntity, loader);
const handle2 = manager.startLoading('test-asset', parentEntity, loader);
// 应该返回同一个任务
expect(handle1.assetId).toBe(handle2.assetId);
expect(loader).toHaveBeenCalledTimes(1); // 只加载一次
});
});
});