refactor: reorganize package structure and decouple framework packages (#338)

* refactor: reorganize package structure and decouple framework packages

## Package Structure Reorganization
- Reorganized 55 packages into categorized subdirectories:
  - packages/framework/ - Generic framework (Laya/Cocos compatible)
  - packages/engine/ - ESEngine core modules
  - packages/rendering/ - Rendering modules (WASM dependent)
  - packages/physics/ - Physics modules
  - packages/streaming/ - World streaming
  - packages/network-ext/ - Network extensions
  - packages/editor/ - Editor framework and plugins
  - packages/rust/ - Rust WASM engine
  - packages/tools/ - Build tools and SDK

## Framework Package Decoupling
- Decoupled behavior-tree and blueprint packages from ESEngine dependencies
- Created abstracted interfaces (IBTAssetManager, IBehaviorTreeAssetContent)
- ESEngine-specific code moved to esengine/ subpath exports
- Framework packages now usable with Cocos/Laya without ESEngine

## CI Configuration
- Updated CI to only type-check and lint framework packages
- Added type-check:framework and lint:framework scripts

## Breaking Changes
- Package import paths changed due to directory reorganization
- ESEngine integrations now use subpath imports (e.g., '@esengine/behavior-tree/esengine')

* fix: update es-engine file path after directory reorganization

* docs: update README to focus on framework over engine

* ci: only build framework packages, remove Rust/WASM dependencies

* fix: remove esengine subpath from behavior-tree and blueprint builds

ESEngine integration code will only be available in full engine builds.
Framework packages are now purely engine-agnostic.

* fix: move network-protocols to framework, build both in CI

* fix: update workflow paths from packages/core to packages/framework/core

* fix: exclude esengine folder from type-check in behavior-tree and blueprint

* fix: update network tsconfig references to new paths

* fix: add test:ci:framework to only test framework packages in CI

* fix: only build core and math npm packages in CI

* fix: exclude test files from CodeQL and fix string escaping security issue
This commit is contained in:
YHH
2025-12-26 14:50:35 +08:00
committed by GitHub
parent a84ff902e4
commit 155411e743
1936 changed files with 4147 additions and 11578 deletions

View File

@@ -0,0 +1,268 @@
/**
* Service identifier type
* 服务标识类型
*/
export type ServiceIdentifier<T = unknown> = abstract new (...args: never[]) => T;
/**
* Service factory function
* 服务工厂函数
*/
export type ServiceFactory<T> = (container: ServiceContainer) => T;
/**
* Service lifecycle
* 服务生命周期
*/
export const enum EServiceLifecycle {
/** Single instance shared across all resolutions | 单例模式 */
Singleton = 'singleton',
/** New instance per resolution | 每次解析创建新实例 */
Transient = 'transient'
}
/**
* Service registration info
* 服务注册信息
*/
interface ServiceRegistration<T = unknown> {
factory: ServiceFactory<T>;
lifecycle: EServiceLifecycle;
instance?: T;
}
/**
* ServiceContainer
*
* Lightweight dependency injection container for FairyGUI.
*
* 轻量级依赖注入容器
*
* Features:
* - Singleton and transient lifecycles
* - Factory-based registration
* - Type-safe resolution
* - Circular dependency detection
*
* @example
* ```typescript
* const container = new ServiceContainer();
*
* // Register singleton
* container.registerSingleton(AudioService, () => new AudioService());
*
* // Register with dependencies
* container.registerSingleton(UIManager, (c) => new UIManager(
* c.resolve(AudioService)
* ));
*
* // Resolve
* const uiManager = container.resolve(UIManager);
* ```
*/
export class ServiceContainer {
private _registrations: Map<ServiceIdentifier, ServiceRegistration> = new Map();
private _resolving: Set<ServiceIdentifier> = new Set();
private _disposed: boolean = false;
/**
* Register a singleton service
* 注册单例服务
*/
public registerSingleton<T>(
identifier: ServiceIdentifier<T>,
factory: ServiceFactory<T>
): this {
this.checkDisposed();
this._registrations.set(identifier, {
factory,
lifecycle: EServiceLifecycle.Singleton
});
return this;
}
/**
* Register a singleton instance directly
* 直接注册单例实例
*/
public registerInstance<T>(identifier: ServiceIdentifier<T>, instance: T): this {
this.checkDisposed();
this._registrations.set(identifier, {
factory: () => instance,
lifecycle: EServiceLifecycle.Singleton,
instance
});
return this;
}
/**
* Register a transient service (new instance per resolution)
* 注册瞬时服务(每次解析创建新实例)
*/
public registerTransient<T>(
identifier: ServiceIdentifier<T>,
factory: ServiceFactory<T>
): this {
this.checkDisposed();
this._registrations.set(identifier, {
factory,
lifecycle: EServiceLifecycle.Transient
});
return this;
}
/**
* Resolve a service
* 解析服务
*/
public resolve<T>(identifier: ServiceIdentifier<T>): T {
this.checkDisposed();
const registration = this._registrations.get(identifier);
if (!registration) {
throw new Error(`Service not registered: ${identifier.name}`);
}
// Check for circular dependency
if (this._resolving.has(identifier)) {
throw new Error(`Circular dependency detected: ${identifier.name}`);
}
// Return cached singleton if available
if (registration.lifecycle === EServiceLifecycle.Singleton && registration.instance !== undefined) {
return registration.instance as T;
}
// Resolve
this._resolving.add(identifier);
try {
const instance = registration.factory(this) as T;
if (registration.lifecycle === EServiceLifecycle.Singleton) {
registration.instance = instance;
}
return instance;
} finally {
this._resolving.delete(identifier);
}
}
/**
* Try to resolve a service, returns null if not found
* 尝试解析服务,未找到时返回 null
*/
public tryResolve<T>(identifier: ServiceIdentifier<T>): T | null {
if (!this._registrations.has(identifier)) {
return null;
}
return this.resolve(identifier);
}
/**
* Check if a service is registered
* 检查服务是否已注册
*/
public isRegistered<T>(identifier: ServiceIdentifier<T>): boolean {
return this._registrations.has(identifier);
}
/**
* Unregister a service
* 取消注册服务
*/
public unregister<T>(identifier: ServiceIdentifier<T>): boolean {
const registration = this._registrations.get(identifier);
if (registration) {
// Dispose singleton if it has dispose method
if (registration.instance && typeof (registration.instance as IDisposable).dispose === 'function') {
(registration.instance as IDisposable).dispose();
}
this._registrations.delete(identifier);
return true;
}
return false;
}
/**
* Create a child container that inherits registrations
* 创建继承注册的子容器
*/
public createChild(): ServiceContainer {
const child = new ServiceContainer();
// Copy registrations (singletons are shared)
for (const [id, reg] of this._registrations) {
child._registrations.set(id, { ...reg });
}
return child;
}
/**
* Dispose the container and all singleton instances
* 销毁容器和所有单例实例
*/
public dispose(): void {
if (this._disposed) return;
for (const registration of this._registrations.values()) {
if (registration.instance && typeof (registration.instance as IDisposable).dispose === 'function') {
(registration.instance as IDisposable).dispose();
}
}
this._registrations.clear();
this._resolving.clear();
this._disposed = true;
}
private checkDisposed(): void {
if (this._disposed) {
throw new Error('ServiceContainer has been disposed');
}
}
}
/**
* Disposable interface
* 可销毁接口
*/
interface IDisposable {
dispose(): void;
}
/**
* Global service container instance
* 全局服务容器实例
*/
let _globalContainer: ServiceContainer | null = null;
/**
* Get global service container
* 获取全局服务容器
*/
export function getGlobalContainer(): ServiceContainer {
if (!_globalContainer) {
_globalContainer = new ServiceContainer();
}
return _globalContainer;
}
/**
* Set global service container
* 设置全局服务容器
*/
export function setGlobalContainer(container: ServiceContainer): void {
_globalContainer = container;
}
/**
* Inject decorator marker (for future decorator support)
* 注入装饰器标记(用于未来装饰器支持)
*/
export function Inject<T>(identifier: ServiceIdentifier<T>): PropertyDecorator {
return (_target: object, _propertyKey: string | symbol) => {
// Store metadata for future use
// This is a placeholder for decorator-based injection
void identifier;
};
}