# DEHub

DEHub is a message-driven library for JavaScript-based clients. It unifies the handling of events and data in all clients by using messages for subscription and processing. This allows all components and business processes in a page to handle business logic in a subscribable manner, and enables access to all registered components through context.

## Key Features

- Component Registration and Management
  - Supports batch component registration
  - Supports parent-child component relationships
  - Supports asynchronous component loading
  - Supports component state management and updates
  - Supports component lifecycle events
  - **Supports resource cleanup via `destroy()`**

- Data Management
  - Supports multiple data loading modes (localDB, custom, none)
  - Supports data singleton maintenance
  - Supports virtual fields
  - Supports data reset and cleanup
  - Supports data cache management
  - Supports high-performance handling of large datasets
  - **Supports safe IndexedDB operations with configurable timeouts**

- Event System
  - Supports event subscription and processing
  - Supports event pre-processing and post-processing
  - Supports event context access
  - **Supports `IEventContext` interface for type-safe event handling**

## Quick Start

### Component Registration

```typescript
// Create and register a basic component
const tag = new Tag({ id: 'cmp1' });
const comp1 = new DEComp<any>(tag);
regComponent(comp1);

// Wait for component to be ready
await comp1.waitReady();

// Update component state
await comp1.update({ a: 1, b: 'abc' });

// Clean up when done
await comp1.destroy();
```

### Component State Management

```typescript
// Listen for component state changes
when({ 
    id: 'cmp1', 
    event: EventNames.StateChanging, 
    stage: EventStage.PostOperation 
}, (context: IEventContext) => {
    const comp = context.sender as DEComp;
    // Handle state changes
});

// Update component state
await comp1.update({ a: 2 });
```

### Data Management

```typescript
// Create data tag
const IdTag = (entity: string, id: string): Tag => {
    return new Tag({ entity, id });
};

// Get data object
const user1Tag = IdTag('user.demo@org0', 'user1');
const user1Data = await fetchData(user1Tag, { 
    loadingMode: 'custom',
    default: { name: 'Default' }
});

// Wait for data to load
await user1Data.until(ObjectStatus.Ready);

// Modify data
await user1Data.set("name", "John");
await user1Data.set("age", 25);

// Submit data
await user1Data.submit();

// Clean up when done
await user1Data.destroy();
```

### Data Loading Modes

```typescript
// LocalDB mode - Load from local database
const localData = await fetchData({ id: 'localData1' }, { 
    loadingMode: 'localDB',
    default: { a: 1 }
});

// Custom mode - Custom loading logic
const customData = await fetchData({ id: 'customData1' }, { 
    loadingMode: 'custom'
});

// None mode - No automatic loading
const noneData = await fetchData({ id: 'noneData1' }, { 
    loadingMode: 'none',
    autoLoad: false
});
```

### Virtual Fields

```typescript
// Set virtual field
await data.set("virtualField", value, true);

// Virtual fields are not saved to database
expect(data.virtual.virtualField).toBeDefined();
expect(data.original.virtualField).toBeUndefined();
```

### Configuration

```typescript
// Configure DEHub options
DEHubConfig({
    FuncExeTimeout: 5000,      // Async handler timeout (ms), default: 3000
    AppLoadEventDelay: 517,    // App loaded event delay (ms), default: 517
    silent: false              // Suppress console warnings, default: false
});
```

### Resource Cleanup

```typescript
// Components
const comp = new DEComp(new Tag({ role: 'input', id: 'username' }));
// ... use component
await comp.destroy();  // Cleans up timers, sessionStorage, event handlers

// Data objects
const data = await fetchData(new Tag({ type: 'user', id: '1' }), {
    loadingMode: 'localDB'
});
// ... use data
await data.destroy();  // Cleans up IndexedDB timers, waiters, state maps
```

## Performance Optimization

- Efficient handling of large datasets
- Data cache management
- Batch component registration and state updates
- Asynchronous loading and lazy initialization
- **O(1) async handler timeout tracking** (replaced O(n²) array scanning)
- **Reference-based handler deduplication** (replaced deep comparison)
- **Debounced sessionStorage writes** (300ms throttle to reduce sync I/O)
- **Tag key limit safeguard** (warns when tag keys exceed 10 to prevent 2^n explosion)

## Important Notes

1. Data Object State Management
   - Modified data enters dirty state
   - Data updates to original state after submission
   - Virtual fields are not saved to database
   - **IndexedDB read timeout is 5000ms and will NOT delete data on timeout**

2. Component Lifecycle
   - Mounting event triggered on component registration
   - StateChanging event triggered on state changes
   - WillUnmount event triggered on component unload
   - **Always call `destroy()` when a component or data object is no longer needed to prevent memory leaks**

3. Data Loading Modes
   - localDB: Suitable for local data storage
   - custom: Suitable for custom data loading logic
   - none: Suitable for manual data loading control

4. Event Handling
   - Use `IEventContext` instead of `EventContext` for typing event handlers
   - `EventContext` is the runtime class; `IEventContext` is the interface

## Best Practices

1. Component Registration
   - Use meaningful component IDs
   - Properly utilize parent-child relationships
   - **Always call `destroy()` to release resources when unmounting**

2. Data Management
   - Choose appropriate loading modes
   - Properly use virtual fields
   - Pay attention to data state management
   - **Call `destroy()` on data objects to clean up IndexedDB connections and waiters**

3. Event Handling
   - Properly use event pre-processing and post-processing
   - Consider performance impact of event handling
   - Avoid circular dependencies in event handling
   - Use `stop()` to remove event listeners when components are destroyed

---

# DEHub (中文)

DEHub 是一个基于 JavaScript 的客户端消息驱动库，通过消息订阅的方式统一处理客户端的事件与数据。它允许页面中的所有组件和业务流程以订阅的方式处理业务逻辑，并能够通过上下文访问所有已注册组件。

## 主要特性

- 组件注册与管理
  - 支持组件的批量注册
  - 支持组件的父子关系
  - 支持组件的异步加载
  - 支持组件的状态管理和更新
  - 支持组件的生命周期事件
  - **支持通过 `destroy()` 主动释放资源**

- 数据管理
  - 支持多种数据加载模式(localDB, custom, none)
  - 支持数据单例维护
  - 支持虚拟字段(virtual fields)
  - 支持数据重置和清理
  - 支持数据缓存管理
  - 支持大量数据的高性能处理
  - **支持安全的 IndexedDB 操作与可配置超时**

- 事件系统
  - 支持事件订阅和处理
  - 支持事件预处理和后处理
  - 支持事件上下文访问
  - **支持 `IEventContext` 接口实现类型安全的事件处理**

## 快速开始

### 组件注册

```typescript
// 创建并注册一个基础组件
const tag = new Tag({ id: 'cmp1' });
const comp1 = new DEComp<any>(tag);
regComponent(comp1);

// 等待组件就绪
await comp1.waitReady();

// 更新组件状态
await comp1.update({ a: 1, b: 'abc' });

// 使用完毕后清理资源
await comp1.destroy();
```

### 组件状态管理

```typescript
// 监听组件状态变更
when({ 
    id: 'cmp1', 
    event: EventNames.StateChanging, 
    stage: EventStage.PostOperation 
}, (context: IEventContext) => {
    const comp = context.sender as DEComp;
    // 处理状态变更
});

// 更新组件状态
await comp1.update({ a: 2 });
```

### 数据管理

```typescript
// 创建数据标签
const IdTag = (entity: string, id: string): Tag => {
    return new Tag({ entity, id });
};

// 获取数据对象
const user1Tag = IdTag('user.demo@org0', 'user1');
const user1Data = await fetchData(user1Tag, { 
    loadingMode: 'custom',
    default: { name: 'Default' }
});

// 等待数据加载完成
await user1Data.until(ObjectStatus.Ready);

// 修改数据
await user1Data.set("name", "John");
await user1Data.set("age", 25);

// 提交数据
await user1Data.submit();

// 使用完毕后清理资源
await user1Data.destroy();
```

### 数据加载模式

```typescript
// LocalDB 模式 - 从本地数据库加载
const localData = await fetchData({ id: 'localData1' }, { 
    loadingMode: 'localDB',
    default: { a: 1 }
});

// Custom 模式 - 自定义加载逻辑
const customData = await fetchData({ id: 'customData1' }, { 
    loadingMode: 'custom'
});

// None 模式 - 不自动加载
const noneData = await fetchData({ id: 'noneData1' }, { 
    loadingMode: 'none',
    autoLoad: false
});
```

### 虚拟字段

```typescript
// 设置虚拟字段
await data.set("virtualField", value, true);

// 虚拟字段不会被保存到数据库
expect(data.virtual.virtualField).toBeDefined();
expect(data.original.virtualField).toBeUndefined();
```

### 全局配置

```typescript
// 配置 DEHub 选项
DEHubConfig({
    FuncExeTimeout: 5000,      // 异步函数执行超时（毫秒），默认 3000
    AppLoadEventDelay: 517,    // 应用加载完成事件延迟（毫秒），默认 517
    silent: false              // 是否静默控制台警告，默认 false
});
```

### 资源释放

```typescript
// 组件资源释放
const comp = new DEComp(new Tag({ role: 'input', id: 'username' }));
// ... 使用组件
await comp.destroy();  // 清理定时器、sessionStorage、事件监听

// 数据对象资源释放
const data = await fetchData(new Tag({ type: 'user', id: '1' }), {
    loadingMode: 'localDB'
});
// ... 使用数据
await data.destroy();  // 清理 IndexedDB 定时器、等待器、状态映射
```

## 性能优化

- 支持大量数据对象的高效处理
- 支持数据缓存管理
- 支持组件的批量注册和状态更新
- 支持异步加载和延迟初始化
- **O(1) 异步事件超时检测**（替代原有的 O(n²) 数组扫描）
- **引用比较去重**（替代原有的深比较去重）
- **sessionStorage 防抖写入**（300ms 节流，减少同步 I/O 阻塞）
- **标签键数安全限制**（超过 10 个键时告警，防止 2^n 组合爆炸）

## 注意事项

1. 数据对象的状态管理
   - 修改数据会进入 dirty 状态
   - 提交后数据会更新到 original 状态
   - 虚拟字段不会被保存到数据库
   - **IndexedDB 读取超时为 5000ms，超时后不会自动删除数据**

2. 组件生命周期
   - 组件注册时会触发 Mounting 事件
   - 状态变更时会触发 StateChanging 事件
   - 组件卸载时会触发 WillUnmount 事件
   - **组件或数据对象不再使用时，请务必调用 `destroy()` 防止内存泄漏**

3. 数据加载模式
   - localDB: 适合本地数据存储
   - custom: 适合自定义数据加载逻辑
   - none: 适合手动控制数据加载

4. 事件处理
   - 事件处理函数类型请使用 `IEventContext` 而非 `EventContext`
   - `EventContext` 是运行时类，`IEventContext` 是接口类型
   - 组件卸载时请使用 `stop()` 移除事件监听

## 最佳实践

1. 组件注册
   - 使用有意义的组件 ID
   - 合理使用组件的父子关系
   - **组件卸载时调用 `destroy()` 释放全局状态、定时器和存储**

2. 数据管理
   - 选择合适的加载模式
   - 合理使用虚拟字段
   - 注意数据状态的管理
   - **数据对象废弃时调用 `destroy()` 清理 IndexedDB 连接和等待器**

3. 事件处理
   - 合理使用事件预处理和后处理
   - 注意事件处理的性能影响
   - 避免事件处理的循环依赖
   - 使用 `stop()` 在组件销毁时移除事件监听
