id-scanner-lib 1.5.0 → 1.6.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +378 -59
- package/dist/id-scanner-lib.esm.js +195 -10
- package/dist/id-scanner-lib.esm.js.map +1 -1
- package/dist/id-scanner-lib.js +4812 -14709
- package/dist/id-scanner-lib.js.map +1 -1
- package/dist/types/browser-image-compression.d.ts +19 -0
- package/dist/types/tesseract.d.ts +280 -0
- package/package.json +21 -11
- package/src/core/camera-manager.ts +16 -1
- package/src/core/config.ts +37 -0
- package/src/core/errors.ts +3 -3
- package/src/core/event-emitter.test.ts +42 -0
- package/src/core/loading-state.test.ts +67 -0
- package/src/core/loading-state.ts +156 -0
- package/src/core/logger.test.ts +49 -0
- package/src/core/module-manager.ts +2 -4
- package/src/core/scanner-factory.ts +8 -9
- package/src/index.ts +3 -2
- package/src/modules/face/face-detector.ts +123 -66
- package/src/modules/id-card/anti-fake-detector.ts +2 -2
- package/src/modules/id-card/ocr-worker.ts +1 -1
- package/src/modules/qrcode/qr-code-scanner.ts +2 -1
- package/src/modules/qrcode/types.ts +111 -7
- package/src/types/common.test.ts +99 -0
- package/src/types/common.ts +166 -0
- package/src/utils/camera.test.ts +30 -0
- package/src/utils/camera.ts +4 -1
- package/src/utils/error-handler.test.ts +137 -0
- package/src/utils/error-handler.ts +110 -0
- package/src/utils/index.test.ts +186 -0
- package/src/utils/index.ts +3 -0
- package/src/utils/retry.test.ts +142 -0
- package/src/utils/retry.ts +282 -0
- package/src/utils/utils.test.ts +171 -0
- package/src/version.ts +1 -1
- package/dist/types/core/base-module.d.ts +0 -44
- package/dist/types/core/camera-manager.d.ts +0 -258
- package/dist/types/core/config.d.ts +0 -88
- package/dist/types/core/errors.d.ts +0 -111
- package/dist/types/core/event-emitter.d.ts +0 -55
- package/dist/types/core/logger.d.ts +0 -277
- package/dist/types/core/module-manager.d.ts +0 -78
- package/dist/types/core/plugin-manager.d.ts +0 -158
- package/dist/types/core/resource-manager.d.ts +0 -246
- package/dist/types/core/result.d.ts +0 -83
- package/dist/types/core/scanner-factory.d.ts +0 -93
- package/dist/types/index.bundle.d.ts +0 -1303
- package/dist/types/index.d.ts +0 -86
- package/dist/types/interfaces/external-types.d.ts +0 -174
- package/dist/types/interfaces/face-detection.d.ts +0 -293
- package/dist/types/interfaces/scanner-module.d.ts +0 -280
- package/dist/types/modules/face/face-detector.d.ts +0 -170
- package/dist/types/modules/face/index.d.ts +0 -56
- package/dist/types/modules/face/liveness-detector.d.ts +0 -177
- package/dist/types/modules/face/types.d.ts +0 -136
- package/dist/types/modules/id-card/anti-fake-detector.d.ts +0 -170
- package/dist/types/modules/id-card/id-card-detector.d.ts +0 -131
- package/dist/types/modules/id-card/index.d.ts +0 -89
- package/dist/types/modules/id-card/ocr-processor.d.ts +0 -110
- package/dist/types/modules/id-card/ocr-worker.d.ts +0 -31
- package/dist/types/modules/id-card/types.d.ts +0 -181
- package/dist/types/modules/qrcode/index.d.ts +0 -51
- package/dist/types/modules/qrcode/qr-code-scanner.d.ts +0 -64
- package/dist/types/modules/qrcode/types.d.ts +0 -67
- package/dist/types/utils/camera.d.ts +0 -81
- package/dist/types/utils/image-processing.d.ts +0 -176
- package/dist/types/utils/index.d.ts +0 -175
- package/dist/types/utils/performance.d.ts +0 -81
- package/dist/types/utils/resource-manager.d.ts +0 -53
- package/dist/types/utils/types.d.ts +0 -166
- package/dist/types/utils/worker.d.ts +0 -52
- package/dist/types/version.d.ts +0 -7
package/README.md
CHANGED
|
@@ -1,100 +1,419 @@
|
|
|
1
|
-
# ID
|
|
1
|
+
# ID Scanner Lib
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[English](./README_EN.md) | [中文](./README.md)
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
一个功能强大的浏览器端身份验证和人脸识别库,支持人脸检测、人脸比对、活体检测和二维码扫描。
|
|
6
6
|
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
7
|
+

|
|
8
|
+

|
|
9
|
+

|
|
10
|
+
|
|
11
|
+
## 特性
|
|
12
|
+
|
|
13
|
+
- 🚀 **模块化架构** - 核心组件独立封装,便于扩展和维护
|
|
14
|
+
- 👤 **人脸检测** - 快速准确的人脸定位和属性分析
|
|
15
|
+
- 🔍 **人脸比对** - 高精度的人脸相似度比对
|
|
16
|
+
- 🛡️ **活体检测** - 支持被动式和主动式活体验证,防止照片、视频欺骗
|
|
17
|
+
- 📱 **二维码扫描** - 支持QR码和多种条形码格式
|
|
18
|
+
- ⚡ **轻量级** - 优化的模型加载策略,按需加载
|
|
19
|
+
- 🌐 **跨平台** - 支持所有主流浏览器和设备
|
|
13
20
|
|
|
14
21
|
## 安装
|
|
15
22
|
|
|
23
|
+
### NPM
|
|
24
|
+
|
|
16
25
|
```bash
|
|
17
26
|
npm install id-scanner-lib
|
|
18
27
|
```
|
|
19
28
|
|
|
29
|
+
### CDN
|
|
30
|
+
|
|
31
|
+
```html
|
|
32
|
+
<!-- UMD -->
|
|
33
|
+
<script src="https://cdn.jsdelivr.net/npm/id-scanner-lib/dist/id-scanner-lib.min.js"></script>
|
|
34
|
+
|
|
35
|
+
<!-- ESM -->
|
|
36
|
+
<script type="module">
|
|
37
|
+
import IDScannerLib from 'https://cdn.jsdelivr.net/npm/id-scanner-lib/dist/id-scanner-lib.esm.js';
|
|
38
|
+
</script>
|
|
39
|
+
```
|
|
40
|
+
|
|
20
41
|
## 快速开始
|
|
21
42
|
|
|
22
|
-
###
|
|
43
|
+
### 基础使用
|
|
44
|
+
|
|
45
|
+
```typescript
|
|
46
|
+
import { IDScanner, FaceModule } from 'id-scanner-lib';
|
|
47
|
+
|
|
48
|
+
// 初始化库
|
|
49
|
+
await IDScanner.initialize({
|
|
50
|
+
debug: true
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
// 创建人脸模块
|
|
54
|
+
const faceModule = new FaceModule({
|
|
55
|
+
onFaceDetected: (faces) => console.log('检测到人脸:', faces),
|
|
56
|
+
onError: (error) => console.error('错误:', error)
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// 初始化人脸模块
|
|
60
|
+
await faceModule.initialize();
|
|
23
61
|
|
|
24
|
-
|
|
25
|
-
|
|
62
|
+
// 启动摄像头并开始人脸检测
|
|
63
|
+
const videoElement = document.getElementById('video');
|
|
64
|
+
await faceModule.startFaceRecognition(videoElement);
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### 人脸比对
|
|
68
|
+
|
|
69
|
+
```typescript
|
|
70
|
+
// 比对两张人脸图片
|
|
71
|
+
const result = await faceModule.compareFaces(image1, image2);
|
|
26
72
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
73
|
+
console.log(`相似度: ${result.similarity}`);
|
|
74
|
+
console.log(`是否匹配: ${result.isMatch}`);
|
|
75
|
+
```
|
|
30
76
|
|
|
31
|
-
|
|
32
|
-
const idCardModule = scanner.getIDCardModule();
|
|
77
|
+
### 活体检测
|
|
33
78
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
const result = await
|
|
79
|
+
```typescript
|
|
80
|
+
// 被动式活体检测
|
|
81
|
+
const result = await faceModule.detectLiveness(image, {
|
|
82
|
+
type: LivenessDetectionType.PASSIVE,
|
|
83
|
+
onlyLive: true,
|
|
84
|
+
minConfidence: 0.7
|
|
85
|
+
});
|
|
37
86
|
|
|
38
|
-
console.log(
|
|
39
|
-
console.log(
|
|
40
|
-
console.log('地址:', result.address);
|
|
87
|
+
console.log(`是否为真人: ${result.isLive}`);
|
|
88
|
+
console.log(`置信度: ${result.score}`);
|
|
41
89
|
```
|
|
42
90
|
|
|
43
91
|
### 二维码扫描
|
|
44
92
|
|
|
45
|
-
```
|
|
46
|
-
//
|
|
47
|
-
const
|
|
93
|
+
```typescript
|
|
94
|
+
// 创建二维码扫描器
|
|
95
|
+
const qrScanner = IDScanner.createQRScanner({
|
|
96
|
+
scanFrequency: 200,
|
|
97
|
+
formats: ['qrcode', 'code_128', 'code_39', 'ean_13']
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
// 初始化扫描器
|
|
101
|
+
await qrScanner.init();
|
|
102
|
+
|
|
103
|
+
// 启动实时扫描
|
|
104
|
+
await qrScanner.startRealtime(videoElement);
|
|
105
|
+
|
|
106
|
+
// 处理扫描结果
|
|
107
|
+
qrScanner.on('module:realtime:result', (event) => {
|
|
108
|
+
console.log('扫描结果:', event.result.content);
|
|
109
|
+
});
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## API 文档
|
|
113
|
+
|
|
114
|
+
### 核心类
|
|
115
|
+
|
|
116
|
+
| 类 | 说明 |
|
|
117
|
+
|---|---|
|
|
118
|
+
| `IDScanner` | 主入口类,管理所有模块 |
|
|
119
|
+
| `FaceModule` | 人脸检测、比对、活体检测模块 |
|
|
120
|
+
| `IDCardModule` | 身份证识别模块 |
|
|
121
|
+
| `QRCodeModule` | 二维码扫描模块 |
|
|
122
|
+
|
|
123
|
+
### 工具函数
|
|
124
|
+
|
|
125
|
+
| 函数 | 说明 |
|
|
126
|
+
|---|---|
|
|
127
|
+
| `withRetry()` | 带重试的异步函数包装器 |
|
|
128
|
+
| `AsyncCache` | 异步缓存类 |
|
|
129
|
+
| `Semaphore` | 信号量,并发控制 |
|
|
130
|
+
| `ErrorHandler` | 统一错误处理 |
|
|
131
|
+
| `LoadingStateManager` | 加载状态管理 |
|
|
132
|
+
|
|
133
|
+
### 类型定义
|
|
134
|
+
|
|
135
|
+
```typescript
|
|
136
|
+
import type {
|
|
137
|
+
ImageSource,
|
|
138
|
+
Rectangle,
|
|
139
|
+
Point,
|
|
140
|
+
ModuleState,
|
|
141
|
+
BaseResult
|
|
142
|
+
} from 'id-scanner-lib';
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## 性能优化
|
|
146
|
+
|
|
147
|
+
### 模型懒加载
|
|
148
|
+
|
|
149
|
+
默认只加载必要的模型,按需加载其他模型:
|
|
150
|
+
|
|
151
|
+
```typescript
|
|
152
|
+
const faceModule = new FaceModule({
|
|
153
|
+
// 只加载检测模型,不加载表情、年龄等模型
|
|
154
|
+
extractEmbeddings: false,
|
|
155
|
+
detectExpressions: false,
|
|
156
|
+
detectAgeGender: false
|
|
157
|
+
});
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### 内存管理
|
|
161
|
+
|
|
162
|
+
使用完成后务必释放资源:
|
|
163
|
+
|
|
164
|
+
```typescript
|
|
165
|
+
// 释放模块
|
|
166
|
+
await faceModule.dispose();
|
|
167
|
+
|
|
168
|
+
// 释放整个库
|
|
169
|
+
await scanner.dispose();
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
## 浏览器兼容性
|
|
173
|
+
|
|
174
|
+
| 浏览器 | 最低版本 |
|
|
175
|
+
|--------|---------|
|
|
176
|
+
| Chrome | 80+ |
|
|
177
|
+
| Firefox | 75+ |
|
|
178
|
+
| Safari | 14+ |
|
|
179
|
+
| Edge | 80+ |
|
|
180
|
+
|
|
181
|
+
## 项目结构
|
|
182
|
+
|
|
183
|
+
```
|
|
184
|
+
src/
|
|
185
|
+
├── core/ # 核心功能
|
|
186
|
+
│ ├── camera-manager.ts # 摄像头管理
|
|
187
|
+
│ ├── config.ts # 配置管理
|
|
188
|
+
│ ├── logger.ts # 日志系统
|
|
189
|
+
│ └── loading-state.ts # 加载状态
|
|
190
|
+
├── modules/ # 功能模块
|
|
191
|
+
│ ├── face/ # 人脸模块
|
|
192
|
+
│ ├── id-card/ # 身份证模块
|
|
193
|
+
│ └── qrcode/ # 二维码模块
|
|
194
|
+
├── utils/ # 工具函数
|
|
195
|
+
│ ├── retry.ts # 重试机制
|
|
196
|
+
│ └── error-handler.ts # 错误处理
|
|
197
|
+
└── types/ # 类型定义
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
## 常见问题
|
|
201
|
+
|
|
202
|
+
### Q: 模型加载失败怎么办?
|
|
203
|
+
|
|
204
|
+
A: 检查网络连接,或使用本地模型:
|
|
205
|
+
|
|
206
|
+
```typescript
|
|
207
|
+
const faceModule = new FaceModule({
|
|
208
|
+
modelPath: '/local/models'
|
|
209
|
+
});
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### Q: 如何处理权限问题?
|
|
213
|
+
|
|
214
|
+
A: 确保页面在 HTTPS 环境下运行,并获取用户授权:
|
|
215
|
+
|
|
216
|
+
```typescript
|
|
217
|
+
const stream = await navigator.mediaDevices.getUserMedia({
|
|
218
|
+
video: { facingMode: 'user' }
|
|
219
|
+
});
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
### Q: 如何处理内存泄漏?
|
|
223
|
+
|
|
224
|
+
A: 确保在使用完毕后释放资源:
|
|
225
|
+
|
|
226
|
+
```typescript
|
|
227
|
+
// 组件卸载时
|
|
228
|
+
useEffect(() => {
|
|
229
|
+
return () => {
|
|
230
|
+
faceModule?.dispose();
|
|
231
|
+
};
|
|
232
|
+
}, []);
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
### Q: 支持哪些图片格式?
|
|
236
|
+
|
|
237
|
+
A: 支持 JPEG、PNG、WebP 等浏览器常见的图片格式。
|
|
238
|
+
|
|
239
|
+
## TypeScript 类型
|
|
240
|
+
|
|
241
|
+
完整类型定义请参考 [types](./src/types/) 目录。
|
|
242
|
+
|
|
243
|
+
### 核心类型
|
|
244
|
+
|
|
245
|
+
```typescript
|
|
246
|
+
// 图像源
|
|
247
|
+
type ImageSource = HTMLImageElement | HTMLCanvasElement | HTMLVideoElement | ImageData;
|
|
248
|
+
|
|
249
|
+
// 矩形区域
|
|
250
|
+
interface Rectangle {
|
|
251
|
+
x: number;
|
|
252
|
+
y: number;
|
|
253
|
+
width: number;
|
|
254
|
+
height: number;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// 点坐标
|
|
258
|
+
interface Point {
|
|
259
|
+
x: number;
|
|
260
|
+
y: number;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// 模块状态
|
|
264
|
+
type ModuleState = 'idle' | 'loading' | 'ready' | 'error' | 'disposed';
|
|
48
265
|
|
|
49
|
-
//
|
|
50
|
-
|
|
51
|
-
|
|
266
|
+
// 人脸检测结果
|
|
267
|
+
interface FaceDetectionResult {
|
|
268
|
+
faces: Face[];
|
|
269
|
+
image: ImageData;
|
|
270
|
+
}
|
|
52
271
|
|
|
53
|
-
|
|
272
|
+
// 人脸详情
|
|
273
|
+
interface Face {
|
|
274
|
+
box: Rectangle;
|
|
275
|
+
landmarks: Point[];
|
|
276
|
+
expressions?: Record<string, number>;
|
|
277
|
+
age?: number;
|
|
278
|
+
gender?: string;
|
|
279
|
+
embedding?: number[];
|
|
280
|
+
}
|
|
54
281
|
```
|
|
55
282
|
|
|
56
|
-
|
|
283
|
+
## 错误处理
|
|
57
284
|
|
|
58
|
-
|
|
59
|
-
// 获取人脸模块
|
|
60
|
-
const faceModule = scanner.getFaceModule();
|
|
285
|
+
### 错误类型
|
|
61
286
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
const faceResult = await faceModule.detectFace(faceImage);
|
|
287
|
+
```typescript
|
|
288
|
+
import { ScannerError, ErrorCode } from 'id-scanner-lib';
|
|
65
289
|
|
|
66
|
-
|
|
67
|
-
|
|
290
|
+
try {
|
|
291
|
+
await faceModule.initialize();
|
|
292
|
+
} catch (error) {
|
|
293
|
+
if (error instanceof ScannerError) {
|
|
294
|
+
switch (error.code) {
|
|
295
|
+
case ErrorCode.CAMERA_NOT_FOUND:
|
|
296
|
+
// 处理摄像头未找到
|
|
297
|
+
break;
|
|
298
|
+
case ErrorCode.PERMISSION_DENIED:
|
|
299
|
+
// 处理权限被拒绝
|
|
300
|
+
break;
|
|
301
|
+
case ErrorCode.MODEL_LOAD_FAILED:
|
|
302
|
+
// 处理模型加载失败
|
|
303
|
+
break;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
### 错误代码
|
|
310
|
+
|
|
311
|
+
| 代码 | 说明 |
|
|
312
|
+
|------|------|
|
|
313
|
+
| `CAMERA_NOT_FOUND` | 摄像头未找到 |
|
|
314
|
+
| `PERMISSION_DENIED` | 权限被拒绝 |
|
|
315
|
+
| `MODEL_LOAD_FAILED` | 模型加载失败 |
|
|
316
|
+
| `INITIALIZATION_FAILED` | 初始化失败 |
|
|
317
|
+
| `PROCESSING_FAILED` | 处理失败 |
|
|
318
|
+
| `DISPOSED` | 模块已释放 |
|
|
68
319
|
|
|
69
|
-
|
|
70
|
-
const face1 = document.getElementById('face1');
|
|
71
|
-
const face2 = document.getElementById('face2');
|
|
72
|
-
const comparison = await faceModule.compareFaces(face1, face2);
|
|
320
|
+
## 性能调优
|
|
73
321
|
|
|
74
|
-
|
|
75
|
-
|
|
322
|
+
### 1. 调整检测频率
|
|
323
|
+
|
|
324
|
+
```typescript
|
|
325
|
+
const faceModule = new FaceModule({
|
|
326
|
+
// 降低检测频率以提升性能
|
|
327
|
+
detectionFrequency: 100, // ms
|
|
328
|
+
});
|
|
76
329
|
```
|
|
77
330
|
|
|
78
|
-
|
|
331
|
+
### 2. 缩小检测区域
|
|
79
332
|
|
|
80
|
-
|
|
333
|
+
```typescript
|
|
334
|
+
const faceModule = new FaceModule({
|
|
335
|
+
// 只检测画面中心区域
|
|
336
|
+
detectionRegion: {
|
|
337
|
+
x: 0.25,
|
|
338
|
+
y: 0.25,
|
|
339
|
+
width: 0.5,
|
|
340
|
+
height: 0.5
|
|
341
|
+
}
|
|
342
|
+
});
|
|
343
|
+
```
|
|
81
344
|
|
|
82
|
-
|
|
345
|
+
### 3. 使用 Web Worker
|
|
83
346
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
347
|
+
```typescript
|
|
348
|
+
// 身份证识别使用 Web Worker,不阻塞主线程
|
|
349
|
+
const idCardModule = new IDCardModule({
|
|
350
|
+
useWorker: true
|
|
351
|
+
});
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
### 4. 模型选择
|
|
355
|
+
|
|
356
|
+
```typescript
|
|
357
|
+
const faceModule = new FaceModule({
|
|
358
|
+
// 使用轻量级模型
|
|
359
|
+
modelType: 'tiny',
|
|
360
|
+
// 或者使用完整模型(更准确但更慢)
|
|
361
|
+
// modelType: 'full'
|
|
362
|
+
});
|
|
363
|
+
```
|
|
87
364
|
|
|
88
365
|
## 浏览器兼容性
|
|
89
366
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
367
|
+
| 浏览器 | 最低版本 | 支持情况 |
|
|
368
|
+
|--------|---------|---------|
|
|
369
|
+
| Chrome | 80+ | ✅ 完全支持 |
|
|
370
|
+
| Firefox | 75+ | ✅ 完全支持 |
|
|
371
|
+
| Safari | 14+ | ✅ 完全支持 |
|
|
372
|
+
| Edge | 80+ | ✅ 完全支持 |
|
|
373
|
+
| iOS Safari | 14+ | ✅ 完全支持 |
|
|
374
|
+
| Android Chrome | 80+ | ✅ 完全支持 |
|
|
375
|
+
|
|
376
|
+
### Polyfill
|
|
377
|
+
|
|
378
|
+
如需支持旧版浏览器,请添加以下 polyfill:
|
|
379
|
+
|
|
380
|
+
```html
|
|
381
|
+
<script src="https://polyfill.io/v3/polyfill.min.js"></script>
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
## 项目结构
|
|
385
|
+
|
|
386
|
+
```
|
|
387
|
+
src/
|
|
388
|
+
├── core/ # 核心功能
|
|
389
|
+
│ ├── camera-manager.ts # 摄像头管理
|
|
390
|
+
│ ├── config.ts # 配置管理
|
|
391
|
+
│ ├── logger.ts # 日志系统
|
|
392
|
+
│ └── loading-state.ts # 加载状态
|
|
393
|
+
├── modules/ # 功能模块
|
|
394
|
+
│ ├── face/ # 人脸模块
|
|
395
|
+
│ ├── id-card/ # 身份证模块
|
|
396
|
+
│ └── qrcode/ # 二维码模块
|
|
397
|
+
├── utils/ # 工具函数
|
|
398
|
+
│ ├── retry.ts # 重试机制
|
|
399
|
+
│ └── error-handler.ts # 错误处理
|
|
400
|
+
└── types/ # 类型定义
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
## 贡献指南
|
|
404
|
+
|
|
405
|
+
欢迎提交 Issue 和 Pull Request!
|
|
406
|
+
|
|
407
|
+
1. Fork 本仓库
|
|
408
|
+
2. 创建特性分支 (`git checkout -b feature/xxx`)
|
|
409
|
+
3. 提交更改 (`git commit -m 'Add xxx'`)
|
|
410
|
+
4. 推送分支 (`git push origin feature/xxx`)
|
|
411
|
+
5. 创建 Pull Request
|
|
97
412
|
|
|
98
413
|
## 许可证
|
|
99
414
|
|
|
100
|
-
|
|
415
|
+
MIT License
|
|
416
|
+
|
|
417
|
+
## 更新日志
|
|
418
|
+
|
|
419
|
+
See [CHANGELOG](./CHANGELOG.md)
|