@file-ud.js/plugins 0.1.0

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 ADDED
@@ -0,0 +1,583 @@
1
+ # file-ud.js 插件系统
2
+
3
+ ## 📖 概述
4
+
5
+ file-ud.js 提供了强大的插件系统,允许你通过非侵入式的方式扩展上传功能。所有插件都实现 `IUDPlugin` 接口,可以在不修改核心代码的情况下添加新功能。
6
+
7
+ ---
8
+
9
+ ## 🎯 设计理念
10
+
11
+ ### 1. 非侵入式扩展
12
+ - ✅ **零核心代码修改**:所有功能通过插件实现
13
+ - ✅ **向后兼容**:不影响现有功能和 API
14
+ - ✅ **可选启用**:用户可以选择性使用插件
15
+
16
+ ### 2. 插件化架构
17
+ - 实现 `IUDPlugin` 接口
18
+ - 利用事件钩子(`onFileSelect`, `beforeTransfer`, `onProgress` 等)
19
+ - 独立的配置和状态管理
20
+
21
+ ### 3. 优先级机制
22
+ - 每个插件可以设置 `priority`(数字越小越先执行)
23
+ - 关键插件(如验证器)使用 priority: 0
24
+ - 普通插件使用 priority: 10-50
25
+
26
+ ---
27
+
28
+ ## 📦 内置插件
29
+
30
+ ### 1. 文件验证插件 (FileValidatorPlugin)
31
+
32
+ **优先级**: 0(最高)
33
+ **位置**: [`validator/README.md`](./validator/README.md)
34
+
35
+ **功能**:
36
+ - ✅ 文件大小验证(最小/最大)
37
+ - ✅ 文件类型验证
38
+ - ✅ 空文件检测
39
+ - ✅ 自定义验证函数
40
+ - ✅ 错误消息定制
41
+
42
+ **快速开始**:
43
+ ```typescript
44
+ import { FileValidatorPlugin } from '@file-ud.js/plugins/uploader';
45
+
46
+ uploader.use(new FileValidatorPlugin({
47
+ maxSize: 10 * 1024 * 1024, // 10MB
48
+ accept: ['image/*'],
49
+ allowEmpty: false
50
+ }));
51
+ ```
52
+
53
+ ---
54
+
55
+ ### 2. 图片压缩插件 (CompressImagePlugin)
56
+
57
+ **优先级**: 10
58
+ **位置**: [`compress/README.md`](./compress/README.md)
59
+
60
+ **功能**:
61
+ - ✅ 智能图片压缩
62
+ - ✅ 尺寸调整
63
+ - ✅ 格式转换(JPEG/PNG/WebP)
64
+ - ✅ 质量可控
65
+ - ✅ 实时反馈
66
+
67
+ **快速开始**:
68
+ ```typescript
69
+ import { CompressImagePlugin } from '@file-ud.js/plugins/uploader';
70
+
71
+ uploader.use(new CompressImagePlugin({
72
+ quality: 0.8,
73
+ maxWidth: 1920,
74
+ maxHeight: 1080,
75
+ format: "webp"
76
+ }));
77
+ ```
78
+
79
+ ---
80
+
81
+ ### 3. 水印插件 (WatermarkPlugin)
82
+
83
+ **优先级**: 20
84
+ **位置**: [`watermark/README.md`](./watermark/README.md)
85
+
86
+ **功能**:
87
+ - ✅ 文字水印
88
+ - ✅ 图片水印
89
+ - ✅ 多位置选择(5个预设位置)
90
+ - ✅ 透明度控制
91
+ - ✅ 样式自定义
92
+
93
+ **快速开始**:
94
+ ```typescript
95
+ import { WatermarkPlugin } from '@file-ud.js/plugins/uploader';
96
+
97
+ // 文字水印
98
+ uploader.use(new WatermarkPlugin({
99
+ text: "© MyCompany",
100
+ position: "bottom-right",
101
+ opacity: 0.6
102
+ }));
103
+
104
+ // 图片水印
105
+ uploader.use(new WatermarkPlugin({
106
+ imageUrl: "https://example.com/logo.png",
107
+ position: "top-left",
108
+ imageWidth: 100,
109
+ imageHeight: 100
110
+ }));
111
+ ```
112
+
113
+ ---
114
+
115
+ ### 4. 智能重试插件 (SmartRetryPlugin)
116
+
117
+ **优先级**: 10
118
+ **位置**: [`retry/README.md`](./retry/README.md)
119
+
120
+ **功能**:
121
+ - ✅ 三种重试策略(固定延迟、指数退避、线性增长)
122
+ - ✅ 可配置重试次数
123
+ - ✅ 智能错误过滤
124
+ - ✅ 自动状态清理
125
+ - ✅ 完全非侵入式
126
+
127
+ **快速开始**:
128
+ ```typescript
129
+ import { SmartRetryPlugin } from '@file-ud.js/plugins/retry';
130
+
131
+ uploader.use(new SmartRetryPlugin({
132
+ maxRetries: 3,
133
+ strategy: "exponential",
134
+ initialDelay: 1000,
135
+ maxDelay: 30000
136
+ }));
137
+ ```
138
+
139
+ ---
140
+
141
+ ## 🔧 使用指南
142
+
143
+ ### 1. 安装插件包
144
+
145
+ ```bash
146
+ npm install @file-ud.js/plugins
147
+ ```
148
+
149
+ ### 2. 导入和使用插件
150
+
151
+ 推荐按能力导入,根入口保留用于兼容旧版本和自定义插件基类:
152
+
153
+ ```typescript
154
+ // 上传相关插件
155
+ import { FileValidatorPlugin, CompressImagePlugin } from '@file-ud.js/plugins/uploader';
156
+
157
+ // 下载相关插件(当前先提供通用重试能力)
158
+ import { SmartRetryPlugin as DownloaderRetryPlugin } from '@file-ud.js/plugins/downloader';
159
+
160
+ // 上传/下载通用插件
161
+ import { SmartRetryPlugin } from '@file-ud.js/plugins/retry';
162
+
163
+ // 自定义插件基类
164
+ import { BasePlugin } from '@file-ud.js/plugins';
165
+ ```
166
+
167
+ ```typescript
168
+ import {
169
+ FileValidatorPlugin,
170
+ CompressImagePlugin,
171
+ WatermarkPlugin
172
+ } from '@file-ud.js/plugins/uploader';
173
+ import { SmartRetryPlugin } from '@file-ud.js/plugins/retry';
174
+
175
+ const uploader = FileUD.createUploader("myUploader", {
176
+ action: '/api/upload',
177
+ });
178
+
179
+ // 使用单个插件
180
+ uploader.use(new FileValidatorPlugin({
181
+ maxSize: 10 * 1024 * 1024
182
+ }));
183
+
184
+ // 或使用多个插件
185
+ uploader.use([
186
+ new FileValidatorPlugin({ maxSize: 10 * 1024 * 1024 }),
187
+ new CompressImagePlugin({ quality: 0.8 }),
188
+ new SmartRetryPlugin({ maxRetries: 3 })
189
+ ]);
190
+ ```
191
+
192
+ ### 3. 插件执行顺序
193
+
194
+ 插件按照 `priority` 从小到大依次执行:
195
+
196
+ ```typescript
197
+ uploader.use([
198
+ new FileValidatorPlugin(), // priority: 0 (最先执行)
199
+ new CompressImagePlugin(), // priority: 10
200
+ new SmartRetryPlugin(), // priority: 10
201
+ new WatermarkPlugin() // priority: 20 (最后执行)
202
+ ]);
203
+ ```
204
+
205
+ **典型流程**:
206
+ ```
207
+ 文件选择
208
+
209
+ FileValidatorPlugin (验证文件)
210
+
211
+ CompressImagePlugin (压缩图片)
212
+
213
+ WatermarkPlugin (添加水印)
214
+
215
+ 上传开始
216
+
217
+ SmartRetryPlugin (失败时重试)
218
+
219
+ 上传完成
220
+ ```
221
+
222
+ ---
223
+
224
+ ## 🎨 开发自定义插件
225
+
226
+ ### 1. 插件接口
227
+
228
+ 所有插件必须实现 `IUDPlugin` 接口:
229
+
230
+ ```typescript
231
+ interface IUDPlugin {
232
+ /** 插件名称 */
233
+ name: string;
234
+
235
+ /** 插件版本 */
236
+ version?: string;
237
+
238
+ /** 插件描述 */
239
+ desc?: string;
240
+
241
+ /** 插件优先级(数字越小越先执行) */
242
+ priority?: number;
243
+
244
+ /** 插件初始化(注册时调用一次) */
245
+ install?: (uploader: Uploader, options?: any) => void | Promise<void>;
246
+
247
+ /** 创建时钩子 */
248
+ created?: (uploader: Uploader) => void | Promise<void>;
249
+
250
+ /** 文件选择后触发 */
251
+ onFileSelect?: (
252
+ file: UploadFile,
253
+ context: PluginContext,
254
+ ) => Promise<UploadFile | void | false> | UploadFile | void;
255
+
256
+ /** 上传前触发 */
257
+ beforeTransfer?: (
258
+ file: UploadFile,
259
+ context: PluginContext,
260
+ ) => Promise<boolean | void> | boolean | void;
261
+
262
+ /** 上传进度触发 */
263
+ onProgress?: (
264
+ percent: number,
265
+ file: UploadFile,
266
+ context: PluginContext,
267
+ ) => void;
268
+
269
+ /** 上传成功触发 */
270
+ onSuccess?: (
271
+ response: any,
272
+ file: UploadFile,
273
+ context: PluginContext,
274
+ ) => void;
275
+
276
+ /** 上传失败触发 */
277
+ onError?: (
278
+ error: Error,
279
+ file: UploadFile,
280
+ context: PluginContext,
281
+ ) => void;
282
+
283
+ /** 插件销毁时调用 */
284
+ destroy?: () => void;
285
+ }
286
+ ```
287
+
288
+ ### 2. 基础插件类
289
+
290
+ 推荐使用 `BasePlugin` 作为基类:
291
+
292
+ ```typescript
293
+ import { BasePlugin } from '@file-ud.js/plugins';
294
+ import { UploadFile, PluginContext } from '@file-ud.js/core/types';
295
+
296
+ export interface MyPluginOptions {
297
+ // 配置选项
298
+ }
299
+
300
+ export class MyPlugin extends BasePlugin {
301
+ name = "my-plugin";
302
+ version = "1.0.0";
303
+ desc = "我的自定义插件";
304
+ priority = 50;
305
+
306
+ private options: Required<MyPluginOptions>;
307
+
308
+ constructor(options: MyPluginOptions = {}) {
309
+ super();
310
+ this.options = {
311
+ // 默认配置
312
+ ...options
313
+ };
314
+ }
315
+
316
+ async onFileSelect(file: UploadFile, context: PluginContext) {
317
+ // 实现你的逻辑
318
+ console.log('文件已选择:', file.fileName);
319
+
320
+ // 可以修改文件
321
+ // file.proxy.File = newFile;
322
+
323
+ return file;
324
+ }
325
+ }
326
+ ```
327
+
328
+ ### 3. 完整示例:日志插件
329
+
330
+ ```typescript
331
+ import { BasePlugin } from '@file-ud.js/plugins';
332
+ import { UploadFile, PluginContext } from '@file-ud.js/core/types';
333
+
334
+ export class LoggerPlugin extends BasePlugin {
335
+ name = "logger-plugin";
336
+ version = "1.0.0";
337
+ desc = "上传日志插件";
338
+ priority = 5;
339
+
340
+ onFileSelect(file: UploadFile, context: PluginContext) {
341
+ console.log(`📁 文件选择: ${file.fileName} (${file.formatSize})`);
342
+ return file;
343
+ }
344
+
345
+ beforeTransfer(file: UploadFile, context: PluginContext) {
346
+ console.log(`⬆️ 开始上传: ${file.fileName}`);
347
+ return true;
348
+ }
349
+
350
+ onProgress(percent: number, file: UploadFile, context: PluginContext) {
351
+ if (percent % 10 === 0) { // 每 10% 输出一次
352
+ console.log(`📊 上传进度: ${file.fileName} - ${percent}%`);
353
+ }
354
+ }
355
+
356
+ onSuccess(response: any, file: UploadFile, context: PluginContext) {
357
+ console.log(`✅ 上传成功: ${file.fileName}`, response);
358
+ }
359
+
360
+ onError(error: Error, file: UploadFile, context: PluginContext) {
361
+ console.error(`❌ 上传失败: ${file.fileName}`, error);
362
+ }
363
+ }
364
+ ```
365
+
366
+ **使用**:
367
+ ```typescript
368
+ uploader.use(new LoggerPlugin());
369
+ ```
370
+
371
+ ### 4. 插件生命周期
372
+
373
+ ```
374
+ 1. install() - 插件注册时调用(仅一次)
375
+
376
+ 2. created() - Uploader 创建时调用
377
+
378
+ 3. onFileSelect() - 用户选择文件时调用
379
+
380
+ 4. beforeTransfer() - 上传开始前调用
381
+
382
+ 5. onProgress() - 上传过程中持续调用
383
+
384
+ 6. onSuccess() - 上传成功时调用
385
+
386
+ onError() - 上传失败时调用
387
+
388
+ 7. destroy() - 插件卸载时调用
389
+ ```
390
+
391
+ ---
392
+
393
+ ## 💡 最佳实践
394
+
395
+ ### 1. 合理设置优先级
396
+
397
+ ```typescript
398
+ // 验证类插件:priority 0-5
399
+ class ValidatorPlugin extends BasePlugin {
400
+ priority = 0; // 最先执行
401
+ }
402
+
403
+ // 处理类插件:priority 10-20
404
+ class CompressPlugin extends BasePlugin {
405
+ priority = 10;
406
+ }
407
+
408
+ // 辅助类插件:priority 30-50
409
+ class LoggerPlugin extends BasePlugin {
410
+ priority = 50; // 最后执行
411
+ }
412
+ ```
413
+
414
+ ### 2. 错误处理
415
+
416
+ ```typescript
417
+ async onFileSelect(file: UploadFile, context: PluginContext) {
418
+ try {
419
+ // 你的逻辑
420
+ const result = await doSomething(file);
421
+ return result;
422
+ } catch (error) {
423
+ console.error('插件执行失败:', error);
424
+ // 返回原文件,不阻断上传流程
425
+ return file;
426
+ }
427
+ }
428
+ ```
429
+
430
+ ### 3. 状态清理
431
+
432
+ ```typescript
433
+ export class MyPlugin extends BasePlugin {
434
+ private stateMap = new Map<string, any>();
435
+
436
+ onSuccess(response: any, file: UploadFile, context: PluginContext) {
437
+ // 清理文件相关的状态
438
+ this.stateMap.delete(file.fileId);
439
+ }
440
+
441
+ destroy() {
442
+ // 清理所有状态
443
+ this.stateMap.clear();
444
+ }
445
+ }
446
+ ```
447
+
448
+ ### 4. 配置验证
449
+
450
+ ```typescript
451
+ constructor(options: MyPluginOptions = {}) {
452
+ super();
453
+
454
+ // 验证配置
455
+ if (options.quality && (options.quality < 0 || options.quality > 1)) {
456
+ throw new Error('quality 必须在 0-1 之间');
457
+ }
458
+
459
+ this.options = {
460
+ ...this.defaultOptions,
461
+ ...options
462
+ };
463
+ }
464
+ ```
465
+
466
+ ---
467
+
468
+ ## 🐛 常见问题
469
+
470
+ ### Q1: 如何调试插件?
471
+
472
+ ```typescript
473
+ class DebugPlugin extends BasePlugin {
474
+ name = "debug-plugin";
475
+ priority = 1;
476
+
477
+ onFileSelect(file, context) {
478
+ console.group('🔍 插件调试信息');
479
+ console.log('文件名:', file.fileName);
480
+ console.log('文件大小:', file.File.size);
481
+ console.log('文件类型:', file.File.type);
482
+ console.log('上下文:', context);
483
+ console.groupEnd();
484
+
485
+ return file;
486
+ }
487
+ }
488
+ ```
489
+
490
+ ### Q2: 插件之间如何通信?
491
+
492
+ 使用 `context.shared` Map:
493
+
494
+ ```typescript
495
+ // 插件 A:存储数据
496
+ class PluginA extends BasePlugin {
497
+ onFileSelect(file, context) {
498
+ context.shared.set('pluginA_data', { timestamp: Date.now() });
499
+ return file;
500
+ }
501
+ }
502
+
503
+ // 插件 B:读取数据
504
+ class PluginB extends BasePlugin {
505
+ onFileSelect(file, context) {
506
+ const data = context.shared.get('pluginA_data');
507
+ console.log('来自插件A的数据:', data);
508
+ return file;
509
+ }
510
+ }
511
+ ```
512
+
513
+ ### Q3: 如何阻止上传?
514
+
515
+ ```typescript
516
+ // 方式1:在 onFileSelect 中返回 false
517
+ onFileSelect(file, context) {
518
+ if (!isValid(file)) {
519
+ return false; // 阻止上传
520
+ }
521
+ return file;
522
+ }
523
+
524
+ // 方式2:在 beforeTransfer 中返回 false
525
+ beforeTransfer(file, context) {
526
+ if (!shouldUpload(file)) {
527
+ return false; // 阻止上传
528
+ }
529
+ return true;
530
+ }
531
+ ```
532
+
533
+ ### Q4: 如何修改文件内容?
534
+
535
+ ```typescript
536
+ onFileSelect(file, context) {
537
+ // 替换文件对象
538
+ const newFile = new File([blob], file.fileName, {
539
+ type: 'image/jpeg'
540
+ });
541
+
542
+ // 使用 proxy 确保响应式更新
543
+ file.proxy.File = newFile;
544
+ file.proxy.formatSize = formatFileSize(newFile.size);
545
+
546
+ return file;
547
+ }
548
+ ```
549
+
550
+ ---
551
+
552
+ ## 📚 相关资源
553
+
554
+ - [文件验证插件文档](./validator/README.md)
555
+ - [图片压缩插件文档](./compress/README.md)
556
+ - [水印插件文档](./watermark/README.md)
557
+ - [智能重试插件文档](./retry/README.md)
558
+ - [file-ud.js 核心文档](../../../README.md)
559
+
560
+ ---
561
+
562
+ ## 🤝 贡献指南
563
+
564
+ 欢迎提交新的插件!
565
+
566
+ **提交步骤**:
567
+ 1. Fork 仓库
568
+ 2. 创建新分支 (`feature/my-plugin`)
569
+ 3. 实现插件(遵循 `IUDPlugin` 接口)
570
+ 4. 编写详细文档(README.md)
571
+ 5. 添加使用示例
572
+ 6. 提交 Pull Request
573
+
574
+ **插件要求**:
575
+ - ✅ 实现完整的 TypeScript 类型定义
576
+ - ✅ 提供详细的 README 文档
577
+ - ✅ 包含使用示例
578
+ - ✅ 不修改核心代码
579
+ - ✅ 良好的错误处理
580
+
581
+ **报告 Bug**:[GitHub Issues](https://github.com/your-repo/file-ud/issues)
582
+
583
+ **提出建议**:[Feature Requests](https://github.com/your-repo/file-ud/discussions)
@@ -0,0 +1,2 @@
1
+ export { SmartRetryConfig, SmartRetryPlugin } from '../retry/index.mjs';
2
+ import '@file-ud.js/core';
@@ -0,0 +1,2 @@
1
+ export { SmartRetryConfig, SmartRetryPlugin } from '../retry/index.js';
2
+ import '@file-ud.js/core';
@@ -0,0 +1,126 @@
1
+ 'use strict';
2
+
3
+ // src/retry/smart-retry-plugin.ts
4
+ var SmartRetryPlugin = class {
5
+ // 文件ID -> 重试定时器
6
+ constructor(config = {}) {
7
+ this.name = "SmartRetryPlugin";
8
+ this.version = "1.0.0";
9
+ this.desc = "\u667A\u80FD\u91CD\u8BD5\u7B56\u7565\u63D2\u4EF6\uFF0C\u540C\u65F6\u652F\u6301\u4E0A\u4F20/\u4E0B\u8F7D\uFF0C\u652F\u6301\u6307\u6570\u9000\u907F\u3001\u7EBF\u6027\u589E\u957F\u7B49\u591A\u79CD\u91CD\u8BD5\u7B56\u7565";
10
+ this.priority = 10;
11
+ this.retryCountMap = /* @__PURE__ */ new Map();
12
+ // 文件ID -> 重试次数
13
+ this.retryTimerMap = /* @__PURE__ */ new Map();
14
+ this.config = {
15
+ maxRetries: config.maxRetries ?? 3,
16
+ strategy: config.strategy ?? "exponential",
17
+ initialDelay: config.initialDelay ?? 1e3,
18
+ maxDelay: config.maxDelay ?? 3e4,
19
+ retryableErrors: config.retryableErrors ?? [],
20
+ showRetryNotification: config.showRetryNotification ?? true
21
+ };
22
+ }
23
+ /**
24
+ * 插件初始化
25
+ */
26
+ install(transfer, options) {
27
+ console.log(`\u2705 [${this.name}] \u63D2\u4EF6\u5DF2\u5B89\u88C5`, this.config);
28
+ }
29
+ /**
30
+ * 传输失败时触发(上传/下载通用)
31
+ */
32
+ onError(error, file, context) {
33
+ const fileId = file.fileId;
34
+ const currentRetries = this.retryCountMap.get(fileId) || 0;
35
+ if (currentRetries >= this.config.maxRetries) {
36
+ console.warn(
37
+ `[${this.name}] \u6587\u4EF6 ${file.fileName} \u5DF2\u8FBE\u5230\u6700\u5927\u91CD\u8BD5\u6B21\u6570 (${this.config.maxRetries})\uFF0C\u505C\u6B62\u91CD\u8BD5`
38
+ );
39
+ this.cleanup(fileId);
40
+ return;
41
+ }
42
+ if (!this.isRetryableError(error)) {
43
+ console.log(
44
+ `[${this.name}] \u6587\u4EF6 ${file.fileName} \u7684\u9519\u8BEF\u4E0D\u53EF\u91CD\u8BD5\uFF0C\u8DF3\u8FC7\u91CD\u8BD5`
45
+ );
46
+ return;
47
+ }
48
+ const delay = this.calculateDelay(currentRetries);
49
+ const timer = setTimeout(() => {
50
+ console.log(
51
+ `[${this.name}] \u6587\u4EF6 ${file.fileName} \u7B2C ${currentRetries + 1}/${this.config.maxRetries} \u6B21\u91CD\u8BD5\uFF08\u5EF6\u8FDF ${delay}ms\uFF09`
52
+ );
53
+ this.retryCountMap.set(fileId, currentRetries + 1);
54
+ file.retry();
55
+ this.retryTimerMap.delete(fileId);
56
+ }, delay);
57
+ this.retryTimerMap.set(fileId, timer);
58
+ if (this.config.showRetryNotification) {
59
+ console.info(
60
+ `[${this.name}] \u23F3 \u6587\u4EF6 ${file.fileName} \u5C06\u5728 ${delay}ms \u540E\u81EA\u52A8\u91CD\u8BD5...`
61
+ );
62
+ }
63
+ }
64
+ /**
65
+ * 传输成功时清理重试状态
66
+ */
67
+ onSuccess(response, file, context) {
68
+ this.cleanup(file.fileId);
69
+ }
70
+ /**
71
+ * 文件移除时清理重试状态
72
+ */
73
+ destroy() {
74
+ this.retryTimerMap.forEach((timer) => clearTimeout(timer));
75
+ this.retryTimerMap.clear();
76
+ this.retryCountMap.clear();
77
+ console.log(`\u{1F5D1}\uFE0F [${this.name}] \u63D2\u4EF6\u5DF2\u9500\u6BC1`);
78
+ }
79
+ /**
80
+ * 判断错误是否可重试
81
+ */
82
+ isRetryableError(error) {
83
+ if (this.config.retryableErrors.length === 0) {
84
+ return true;
85
+ }
86
+ const errorCode = error.code || error.message || "";
87
+ return this.config.retryableErrors.some(
88
+ (code) => errorCode.includes(code)
89
+ );
90
+ }
91
+ /**
92
+ * 计算延迟时间
93
+ */
94
+ calculateDelay(retryCount) {
95
+ const { strategy, initialDelay, maxDelay } = this.config;
96
+ let delay;
97
+ switch (strategy) {
98
+ case "fixed":
99
+ delay = initialDelay;
100
+ break;
101
+ case "linear":
102
+ delay = initialDelay * (retryCount + 1);
103
+ break;
104
+ case "exponential":
105
+ default:
106
+ delay = initialDelay * Math.pow(2, retryCount);
107
+ break;
108
+ }
109
+ return Math.min(delay, maxDelay);
110
+ }
111
+ /**
112
+ * 清理文件的重试状态
113
+ */
114
+ cleanup(fileId) {
115
+ const timer = this.retryTimerMap.get(fileId);
116
+ if (timer) {
117
+ clearTimeout(timer);
118
+ this.retryTimerMap.delete(fileId);
119
+ }
120
+ this.retryCountMap.delete(fileId);
121
+ }
122
+ };
123
+
124
+ exports.SmartRetryPlugin = SmartRetryPlugin;
125
+ //# sourceMappingURL=out.js.map
126
+ //# sourceMappingURL=index.js.map