@taicode/common-base 3.0.1 → 3.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.
Files changed (110) hide show
  1. package/output/batching-buffer/batching-buffer.test.js +1 -1
  2. package/output/batching-buffer/index.d.ts +2 -2
  3. package/output/batching-buffer/index.d.ts.map +1 -1
  4. package/output/batching-buffer/index.js +1 -1
  5. package/output/catch/catch.test.js +1 -1
  6. package/output/catch/index.d.ts +1 -1
  7. package/output/catch/index.d.ts.map +1 -1
  8. package/output/catch/index.js +1 -1
  9. package/output/color/color.d.ts.map +1 -1
  10. package/output/color/color.js +0 -1
  11. package/output/color/color.test.js +1 -1
  12. package/output/color/index.d.ts +1 -1
  13. package/output/color/index.d.ts.map +1 -1
  14. package/output/color/index.js +1 -1
  15. package/output/debounce/debounce.test.js +1 -1
  16. package/output/debounce/index.d.ts +1 -1
  17. package/output/debounce/index.d.ts.map +1 -1
  18. package/output/debounce/index.js +1 -1
  19. package/output/disposer/disposer.d.ts.map +1 -1
  20. package/output/disposer/disposer.js +2 -1
  21. package/output/disposer/disposer.test.js +1 -1
  22. package/output/disposer/index.d.ts +1 -1
  23. package/output/disposer/index.d.ts.map +1 -1
  24. package/output/disposer/index.js +1 -1
  25. package/output/error/error.test.js +1 -1
  26. package/output/error/index.d.ts +2 -2
  27. package/output/error/index.d.ts.map +1 -1
  28. package/output/error/index.js +1 -1
  29. package/output/event-emitter/event-emitter.test.js +1 -1
  30. package/output/event-emitter/index.d.ts +1 -1
  31. package/output/event-emitter/index.d.ts.map +1 -1
  32. package/output/event-emitter/index.js +1 -1
  33. package/output/flow-queue/flow-queue.d.ts.map +1 -1
  34. package/output/flow-queue/flow-queue.js +4 -5
  35. package/output/flow-queue/flow-queue.test.js +2 -2
  36. package/output/flow-queue/index.d.ts +2 -2
  37. package/output/flow-queue/index.d.ts.map +1 -1
  38. package/output/flow-queue/index.js +1 -1
  39. package/output/index.d.ts +17 -17
  40. package/output/index.d.ts.map +1 -1
  41. package/output/index.js +17 -17
  42. package/output/logger/formatter.js +1 -1
  43. package/output/logger/formatter.test.js +3 -3
  44. package/output/logger/index.d.ts +3 -3
  45. package/output/logger/index.d.ts.map +1 -1
  46. package/output/logger/index.js +3 -3
  47. package/output/logger/logger.d.ts +2 -2
  48. package/output/logger/logger.d.ts.map +1 -1
  49. package/output/logger/logger.js +1 -1
  50. package/output/logger/logger.test.js +4 -4
  51. package/output/logger/transport.d.ts +1 -1
  52. package/output/logger/transport.d.ts.map +1 -1
  53. package/output/logger/transport.js +1 -1
  54. package/output/logger/transport.test.js +2 -2
  55. package/output/lru-cache/index.d.ts +1 -1
  56. package/output/lru-cache/index.d.ts.map +1 -1
  57. package/output/lru-cache/index.js +1 -1
  58. package/output/lru-cache/lru-cache.test.js +1 -1
  59. package/output/number/index.d.ts +1 -1
  60. package/output/number/index.d.ts.map +1 -1
  61. package/output/number/index.js +1 -1
  62. package/output/number/number.test.js +1 -1
  63. package/output/object/index.d.ts +1 -1
  64. package/output/object/index.d.ts.map +1 -1
  65. package/output/object/index.js +1 -1
  66. package/output/object/object.test.js +1 -1
  67. package/output/ring-cache/index.d.ts +1 -1
  68. package/output/ring-cache/index.d.ts.map +1 -1
  69. package/output/ring-cache/index.js +1 -1
  70. package/output/ring-cache/ring-cache.test.js +1 -1
  71. package/output/scheduler/index.d.ts +2 -0
  72. package/output/scheduler/index.d.ts.map +1 -0
  73. package/output/scheduler/index.js +1 -0
  74. package/output/scheduler/scheduler.d.ts +222 -0
  75. package/output/scheduler/scheduler.d.ts.map +1 -0
  76. package/output/scheduler/scheduler.js +348 -0
  77. package/output/scheduler/scheduler.test.d.ts +2 -0
  78. package/output/scheduler/scheduler.test.d.ts.map +1 -0
  79. package/output/scheduler/scheduler.test.js +341 -0
  80. package/output/service/index.d.ts +1 -1
  81. package/output/service/index.d.ts.map +1 -1
  82. package/output/service/index.js +1 -1
  83. package/output/string/index.d.ts +1 -1
  84. package/output/string/index.d.ts.map +1 -1
  85. package/output/string/index.js +1 -1
  86. package/output/string/string.test.js +1 -1
  87. package/output/throttle/index.d.ts +1 -1
  88. package/output/throttle/index.d.ts.map +1 -1
  89. package/output/throttle/index.js +1 -1
  90. package/output/throttle/throttle.test.js +1 -1
  91. package/output/ttl-cache/index.d.ts +1 -1
  92. package/output/ttl-cache/index.d.ts.map +1 -1
  93. package/output/ttl-cache/index.js +1 -1
  94. package/output/ttl-cache/ttl-cache.test.js +1 -1
  95. package/package.json +7 -5
  96. package/output/events/disposer.d.ts +0 -6
  97. package/output/events/disposer.d.ts.map +0 -1
  98. package/output/events/disposer.js +0 -19
  99. package/output/events/disposer.test.d.ts +0 -2
  100. package/output/events/disposer.test.d.ts.map +0 -1
  101. package/output/events/disposer.test.js +0 -192
  102. package/output/events/event-emitter.d.ts +0 -33
  103. package/output/events/event-emitter.d.ts.map +0 -1
  104. package/output/events/event-emitter.js +0 -66
  105. package/output/events/event-emitter.test.d.ts +0 -2
  106. package/output/events/event-emitter.test.d.ts.map +0 -1
  107. package/output/events/event-emitter.test.js +0 -213
  108. package/output/events/index.d.ts +0 -3
  109. package/output/events/index.d.ts.map +0 -1
  110. package/output/events/index.js +0 -3
@@ -2,7 +2,7 @@
2
2
  * RingCache 测试文件
3
3
  */
4
4
  import { describe, it, expect, beforeEach } from 'vitest';
5
- import { RingCache } from './ring-cache';
5
+ import { RingCache } from './ring-cache.js';
6
6
  describe('RingCache', () => {
7
7
  let cache;
8
8
  beforeEach(() => {
@@ -0,0 +1,2 @@
1
+ export * from './scheduler';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../source/scheduler/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAA"}
@@ -0,0 +1 @@
1
+ export * from './scheduler';
@@ -0,0 +1,222 @@
1
+ import { EventEmitter } from '../event-emitter';
2
+ import { type CatchResult } from '../catch';
3
+ /**
4
+ * 定时任务配置
5
+ */
6
+ export interface ScheduledTaskConfig {
7
+ /**
8
+ * 任务唯一标识
9
+ */
10
+ id: string;
11
+ /**
12
+ * 任务执行间隔(秒),必须是 10 的倍数
13
+ */
14
+ interval: number;
15
+ /**
16
+ * 任务处理函数
17
+ */
18
+ handler: () => Promise<void> | void;
19
+ /**
20
+ * 最大重试次数,默认 3
21
+ */
22
+ maxRetries?: number;
23
+ /**
24
+ * 重试间隔(毫秒),默认 1000
25
+ */
26
+ retryDelay?: number;
27
+ /**
28
+ * 是否在启动时立即执行一次,默认 false
29
+ */
30
+ immediate?: boolean;
31
+ /**
32
+ * 是否启用,默认 true
33
+ */
34
+ enabled?: boolean;
35
+ }
36
+ /**
37
+ * 任务执行记录
38
+ */
39
+ export interface TaskExecution {
40
+ taskId: string;
41
+ startTime: number;
42
+ endTime?: number;
43
+ success: boolean;
44
+ error?: Error;
45
+ retryCount: number;
46
+ }
47
+ /**
48
+ * 调度器事件
49
+ */
50
+ export interface SchedulerEvents {
51
+ 'task:start': {
52
+ taskId: string;
53
+ };
54
+ 'task:success': {
55
+ taskId: string;
56
+ duration: number;
57
+ };
58
+ 'task:error': {
59
+ taskId: string;
60
+ error: Error;
61
+ retryCount: number;
62
+ };
63
+ 'task:retry': {
64
+ taskId: string;
65
+ retryCount: number;
66
+ maxRetries: number;
67
+ };
68
+ 'task:failed': {
69
+ taskId: string;
70
+ error: Error;
71
+ };
72
+ 'scheduler:start': void;
73
+ 'scheduler:stop': void;
74
+ 'scheduler:tick': {
75
+ tick: number;
76
+ };
77
+ }
78
+ /**
79
+ * 内部任务状态
80
+ */
81
+ interface TaskState {
82
+ config: Required<ScheduledTaskConfig>;
83
+ nextTick: number;
84
+ running: boolean;
85
+ retryCount: number;
86
+ lastError?: Error;
87
+ executions: TaskExecution[];
88
+ }
89
+ /**
90
+ * 定时任务调度器
91
+ *
92
+ * 以 10 秒为基本时间单位,支持多个不同间隔的定时任务
93
+ *
94
+ * @example
95
+ * ```ts
96
+ * const scheduler = new Scheduler()
97
+ *
98
+ * // 添加每 30 秒执行一次的任务
99
+ * scheduler.addTask({
100
+ * id: 'sync-data',
101
+ * interval: 30,
102
+ * handler: async () => {
103
+ * await syncDataToServer()
104
+ * },
105
+ * maxRetries: 3
106
+ * })
107
+ *
108
+ * // 添加每 60 秒执行一次的任务
109
+ * scheduler.addTask({
110
+ * id: 'cleanup',
111
+ * interval: 60,
112
+ * handler: async () => {
113
+ * await cleanupTempFiles()
114
+ * }
115
+ * })
116
+ *
117
+ * // 启动调度器
118
+ * scheduler.start()
119
+ *
120
+ * // 监听事件
121
+ * scheduler.on('task:error', (taskId, error) => {
122
+ * console.error(`Task ${taskId} failed:`, error)
123
+ * })
124
+ *
125
+ * // 停止调度器
126
+ * scheduler.stop()
127
+ * ```
128
+ */
129
+ export declare class Scheduler extends EventEmitter<SchedulerEvents> {
130
+ /**
131
+ * 基本时间单位(秒)
132
+ */
133
+ private static readonly TICK_INTERVAL;
134
+ /**
135
+ * 当前 tick 计数
136
+ */
137
+ private currentTick;
138
+ /**
139
+ * 定时器 ID
140
+ */
141
+ private timerId;
142
+ /**
143
+ * 是否正在运行
144
+ */
145
+ private running;
146
+ /**
147
+ * 任务状态映射
148
+ */
149
+ private tasks;
150
+ /**
151
+ * 添加定时任务
152
+ */
153
+ addTask(config: ScheduledTaskConfig): void;
154
+ /**
155
+ * 移除定时任务
156
+ */
157
+ removeTask(taskId: string): boolean;
158
+ /**
159
+ * 启用任务
160
+ */
161
+ enableTask(taskId: string): boolean;
162
+ /**
163
+ * 禁用任务
164
+ */
165
+ disableTask(taskId: string): boolean;
166
+ /**
167
+ * 获取任务状态
168
+ */
169
+ getTaskState(taskId: string): Readonly<TaskState> | undefined;
170
+ /**
171
+ * 获取所有任务 ID
172
+ */
173
+ getTaskIds(): string[];
174
+ /**
175
+ * 启动调度器
176
+ */
177
+ start(): void;
178
+ /**
179
+ * 停止调度器
180
+ */
181
+ stop(): void;
182
+ /**
183
+ * 是否正在运行
184
+ */
185
+ isRunning(): boolean;
186
+ /**
187
+ * 获取当前 tick
188
+ */
189
+ getCurrentTick(): number;
190
+ /**
191
+ * 手动触发任务执行(忽略时间间隔)
192
+ */
193
+ triggerTask(taskId: string): Promise<CatchResult<void, string>>;
194
+ /**
195
+ * 执行 tick
196
+ */
197
+ private tick;
198
+ /**
199
+ * 执行任务
200
+ */
201
+ private executeTask;
202
+ /**
203
+ * 获取任务执行历史
204
+ */
205
+ getTaskExecutions(taskId: string, limit?: number): TaskExecution[];
206
+ /**
207
+ * 获取调度器统计信息
208
+ */
209
+ getStats(): {
210
+ running: boolean;
211
+ currentTick: number;
212
+ totalTasks: number;
213
+ enabledTasks: number;
214
+ runningTasks: number;
215
+ };
216
+ /**
217
+ * 清理所有任务
218
+ */
219
+ clear(): void;
220
+ }
221
+ export {};
222
+ //# sourceMappingURL=scheduler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scheduler.d.ts","sourceRoot":"","sources":["../../source/scheduler/scheduler.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,348 @@
1
+ import { EventEmitter } from '../event-emitter';
2
+ import { catchIt } from '../catch';
3
+ /**
4
+ * 定时任务调度器
5
+ *
6
+ * 以 10 秒为基本时间单位,支持多个不同间隔的定时任务
7
+ *
8
+ * @example
9
+ * ```ts
10
+ * const scheduler = new Scheduler()
11
+ *
12
+ * // 添加每 30 秒执行一次的任务
13
+ * scheduler.addTask({
14
+ * id: 'sync-data',
15
+ * interval: 30,
16
+ * handler: async () => {
17
+ * await syncDataToServer()
18
+ * },
19
+ * maxRetries: 3
20
+ * })
21
+ *
22
+ * // 添加每 60 秒执行一次的任务
23
+ * scheduler.addTask({
24
+ * id: 'cleanup',
25
+ * interval: 60,
26
+ * handler: async () => {
27
+ * await cleanupTempFiles()
28
+ * }
29
+ * })
30
+ *
31
+ * // 启动调度器
32
+ * scheduler.start()
33
+ *
34
+ * // 监听事件
35
+ * scheduler.on('task:error', (taskId, error) => {
36
+ * console.error(`Task ${taskId} failed:`, error)
37
+ * })
38
+ *
39
+ * // 停止调度器
40
+ * scheduler.stop()
41
+ * ```
42
+ */
43
+ export class Scheduler extends EventEmitter {
44
+ /**
45
+ * 基本时间单位(秒)
46
+ */
47
+ static TICK_INTERVAL = 10;
48
+ /**
49
+ * 当前 tick 计数
50
+ */
51
+ currentTick = 0;
52
+ /**
53
+ * 定时器 ID
54
+ */
55
+ timerId = null;
56
+ /**
57
+ * 是否正在运行
58
+ */
59
+ running = false;
60
+ /**
61
+ * 任务状态映射
62
+ */
63
+ tasks = new Map();
64
+ /**
65
+ * 添加定时任务
66
+ */
67
+ addTask(config) {
68
+ // 验证参数
69
+ if (!config.id) {
70
+ throw new Error('[Scheduler] Task id is required');
71
+ }
72
+ if (!config.interval || config.interval <= 0) {
73
+ throw new Error('[Scheduler] Task interval must be positive');
74
+ }
75
+ if (config.interval % Scheduler.TICK_INTERVAL !== 0) {
76
+ throw new Error(`[Scheduler] Task interval must be a multiple of ${Scheduler.TICK_INTERVAL} seconds`);
77
+ }
78
+ if (!config.handler) {
79
+ throw new Error('[Scheduler] Task handler is required');
80
+ }
81
+ if (this.tasks.has(config.id)) {
82
+ throw new Error(`[Scheduler] Task with id "${config.id}" already exists`);
83
+ }
84
+ // 创建任务状态
85
+ const taskState = {
86
+ config: {
87
+ id: config.id,
88
+ interval: config.interval,
89
+ handler: config.handler,
90
+ maxRetries: config.maxRetries ?? 3,
91
+ retryDelay: config.retryDelay ?? 1000,
92
+ immediate: config.immediate ?? false,
93
+ enabled: config.enabled ?? true,
94
+ },
95
+ nextTick: config.immediate ? 0 : Math.ceil(config.interval / Scheduler.TICK_INTERVAL),
96
+ running: false,
97
+ retryCount: 0,
98
+ executions: [],
99
+ };
100
+ this.tasks.set(config.id, taskState);
101
+ console.log(`[Scheduler] Task "${config.id}" added (interval: ${config.interval}s)`);
102
+ }
103
+ /**
104
+ * 移除定时任务
105
+ */
106
+ removeTask(taskId) {
107
+ const task = this.tasks.get(taskId);
108
+ if (!task) {
109
+ return false;
110
+ }
111
+ if (task.running) {
112
+ console.warn(`[Scheduler] Cannot remove running task "${taskId}"`);
113
+ return false;
114
+ }
115
+ this.tasks.delete(taskId);
116
+ console.log(`[Scheduler] Task "${taskId}" removed`);
117
+ return true;
118
+ }
119
+ /**
120
+ * 启用任务
121
+ */
122
+ enableTask(taskId) {
123
+ const task = this.tasks.get(taskId);
124
+ if (!task) {
125
+ return false;
126
+ }
127
+ task.config.enabled = true;
128
+ console.log(`[Scheduler] Task "${taskId}" enabled`);
129
+ return true;
130
+ }
131
+ /**
132
+ * 禁用任务
133
+ */
134
+ disableTask(taskId) {
135
+ const task = this.tasks.get(taskId);
136
+ if (!task) {
137
+ return false;
138
+ }
139
+ task.config.enabled = false;
140
+ console.log(`[Scheduler] Task "${taskId}" disabled`);
141
+ return true;
142
+ }
143
+ /**
144
+ * 获取任务状态
145
+ */
146
+ getTaskState(taskId) {
147
+ const task = this.tasks.get(taskId);
148
+ return task ? { ...task } : undefined;
149
+ }
150
+ /**
151
+ * 获取所有任务 ID
152
+ */
153
+ getTaskIds() {
154
+ return Array.from(this.tasks.keys());
155
+ }
156
+ /**
157
+ * 启动调度器
158
+ */
159
+ start() {
160
+ if (this.running) {
161
+ console.warn('[Scheduler] Scheduler is already running');
162
+ return;
163
+ }
164
+ this.running = true;
165
+ this.currentTick = 0;
166
+ this.emit('scheduler:start', undefined);
167
+ console.log('[Scheduler] Scheduler started');
168
+ // 立即执行一次 tick
169
+ this.tick();
170
+ // 设置定时器
171
+ this.timerId = setInterval(() => {
172
+ this.tick();
173
+ }, Scheduler.TICK_INTERVAL * 1000);
174
+ }
175
+ /**
176
+ * 停止调度器
177
+ */
178
+ stop() {
179
+ if (!this.running) {
180
+ return;
181
+ }
182
+ if (this.timerId) {
183
+ clearInterval(this.timerId);
184
+ this.timerId = null;
185
+ }
186
+ this.running = false;
187
+ this.emit('scheduler:stop', undefined);
188
+ console.log('[Scheduler] Scheduler stopped');
189
+ }
190
+ /**
191
+ * 是否正在运行
192
+ */
193
+ isRunning() {
194
+ return this.running;
195
+ }
196
+ /**
197
+ * 获取当前 tick
198
+ */
199
+ getCurrentTick() {
200
+ return this.currentTick;
201
+ }
202
+ /**
203
+ * 手动触发任务执行(忽略时间间隔)
204
+ */
205
+ async triggerTask(taskId) {
206
+ const task = this.tasks.get(taskId);
207
+ if (!task) {
208
+ return catchIt(async () => {
209
+ throw new Error(`Task "${taskId}" not found`);
210
+ });
211
+ }
212
+ return this.executeTask(task);
213
+ }
214
+ /**
215
+ * 执行 tick
216
+ */
217
+ tick() {
218
+ this.currentTick++;
219
+ this.emit('scheduler:tick', { tick: this.currentTick });
220
+ // 检查需要执行的任务
221
+ for (const task of this.tasks.values()) {
222
+ if (!task.config.enabled) {
223
+ continue;
224
+ }
225
+ if (task.running) {
226
+ continue;
227
+ }
228
+ // 检查是否到达执行时间
229
+ if (this.currentTick >= task.nextTick) {
230
+ // 异步执行任务,不阻塞 tick
231
+ this.executeTask(task).catch((error) => {
232
+ console.error(`[Scheduler] Unexpected error in task "${task.config.id}":`, error);
233
+ });
234
+ }
235
+ }
236
+ }
237
+ /**
238
+ * 执行任务
239
+ */
240
+ async executeTask(task) {
241
+ const { config } = task;
242
+ const startTime = Date.now();
243
+ task.running = true;
244
+ this.emit('task:start', { taskId: config.id });
245
+ const execution = {
246
+ taskId: config.id,
247
+ startTime,
248
+ success: false,
249
+ retryCount: task.retryCount,
250
+ };
251
+ const result = await catchIt(async () => config.handler());
252
+ const endTime = Date.now();
253
+ const duration = endTime - startTime;
254
+ execution.endTime = endTime;
255
+ if (result.isError()) {
256
+ // 执行失败
257
+ const error = new Error(String(result.error));
258
+ execution.success = false;
259
+ execution.error = error;
260
+ task.lastError = error;
261
+ task.retryCount++;
262
+ this.emit('task:error', { taskId: config.id, error, retryCount: task.retryCount });
263
+ // 检查是否需要重试
264
+ if (task.retryCount <= config.maxRetries) {
265
+ this.emit('task:retry', {
266
+ taskId: config.id,
267
+ retryCount: task.retryCount,
268
+ maxRetries: config.maxRetries,
269
+ });
270
+ console.warn(`[Scheduler] Task "${config.id}" failed, will retry (${task.retryCount}/${config.maxRetries})`);
271
+ // 延迟后重试
272
+ await new Promise((resolve) => setTimeout(resolve, config.retryDelay));
273
+ task.running = false;
274
+ return this.executeTask(task);
275
+ }
276
+ else {
277
+ // 超过最大重试次数
278
+ this.emit('task:failed', { taskId: config.id, error });
279
+ console.error(`[Scheduler] Task "${config.id}" failed after ${config.maxRetries} retries:`, error.message);
280
+ // 重置重试计数,计划下次执行
281
+ task.retryCount = 0;
282
+ task.nextTick = this.currentTick + Math.ceil(config.interval / Scheduler.TICK_INTERVAL);
283
+ task.running = false;
284
+ task.executions.push(execution);
285
+ return result;
286
+ }
287
+ }
288
+ else {
289
+ // 执行成功
290
+ execution.success = true;
291
+ task.retryCount = 0;
292
+ task.lastError = undefined;
293
+ this.emit('task:success', { taskId: config.id, duration });
294
+ // 计划下次执行
295
+ task.nextTick = this.currentTick + Math.ceil(config.interval / Scheduler.TICK_INTERVAL);
296
+ task.running = false;
297
+ task.executions.push(execution);
298
+ // 保留最近 100 条执行记录
299
+ if (task.executions.length > 100) {
300
+ task.executions = task.executions.slice(-100);
301
+ }
302
+ return result;
303
+ }
304
+ }
305
+ /**
306
+ * 获取任务执行历史
307
+ */
308
+ getTaskExecutions(taskId, limit = 10) {
309
+ const task = this.tasks.get(taskId);
310
+ if (!task) {
311
+ return [];
312
+ }
313
+ return task.executions.slice(-limit);
314
+ }
315
+ /**
316
+ * 获取调度器统计信息
317
+ */
318
+ getStats() {
319
+ let enabledTasks = 0;
320
+ let runningTasks = 0;
321
+ for (const task of this.tasks.values()) {
322
+ if (task.config.enabled) {
323
+ enabledTasks++;
324
+ }
325
+ if (task.running) {
326
+ runningTasks++;
327
+ }
328
+ }
329
+ return {
330
+ running: this.running,
331
+ currentTick: this.currentTick,
332
+ totalTasks: this.tasks.size,
333
+ enabledTasks,
334
+ runningTasks,
335
+ };
336
+ }
337
+ /**
338
+ * 清理所有任务
339
+ */
340
+ clear() {
341
+ if (this.running) {
342
+ console.warn('[Scheduler] Cannot clear tasks while scheduler is running');
343
+ return;
344
+ }
345
+ this.tasks.clear();
346
+ console.log('[Scheduler] All tasks cleared');
347
+ }
348
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=scheduler.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scheduler.test.d.ts","sourceRoot":"","sources":["../../source/scheduler/scheduler.test.ts"],"names":[],"mappings":""}