npm package: @seed-fe/exception-handler
Description: A library that handles global exceptions.
Author: xianghongai
Latest version: 1.0.0

# @seed-fe/exception-handler

一个现代化的全局异常处理库，支持多种异常来源的统一捕获和处理。

## 特性

- 内置 JavaScript 标准异常捕获（`error`、`unhandledrejection`）
- 支持第三方框架异常接入（Vue、React 等）
- 条件化异常处理，支持多处理器协作
- 并行/串行处理模式
- Leading/Trailing 钩子支持
- 动态处理器管理
- Symbol/String 联合类型名称支持
- 元数据支持，增强异常上下文
- 完整的 TypeScript 类型支持

## 安装

```bash
# 使用 npm
npm install @seed-fe/exception-handler

# 使用 yarn
yarn add @seed-fe/exception-handler

# 使用 pnpm (推荐)
pnpm add @seed-fe/exception-handler
```

## 基础使用

### 快速开始

```typescript
import { createExceptionCatcher } from '@seed-fe/exception-handler';

// 创建异常捕获器 - 现在支持所有参数在一个配置对象中
const catcher = createExceptionCatcher({
  handlers: [
    {
      name: 'logger',
      condition: () => true,
      handler: async (context) => {
        console.log('捕获到异常:', context.error.message);
      }
    }
  ],
  enableStandardCapture: true, // 启用标准异常捕获
  enableConsoleError: true     // 启用控制台错误输出
});

// 启动标准异常监听
catcher.start();

// 手动捕获异常
catcher.captureException(new Error('自定义错误'), {
  source: 'user-action',
  metadata: { userId: '123', page: '/dashboard' }
});
```

### 直接使用构造函数

```typescript
import { ExceptionCatcher } from '@seed-fe/exception-handler';

// 也可以直接使用构造函数，支持相同的配置参数
const catcher = new ExceptionCatcher({
  handlers: [
    {
      name: 'logger',
      condition: () => true,
      handler: async (context) => {
        console.log('捕获到异常:', context.error.message);
      }
    }
  ],
  leadingHook: {
    handler: async () => ({ userId: getCurrentUserId() })
  },
  enableStandardCapture: true
});

catcher.start();
```

### 条件化处理

```typescript
const handlers = [
  {
    name: 'error-reporter',
    condition: (context) => context.error.message.includes('critical'),
    handler: async (context) => {
      // 只处理关键错误
      await reportToService(context.error);
    }
  },
  {
    name: 'logger',
    condition: () => true,
    handler: async (context) => {
      // 记录所有错误
      console.log(context.error);
    }
  }
];

catcher.setHandlers(handlers);
```

约定如果 `name` 为 `__fallback__` 的处理器，当没有其他处理器处理异常时使用。

### 并行/串行处理

处理器可以配置 `mode` 属性，决定是并行执行还是串行执行。默认是串行执行。

- `mode: 'parallel'` 并行执行，同时执行，看到相同的初始上下文
- `mode: 'serial'` 串行执行，按顺序执行，后面的处理器可以看到前面的处理历史

```typescript
const handlers = [
  {
    name: 'parallel-logger',
    condition: () => true,
    handler: async (context) => {
      console.log('并行处理 1');
    },
    mode: 'parallel' // 并行执行
  },
  {
    name: 'parallel-reporter',
    condition: () => true,
    handler: async (context) => {
      console.log('并行处理 2');
    },
    mode: 'parallel' // 与上面同时执行
  },
  {
    name: 'serial-cleanup',
    condition: () => true,
    handler: async (context) => {
      console.log('串行处理，在并行处理完成后执行');
    },
    mode: 'serial' // 默认串行
  }
];
```

### Leading/Trailing 钩子

Leading/Trailing 钩子是可选的，可以不设置。也可以在 `createExceptionCatcher` 时设置。

应用场景：

- 前置钩子：在所有处理器执行前运行，可以获取一些初始数据，如用户信息、页面信息等。
- 后置钩子：在所有处理器执行后运行，可以进行一些清理工作，如统计信息上报、用户通知等。

```typescript
// 方式1：创建时设置钩子
const catcher = createExceptionCatcher({
  handlers: [...],
  leadingHook: {
    handler: async (context) => {
      // 在所有处理器执行前运行
      return {
        userId: getCurrentUserId(),
        timestamp: Date.now()
      };
    },
    breakOnError: true // 前置钩子出错时是否中断处理链
  },
  trailingHook: {
    handler: async (context, leadingResult) => {
      // 在所有处理器执行后运行
      console.log('处理完成', leadingResult);
    }
  }
});

// 方式2：动态设置钩子
catcher.setLeadingHook({
  handler: async (context) => {
    return {
      userId: getCurrentUserId(),
      timestamp: Date.now()
    };
  },
  breakOnError: true
});

catcher.setTrailingHook({
  handler: async (context, leadingResult) => {
    console.log('处理完成', leadingResult);
  }
});
```

## 框架集成

### Vue 集成

```typescript
// Vue 2
Vue.config.errorHandler = (error, vm, info) => {
  catcher.captureException(error, {
    source: 'vue2',
    metadata: { info, componentName: vm?.$options.name }
  });
};

// Vue 3
app.config.errorHandler = (error, instance, info) => {
  catcher.captureException(error, {
    source: 'vue3',
    metadata: { info, componentName: instance?.$?.type?.name }
  });
};
```

### React 集成

```typescript
// Error Boundary
class ErrorBoundary extends React.Component {
  componentDidCatch(error, errorInfo) {
    catcher.captureException(error, {
      source: 'react-boundary',
      metadata: { errorInfo }
    });
  }
}

// 或使用 React 19+ 的新 API
const root = createRoot(container, {
  onCaughtError: (error, errorInfo) => {
    catcher.captureException(error, {
      source: 'react-caught',
      metadata: { errorInfo }
    });
  }
});
```

## 动态管理

### 处理器管理

```typescript
// 添加处理器
catcher.addHandler({
  name: 'new-handler',
  condition: () => true,
  handler: async (context) => {
    console.log('新处理器');
  }
});

// 移除处理器
catcher.removeHandler('new-handler');

// 更新处理器
catcher.updateHandler('logger', {
  name: 'logger',
  condition: () => false, // 禁用
  handler: async () => {}
});

// 检查处理器是否存在
if (catcher.hasHandler('logger')) {
  console.log('logger 处理器存在');
}
```

### 回退处理器

```typescript
// 设置回退处理器，当没有其他处理器处理异常时使用
catcher.setFallbackHandler({
  condition: () => true,
  handler: async (context) => {
    console.error('未处理的异常:', context.error);
  }
});

// 移除回退处理器
catcher.removeFallbackHandler();
```

## Symbol 支持

```typescript
// 使用 Symbol 避免命名冲突
const LOGGER_SYMBOL = Symbol('logger');
const REPORTER_SYMBOL = Symbol('reporter');

catcher.addHandler({
  name: LOGGER_SYMBOL,
  condition: () => true,
  handler: async (context) => {
    console.log('Symbol 处理器');
  }
});

// 通过 Symbol 访问
catcher.updateHandler(LOGGER_SYMBOL, newHandler);
catcher.removeHandler(LOGGER_SYMBOL);
```

## API 参考

### createExceptionCatcher

工厂函数，创建配置好的异常捕获器。现在与构造函数使用相同的配置参数。

```typescript
createExceptionCatcher(config?: ExceptionCatcherOptions): ExceptionCatcher
```

### ExceptionCatcher 构造函数

直接实例化异常捕获器类，支持与工厂函数相同的配置参数。

```typescript
new ExceptionCatcher(options?: ExceptionCatcherOptions)
```

### 实例方法

| 实例方法名                                   | 说明                     |
| ---------------------------------------- | ------------------------ |
| `start()`                               | 启动标准异常监听         |
| `stop()`                                | 停止标准异常监听         |
| `captureException(error, options?)`      | 手动捕获异常             |
| `setHandlers(handlers)`                  | 设置处理器数组           |
| `addHandler(handler)`                    | 添加单个处理器           |
| `removeHandler(name)`                    | 移除处理器               |
| `updateHandler(name, handler)`           | 更新处理器               |
| `setLeadingHook(hook)`                   | 设置前置钩子             |
| `setTrailingHook(hook)`                  | 设置后置钩子             |
| `destroy()`                             | 销毁实例                 |

## 类型系统

### 核心类型

```typescript
// 处理器名称：支持字符串和 Symbol
type HandlerName = string | symbol;

// 异常处理上下文
type HandlerContext = {
  error: Error;                    // 错误对象
  source: string;                  // 异常来源
  processedBy: ProcessRecord[];    // 处理记录
  originalEvent?: ErrorEvent | PromiseRejectionEvent;  // 原始事件
  leadingResult?: LeadingResult;  // 前置钩子结果
  metadata?: Record<string, unknown>;  // 自定义元数据
};

// 异常处理器
type ExceptionHandler = {
  name?: HandlerName;  // 处理器名称
  condition: (context: HandlerContext) => boolean | Promise<boolean>;  // 处理条件
  handler: (context: HandlerContext) => Promise<void>;  // 处理函数
  acceptProcessed?: boolean;  // 是否接收已处理的异常
  mode?: 'serial' | 'parallel';  // 执行模式
};

// 前置钩子
type LeadingHook = {
  handler: (context: HandlerContext) => Promise<LeadingResult>;
  breakOnError?: boolean;  // 出错时是否中断处理链，默认 false 不中断
};

// 后置钩子
type TrailingHook = {
  handler: (context: HandlerContext, leadingResult?: LeadingResult) => Promise<void>;
};

// 异常捕获器配置选项
type ExceptionCatcherOptions = {
  enableStandardCapture?: boolean; // 控制是否自动监听全局 `error` 和 `unhandledrejection` 事件，默认 true
  enableConsoleError?: boolean; // 控制是否输出库内部的错误日志，默认 true
  handlers?: ExceptionHandler[]; // 初始异常处理器数组
  leadingHook?: LeadingHook; // 初始前置钩子
  trailingHook?: TrailingHook; // 初始后置钩子
};
```

## 许可证

MIT