@kyo-services/schedulewise 1.0.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/LICENSE +21 -0
- package/README.md +194 -0
- package/dist/Task.d.ts +74 -0
- package/dist/Task.js +105 -0
- package/dist/Task.js.map +1 -0
- package/dist/TaskScheduler.d.ts +55 -0
- package/dist/TaskScheduler.js +110 -0
- package/dist/TaskScheduler.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/types.d.ts +22 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +66 -0
- package/src/Task.ts +111 -0
- package/src/TaskScheduler.ts +130 -0
- package/src/__tests__/TaskCreation.test.ts +46 -0
- package/src/__tests__/TaskExecution.test.ts +69 -0
- package/src/__tests__/TaskManagement.test.ts +75 -0
- package/src/__tests__/testUtils.ts +38 -0
- package/src/index.ts +4 -0
- package/src/types.ts +23 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Timed Job
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
# ScheduleWise
|
|
2
|
+
|
|
3
|
+
[](https://opensource.org/licenses/MIT)
|
|
4
|
+
[](https://www.typescriptlang.org/)
|
|
5
|
+
[](https://nodejs.org/)
|
|
6
|
+
[](https://browsersl.ist)
|
|
7
|
+
|
|
8
|
+
A smart and efficient task scheduler for managing periodic jobs with precision timing and intelligent scheduling in both browser and Node.js environments. Built with modern TypeScript features and following best practices for reliable task scheduling.
|
|
9
|
+
|
|
10
|
+
## 🚀 Features
|
|
11
|
+
|
|
12
|
+
- **Singleton Pattern**: Ensures single scheduler instance across your application
|
|
13
|
+
- **Type-Safe**: Full TypeScript support with type definitions
|
|
14
|
+
- **Flexible Scheduling**: Custom intervals and immediate execution options
|
|
15
|
+
- **Task Management**: Enable, disable, or remove tasks on demand
|
|
16
|
+
- **Execution History**: Track task execution count and timestamps
|
|
17
|
+
- **Zero Dependencies**: No external runtime dependencies
|
|
18
|
+
- **Well Tested**: Comprehensive test coverage
|
|
19
|
+
|
|
20
|
+
## 📦 Installation
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npm install @kyo-services/schedulewise
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## 🔧 Usage
|
|
27
|
+
|
|
28
|
+
### Basic Task Scheduling
|
|
29
|
+
|
|
30
|
+
```typescript
|
|
31
|
+
import sw from '@kyo-services/schedulewise';
|
|
32
|
+
|
|
33
|
+
// Create a periodic task
|
|
34
|
+
const task = sw.scheduleTask(
|
|
35
|
+
(currentTime: Date, executionCount: number) => {
|
|
36
|
+
console.log(`Task executed at ${currentTime}, count: ${executionCount}`);
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
interval: 1000, // Run every second
|
|
40
|
+
name: 'logTask', // Optional task identifier
|
|
41
|
+
immediate: true // Run immediately when created
|
|
42
|
+
}
|
|
43
|
+
);
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### One-Time Task
|
|
47
|
+
|
|
48
|
+
```typescript
|
|
49
|
+
sw.scheduleTask(
|
|
50
|
+
() => {
|
|
51
|
+
console.log('This task runs only once after 5 seconds');
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
interval: 5000,
|
|
55
|
+
once: true
|
|
56
|
+
}
|
|
57
|
+
);
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Task Management
|
|
61
|
+
|
|
62
|
+
```typescript
|
|
63
|
+
// Find task by name or ID
|
|
64
|
+
const task = sw.findTask('logTask');
|
|
65
|
+
|
|
66
|
+
// Disable task
|
|
67
|
+
task?.disable();
|
|
68
|
+
|
|
69
|
+
// Enable task
|
|
70
|
+
task?.enable();
|
|
71
|
+
|
|
72
|
+
// Update task configuration
|
|
73
|
+
task?.update({
|
|
74
|
+
interval: 2000,
|
|
75
|
+
immediate: false
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
// Remove specific task
|
|
79
|
+
task?.remove();
|
|
80
|
+
// or
|
|
81
|
+
sw.removeTask('logTask');
|
|
82
|
+
|
|
83
|
+
// Remove all tasks
|
|
84
|
+
sw.clearAllTasks();
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## 📚 API Reference
|
|
88
|
+
|
|
89
|
+
### TaskOptions
|
|
90
|
+
|
|
91
|
+
```typescript
|
|
92
|
+
interface TaskOptions {
|
|
93
|
+
interval: number; // Interval in milliseconds
|
|
94
|
+
name?: string; // Optional task identifier
|
|
95
|
+
immediate?: boolean; // Run immediately when created
|
|
96
|
+
once?: boolean; // Run only once
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Task Methods
|
|
101
|
+
|
|
102
|
+
| Method | Description |
|
|
103
|
+
|--------|-------------|
|
|
104
|
+
| `disable()` | Temporarily disables the task |
|
|
105
|
+
| `enable()` | Re-enables a disabled task |
|
|
106
|
+
| `update(options)` | Updates task configuration |
|
|
107
|
+
| `remove()` | Removes the task from scheduler |
|
|
108
|
+
|
|
109
|
+
### Task Properties
|
|
110
|
+
|
|
111
|
+
| Property | Type | Description |
|
|
112
|
+
|----------|------|-------------|
|
|
113
|
+
| `id` | `number` | Unique task identifier |
|
|
114
|
+
| `name` | `string \| undefined` | Optional task name |
|
|
115
|
+
| `lastExecutionTime` | `Date` | Last execution timestamp |
|
|
116
|
+
| `executionCount` | `number` | Number of executions |
|
|
117
|
+
| `isEnabled` | `boolean` | Current enabled status |
|
|
118
|
+
|
|
119
|
+
### Scheduler Methods
|
|
120
|
+
|
|
121
|
+
| Method | Description |
|
|
122
|
+
|--------|-------------|
|
|
123
|
+
| `scheduleTask(callback, options)` | Creates a new task |
|
|
124
|
+
| `findTask(identifier)` | Finds task by ID or name |
|
|
125
|
+
| `removeTask(identifier)` | Removes a specific task |
|
|
126
|
+
| `clearAllTasks()` | Removes all tasks |
|
|
127
|
+
| `getAllTasks()` | Gets all scheduled tasks |
|
|
128
|
+
|
|
129
|
+
## 🧪 Testing
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
# Run tests
|
|
133
|
+
npm test
|
|
134
|
+
|
|
135
|
+
# Run tests with coverage
|
|
136
|
+
npm test -- --coverage
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## 🤝 Contributing
|
|
140
|
+
|
|
141
|
+
1. Fork the repository
|
|
142
|
+
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
|
|
143
|
+
3. Commit your changes using conventional commits (`git commit -m 'feat: add amazing feature'`)
|
|
144
|
+
4. Push to the branch (`git push origin feature/amazing-feature`)
|
|
145
|
+
5. Open a Pull Request
|
|
146
|
+
|
|
147
|
+
## 📝 License
|
|
148
|
+
|
|
149
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
150
|
+
|
|
151
|
+
## ⚡ Best Practices
|
|
152
|
+
|
|
153
|
+
1. **Task Naming**: Use descriptive names for better task management
|
|
154
|
+
```typescript
|
|
155
|
+
sw.scheduleTask(sendEmail, { name: 'dailyNewsletterTask' });
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
2. **Error Handling**: Always handle task errors
|
|
159
|
+
```typescript
|
|
160
|
+
sw.scheduleTask(async () => {
|
|
161
|
+
try {
|
|
162
|
+
await riskyOperation();
|
|
163
|
+
} catch (error) {
|
|
164
|
+
console.error('Task failed:', error);
|
|
165
|
+
}
|
|
166
|
+
}, { interval: 1000 });
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
3. **Resource Cleanup**: Remove tasks when no longer needed
|
|
170
|
+
```typescript
|
|
171
|
+
// In component unmount or cleanup
|
|
172
|
+
task.remove();
|
|
173
|
+
// or for all tasks
|
|
174
|
+
sw.clearAllTasks();
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
4. **Interval Selection**: Choose appropriate intervals
|
|
178
|
+
```typescript
|
|
179
|
+
// Good: Clear intention
|
|
180
|
+
const MINUTE = 60 * 1000;
|
|
181
|
+
sw.scheduleTask(task, { interval: 5 * MINUTE });
|
|
182
|
+
|
|
183
|
+
// Avoid: Magic numbers
|
|
184
|
+
sw.scheduleTask(task, { interval: 300000 });
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
5. **Type Safety**: Leverage TypeScript types
|
|
188
|
+
```typescript
|
|
189
|
+
import type { TaskCallback, TaskOptions } from '@kyo-services/schedulewise';
|
|
190
|
+
|
|
191
|
+
const callback: TaskCallback = (time: Date, count: number) => {
|
|
192
|
+
// Type-safe callback
|
|
193
|
+
};
|
|
194
|
+
```
|
package/dist/Task.d.ts
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { TaskCallback } from './types';
|
|
2
|
+
import { TaskScheduler } from './TaskScheduler';
|
|
3
|
+
/**
|
|
4
|
+
* Represents a scheduled task in the system
|
|
5
|
+
* @class Task
|
|
6
|
+
*/
|
|
7
|
+
export declare class Task {
|
|
8
|
+
readonly name: string | undefined;
|
|
9
|
+
readonly id: number;
|
|
10
|
+
private callback;
|
|
11
|
+
private interval;
|
|
12
|
+
private executeImmediately;
|
|
13
|
+
private isOneTime;
|
|
14
|
+
private scheduler;
|
|
15
|
+
lastExecutionTime: Date;
|
|
16
|
+
executionCount: number;
|
|
17
|
+
isEnabled: boolean;
|
|
18
|
+
/**
|
|
19
|
+
* Creates a new Task instance
|
|
20
|
+
* @param {TaskScheduler} scheduler - Reference to the scheduler instance
|
|
21
|
+
* @param {string} [name] - Optional name to identify the task
|
|
22
|
+
* @param {number} id - Unique identifier for the task
|
|
23
|
+
* @param {TaskCallback} callback - Function to be executed
|
|
24
|
+
* @param {number} interval - Time interval in milliseconds between executions
|
|
25
|
+
* @param {boolean} executeImmediately - Whether to execute immediately
|
|
26
|
+
* @param {boolean} isOneTime - Whether to execute only once
|
|
27
|
+
*/
|
|
28
|
+
constructor(scheduler: TaskScheduler, name: string | undefined, id: number, callback: TaskCallback, interval: number, executeImmediately: boolean, isOneTime: boolean);
|
|
29
|
+
/**
|
|
30
|
+
* Executes the task callback
|
|
31
|
+
* @param {Date} currentTime - Current timestamp
|
|
32
|
+
*/
|
|
33
|
+
execute(currentTime: Date): void;
|
|
34
|
+
/**
|
|
35
|
+
* Enables the task
|
|
36
|
+
* @returns {Task} The task instance for chaining
|
|
37
|
+
*/
|
|
38
|
+
enable(): Task;
|
|
39
|
+
/**
|
|
40
|
+
* Disables the task
|
|
41
|
+
* @returns {Task} The task instance for chaining
|
|
42
|
+
*/
|
|
43
|
+
disable(): Task;
|
|
44
|
+
/**
|
|
45
|
+
* Updates the task configuration
|
|
46
|
+
* @param {TaskOptions} options - New configuration options
|
|
47
|
+
* @param {TaskCallback} [newCallback] - Optional new callback function
|
|
48
|
+
* @returns {Task} The task instance for chaining
|
|
49
|
+
*/
|
|
50
|
+
update(options: {
|
|
51
|
+
interval: number;
|
|
52
|
+
immediate?: boolean;
|
|
53
|
+
}, newCallback?: TaskCallback): Task;
|
|
54
|
+
/**
|
|
55
|
+
* Removes the task from the scheduler
|
|
56
|
+
*/
|
|
57
|
+
remove(): void;
|
|
58
|
+
/**
|
|
59
|
+
* Gets the task's interval
|
|
60
|
+
* @returns {number} The interval in milliseconds
|
|
61
|
+
*/
|
|
62
|
+
getInterval(): number;
|
|
63
|
+
/**
|
|
64
|
+
* Checks if the task is ready to execute
|
|
65
|
+
* @param {number} elapsedTime - Time elapsed since last execution
|
|
66
|
+
* @returns {boolean} Whether the task should execute
|
|
67
|
+
*/
|
|
68
|
+
shouldExecute(elapsedTime: number): boolean;
|
|
69
|
+
/**
|
|
70
|
+
* Checks if the task should be removed after execution
|
|
71
|
+
* @returns {boolean} Whether the task is one-time
|
|
72
|
+
*/
|
|
73
|
+
shouldRemove(): boolean;
|
|
74
|
+
}
|
package/dist/Task.js
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Represents a scheduled task in the system
|
|
3
|
+
* @class Task
|
|
4
|
+
*/
|
|
5
|
+
export class Task {
|
|
6
|
+
name;
|
|
7
|
+
id;
|
|
8
|
+
callback;
|
|
9
|
+
interval;
|
|
10
|
+
executeImmediately;
|
|
11
|
+
isOneTime;
|
|
12
|
+
scheduler;
|
|
13
|
+
lastExecutionTime;
|
|
14
|
+
executionCount;
|
|
15
|
+
isEnabled;
|
|
16
|
+
/**
|
|
17
|
+
* Creates a new Task instance
|
|
18
|
+
* @param {TaskScheduler} scheduler - Reference to the scheduler instance
|
|
19
|
+
* @param {string} [name] - Optional name to identify the task
|
|
20
|
+
* @param {number} id - Unique identifier for the task
|
|
21
|
+
* @param {TaskCallback} callback - Function to be executed
|
|
22
|
+
* @param {number} interval - Time interval in milliseconds between executions
|
|
23
|
+
* @param {boolean} executeImmediately - Whether to execute immediately
|
|
24
|
+
* @param {boolean} isOneTime - Whether to execute only once
|
|
25
|
+
*/
|
|
26
|
+
constructor(scheduler, name, id, callback, interval, executeImmediately, isOneTime) {
|
|
27
|
+
this.name = name;
|
|
28
|
+
this.id = id;
|
|
29
|
+
this.callback = callback;
|
|
30
|
+
this.interval = interval;
|
|
31
|
+
this.executeImmediately = executeImmediately;
|
|
32
|
+
this.isOneTime = isOneTime;
|
|
33
|
+
this.scheduler = scheduler;
|
|
34
|
+
this.executionCount = 0;
|
|
35
|
+
this.isEnabled = true;
|
|
36
|
+
this.lastExecutionTime = new Date(0);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Executes the task callback
|
|
40
|
+
* @param {Date} currentTime - Current timestamp
|
|
41
|
+
*/
|
|
42
|
+
execute(currentTime) {
|
|
43
|
+
this.callback(currentTime, this.executionCount + 1);
|
|
44
|
+
this.lastExecutionTime = currentTime;
|
|
45
|
+
this.executionCount++;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Enables the task
|
|
49
|
+
* @returns {Task} The task instance for chaining
|
|
50
|
+
*/
|
|
51
|
+
enable() {
|
|
52
|
+
this.isEnabled = true;
|
|
53
|
+
return this;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Disables the task
|
|
57
|
+
* @returns {Task} The task instance for chaining
|
|
58
|
+
*/
|
|
59
|
+
disable() {
|
|
60
|
+
this.isEnabled = false;
|
|
61
|
+
return this;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Updates the task configuration
|
|
65
|
+
* @param {TaskOptions} options - New configuration options
|
|
66
|
+
* @param {TaskCallback} [newCallback] - Optional new callback function
|
|
67
|
+
* @returns {Task} The task instance for chaining
|
|
68
|
+
*/
|
|
69
|
+
update(options, newCallback) {
|
|
70
|
+
this.interval = options.interval;
|
|
71
|
+
this.executeImmediately = options.immediate || false;
|
|
72
|
+
if (newCallback)
|
|
73
|
+
this.callback = newCallback;
|
|
74
|
+
return this;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Removes the task from the scheduler
|
|
78
|
+
*/
|
|
79
|
+
remove() {
|
|
80
|
+
this.scheduler.removeTask(this.id);
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Gets the task's interval
|
|
84
|
+
* @returns {number} The interval in milliseconds
|
|
85
|
+
*/
|
|
86
|
+
getInterval() {
|
|
87
|
+
return this.interval;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Checks if the task is ready to execute
|
|
91
|
+
* @param {number} elapsedTime - Time elapsed since last execution
|
|
92
|
+
* @returns {boolean} Whether the task should execute
|
|
93
|
+
*/
|
|
94
|
+
shouldExecute(elapsedTime) {
|
|
95
|
+
return elapsedTime >= this.interval && this.isEnabled;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Checks if the task should be removed after execution
|
|
99
|
+
* @returns {boolean} Whether the task is one-time
|
|
100
|
+
*/
|
|
101
|
+
shouldRemove() {
|
|
102
|
+
return this.isOneTime;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=Task.js.map
|
package/dist/Task.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Task.js","sourceRoot":"","sources":["../src/Task.ts"],"names":[],"mappings":"AAGA;;;GAGG;AACH,MAAM,OAAO,IAAI;IAkBO;IACA;IACR;IACA;IACA;IACA;IAtBJ,SAAS,CAAgB;IAC1B,iBAAiB,CAAO;IACxB,cAAc,CAAS;IACvB,SAAS,CAAU;IAE1B;;;;;;;;;OASG;IACH,YACI,SAAwB,EACR,IAAwB,EACxB,EAAU,EAClB,QAAsB,EACtB,QAAgB,EAChB,kBAA2B,EAC3B,SAAkB;QALV,SAAI,GAAJ,IAAI,CAAoB;QACxB,OAAE,GAAF,EAAE,CAAQ;QAClB,aAAQ,GAAR,QAAQ,CAAc;QACtB,aAAQ,GAAR,QAAQ,CAAQ;QAChB,uBAAkB,GAAlB,kBAAkB,CAAS;QAC3B,cAAS,GAAT,SAAS,CAAS;QAE1B,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;QAC3B,IAAI,CAAC,cAAc,GAAG,CAAC,CAAC;QACxB,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;QACtB,IAAI,CAAC,iBAAiB,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC;IACzC,CAAC;IAED;;;OAGG;IACI,OAAO,CAAC,WAAiB;QAC5B,IAAI,CAAC,QAAQ,CAAC,WAAW,EAAE,IAAI,CAAC,cAAc,GAAG,CAAC,CAAC,CAAC;QACpD,IAAI,CAAC,iBAAiB,GAAG,WAAW,CAAC;QACrC,IAAI,CAAC,cAAc,EAAE,CAAC;IAC1B,CAAC;IAED;;;OAGG;IACI,MAAM;QACT,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;QACtB,OAAO,IAAI,CAAC;IAChB,CAAC;IAED;;;OAGG;IACI,OAAO;QACV,IAAI,CAAC,SAAS,GAAG,KAAK,CAAC;QACvB,OAAO,IAAI,CAAC;IAChB,CAAC;IAED;;;;;OAKG;IACI,MAAM,CAAC,OAAkD,EAAE,WAA0B;QACxF,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;QACjC,IAAI,CAAC,kBAAkB,GAAG,OAAO,CAAC,SAAS,IAAI,KAAK,CAAC;QACrD,IAAI,WAAW;YAAE,IAAI,CAAC,QAAQ,GAAG,WAAW,CAAC;QAC7C,OAAO,IAAI,CAAC;IAChB,CAAC;IAED;;OAEG;IACI,MAAM;QACT,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACvC,CAAC;IAED;;;OAGG;IACI,WAAW;QACd,OAAO,IAAI,CAAC,QAAQ,CAAC;IACzB,CAAC;IAED;;;;OAIG;IACI,aAAa,CAAC,WAAmB;QACpC,OAAO,WAAW,IAAI,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,SAAS,CAAC;IAC1D,CAAC;IAED;;;OAGG;IACI,YAAY;QACf,OAAO,IAAI,CAAC,SAAS,CAAC;IAC1B,CAAC;CACJ"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { Task } from './Task';
|
|
2
|
+
import { TaskCallback, TaskOptions } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* A singleton class that manages scheduled tasks in the system
|
|
5
|
+
* @class TaskScheduler
|
|
6
|
+
*/
|
|
7
|
+
export declare class TaskScheduler {
|
|
8
|
+
private static instance;
|
|
9
|
+
private currentTimestamp;
|
|
10
|
+
private scheduledTasks;
|
|
11
|
+
private schedulerInterval;
|
|
12
|
+
private constructor();
|
|
13
|
+
/**
|
|
14
|
+
* Gets the singleton instance of TaskScheduler
|
|
15
|
+
* @returns {TaskScheduler} The singleton instance
|
|
16
|
+
*/
|
|
17
|
+
static getInstance(): TaskScheduler;
|
|
18
|
+
/**
|
|
19
|
+
* Finds a task by its identifier (id or name)
|
|
20
|
+
* @param {number | string} identifier - The task's ID or name
|
|
21
|
+
* @returns {Task | undefined} The found task or undefined
|
|
22
|
+
*/
|
|
23
|
+
findTask(identifier: number | string): Task | undefined;
|
|
24
|
+
/**
|
|
25
|
+
* Gets all scheduled tasks in the system
|
|
26
|
+
* @returns {Task[]} Array of all scheduled tasks
|
|
27
|
+
*/
|
|
28
|
+
getAllTasks(): Task[];
|
|
29
|
+
/**
|
|
30
|
+
* Creates and schedules a new task
|
|
31
|
+
* @param {TaskCallback} callback - Function to be executed
|
|
32
|
+
* @param {TaskOptions} options - Configuration options for the task
|
|
33
|
+
* @returns {Task} The created task instance
|
|
34
|
+
*/
|
|
35
|
+
scheduleTask(callback: TaskCallback, options: TaskOptions): Task;
|
|
36
|
+
/**
|
|
37
|
+
* Removes a task from the scheduler
|
|
38
|
+
* @param {number | string} identifier - The task's ID or name
|
|
39
|
+
*/
|
|
40
|
+
removeTask(identifier: number | string): void;
|
|
41
|
+
/**
|
|
42
|
+
* Removes all tasks and stops the scheduler
|
|
43
|
+
*/
|
|
44
|
+
clearAllTasks(): void;
|
|
45
|
+
/**
|
|
46
|
+
* Updates current timestamp and processes tasks
|
|
47
|
+
* @internal For testing purposes
|
|
48
|
+
*/
|
|
49
|
+
processTasksNow(): void;
|
|
50
|
+
/**
|
|
51
|
+
* Starts the task processor that executes scheduled tasks
|
|
52
|
+
* @private
|
|
53
|
+
*/
|
|
54
|
+
private startTaskProcessor;
|
|
55
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { Task } from './Task';
|
|
2
|
+
/**
|
|
3
|
+
* A singleton class that manages scheduled tasks in the system
|
|
4
|
+
* @class TaskScheduler
|
|
5
|
+
*/
|
|
6
|
+
export class TaskScheduler {
|
|
7
|
+
static instance;
|
|
8
|
+
currentTimestamp;
|
|
9
|
+
scheduledTasks = new Map();
|
|
10
|
+
schedulerInterval = null;
|
|
11
|
+
constructor() {
|
|
12
|
+
this.currentTimestamp = new Date();
|
|
13
|
+
this.startTaskProcessor();
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Gets the singleton instance of TaskScheduler
|
|
17
|
+
* @returns {TaskScheduler} The singleton instance
|
|
18
|
+
*/
|
|
19
|
+
static getInstance() {
|
|
20
|
+
if (!TaskScheduler.instance)
|
|
21
|
+
TaskScheduler.instance = new TaskScheduler();
|
|
22
|
+
return TaskScheduler.instance;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Finds a task by its identifier (id or name)
|
|
26
|
+
* @param {number | string} identifier - The task's ID or name
|
|
27
|
+
* @returns {Task | undefined} The found task or undefined
|
|
28
|
+
*/
|
|
29
|
+
findTask(identifier) {
|
|
30
|
+
return this.scheduledTasks.get(identifier);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Gets all scheduled tasks in the system
|
|
34
|
+
* @returns {Task[]} Array of all scheduled tasks
|
|
35
|
+
*/
|
|
36
|
+
getAllTasks() {
|
|
37
|
+
return Array.from(this.scheduledTasks.values());
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Creates and schedules a new task
|
|
41
|
+
* @param {TaskCallback} callback - Function to be executed
|
|
42
|
+
* @param {TaskOptions} options - Configuration options for the task
|
|
43
|
+
* @returns {Task} The created task instance
|
|
44
|
+
*/
|
|
45
|
+
scheduleTask(callback, options) {
|
|
46
|
+
const taskId = Math.floor(Math.random() * 1000000);
|
|
47
|
+
const task = new Task(this, options.name, taskId, callback, options.interval, false, options.once || false);
|
|
48
|
+
this.scheduledTasks.set(taskId, task);
|
|
49
|
+
if (options.name)
|
|
50
|
+
this.scheduledTasks.set(options.name, task);
|
|
51
|
+
if (options.immediate) {
|
|
52
|
+
task.execute(this.currentTimestamp);
|
|
53
|
+
task.lastExecutionTime = this.currentTimestamp;
|
|
54
|
+
}
|
|
55
|
+
return task;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Removes a task from the scheduler
|
|
59
|
+
* @param {number | string} identifier - The task's ID or name
|
|
60
|
+
*/
|
|
61
|
+
removeTask(identifier) {
|
|
62
|
+
const task = this.findTask(identifier);
|
|
63
|
+
if (!task)
|
|
64
|
+
return;
|
|
65
|
+
this.scheduledTasks.delete(task.id);
|
|
66
|
+
if (task.name)
|
|
67
|
+
this.scheduledTasks.delete(task.name);
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Removes all tasks and stops the scheduler
|
|
71
|
+
*/
|
|
72
|
+
clearAllTasks() {
|
|
73
|
+
if (this.schedulerInterval) {
|
|
74
|
+
clearInterval(this.schedulerInterval);
|
|
75
|
+
this.schedulerInterval = null;
|
|
76
|
+
}
|
|
77
|
+
this.scheduledTasks.clear();
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Updates current timestamp and processes tasks
|
|
81
|
+
* @internal For testing purposes
|
|
82
|
+
*/
|
|
83
|
+
processTasksNow() {
|
|
84
|
+
const previousTimestamp = this.currentTimestamp;
|
|
85
|
+
this.currentTimestamp = new Date();
|
|
86
|
+
for (const task of this.scheduledTasks.values()) {
|
|
87
|
+
const elapsedTime = task.getInterval(); // Bir interval süresi geçmiş varsayalım
|
|
88
|
+
const shouldRun = task.shouldExecute(elapsedTime) &&
|
|
89
|
+
task.lastExecutionTime.getTime() <= previousTimestamp.getTime();
|
|
90
|
+
if (!shouldRun)
|
|
91
|
+
continue;
|
|
92
|
+
task.execute(this.currentTimestamp);
|
|
93
|
+
if (task.shouldRemove()) {
|
|
94
|
+
this.removeTask(task.id);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Starts the task processor that executes scheduled tasks
|
|
100
|
+
* @private
|
|
101
|
+
*/
|
|
102
|
+
startTaskProcessor() {
|
|
103
|
+
if (this.schedulerInterval)
|
|
104
|
+
clearInterval(this.schedulerInterval);
|
|
105
|
+
this.schedulerInterval = setInterval(() => {
|
|
106
|
+
this.processTasksNow();
|
|
107
|
+
}, 100);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=TaskScheduler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TaskScheduler.js","sourceRoot":"","sources":["../src/TaskScheduler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAG9B;;;GAGG;AACH,MAAM,OAAO,aAAa;IACd,MAAM,CAAC,QAAQ,CAAgB;IAC/B,gBAAgB,CAAO;IACvB,cAAc,GAA+B,IAAI,GAAG,EAAE,CAAC;IACvD,iBAAiB,GAA0C,IAAI,CAAC;IAExE;QACI,IAAI,CAAC,gBAAgB,GAAG,IAAI,IAAI,EAAE,CAAC;QACnC,IAAI,CAAC,kBAAkB,EAAE,CAAC;IAC9B,CAAC;IAED;;;OAGG;IACI,MAAM,CAAC,WAAW;QACrB,IAAI,CAAC,aAAa,CAAC,QAAQ;YAAE,aAAa,CAAC,QAAQ,GAAG,IAAI,aAAa,EAAE,CAAC;QAC1E,OAAO,aAAa,CAAC,QAAQ,CAAC;IAClC,CAAC;IAED;;;;OAIG;IACI,QAAQ,CAAC,UAA2B;QACvC,OAAO,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;IAC/C,CAAC;IAED;;;OAGG;IACI,WAAW;QACd,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC,MAAM,EAAE,CAAC,CAAC;IACpD,CAAC;IAED;;;;;OAKG;IACI,YAAY,CAAC,QAAsB,EAAE,OAAoB;QAC5D,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,OAAO,CAAC,CAAC;QACnD,MAAM,IAAI,GAAG,IAAI,IAAI,CACjB,IAAI,EACJ,OAAO,CAAC,IAAI,EACZ,MAAM,EACN,QAAQ,EACR,OAAO,CAAC,QAAQ,EAChB,KAAK,EACL,OAAO,CAAC,IAAI,IAAI,KAAK,CACxB,CAAC;QAEF,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QACtC,IAAI,OAAO,CAAC,IAAI;YAAE,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QAE9D,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;YACpB,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;YACpC,IAAI,CAAC,iBAAiB,GAAG,IAAI,CAAC,gBAAgB,CAAC;QACnD,CAAC;QAED,OAAO,IAAI,CAAC;IAChB,CAAC;IAED;;;OAGG;IACI,UAAU,CAAC,UAA2B;QACzC,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;QACvC,IAAI,CAAC,IAAI;YAAE,OAAO;QAElB,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACpC,IAAI,IAAI,CAAC,IAAI;YAAE,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACzD,CAAC;IAED;;OAEG;IACI,aAAa;QAChB,IAAI,IAAI,CAAC,iBAAiB,EAAE,CAAC;YACzB,aAAa,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;YACtC,IAAI,CAAC,iBAAiB,GAAG,IAAI,CAAC;QAClC,CAAC;QACD,IAAI,CAAC,cAAc,CAAC,KAAK,EAAE,CAAC;IAChC,CAAC;IAED;;;OAGG;IACI,eAAe;QAClB,MAAM,iBAAiB,GAAG,IAAI,CAAC,gBAAgB,CAAC;QAChD,IAAI,CAAC,gBAAgB,GAAG,IAAI,IAAI,EAAE,CAAC;QAEnC,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,cAAc,CAAC,MAAM,EAAE,EAAE,CAAC;YAC9C,MAAM,WAAW,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,wCAAwC;YAChF,MAAM,SAAS,GAAG,IAAI,CAAC,aAAa,CAAC,WAAW,CAAC;gBACjC,IAAI,CAAC,iBAAiB,CAAC,OAAO,EAAE,IAAI,iBAAiB,CAAC,OAAO,EAAE,CAAC;YAEhF,IAAI,CAAC,SAAS;gBAAE,SAAS;YAEzB,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;YACpC,IAAI,IAAI,CAAC,YAAY,EAAE,EAAE,CAAC;gBACtB,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YAC7B,CAAC;QACL,CAAC;IACL,CAAC;IAED;;;OAGG;IACK,kBAAkB;QACtB,IAAI,IAAI,CAAC,iBAAiB;YAAE,aAAa,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;QAElE,IAAI,CAAC,iBAAiB,GAAG,WAAW,CAAC,GAAG,EAAE;YACtC,IAAI,CAAC,eAAe,EAAE,CAAC;QAC3B,CAAC,EAAE,GAAG,CAAC,CAAC;IACZ,CAAC;CACJ"}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAGhD,eAAe,aAAa,CAAC,WAAW,EAAE,CAAC"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Callback function type for scheduled tasks
|
|
3
|
+
* @callback TaskCallback
|
|
4
|
+
* @param {Date} [executionTime] - Timestamp when the task was executed
|
|
5
|
+
* @param {number} [executionCount] - Number of times the task has been executed
|
|
6
|
+
* @returns {void}
|
|
7
|
+
*/
|
|
8
|
+
export type TaskCallback = (executionTime?: Date, executionCount?: number) => void;
|
|
9
|
+
/**
|
|
10
|
+
* Configuration options for creating a scheduled task
|
|
11
|
+
* @typedef {Object} TaskOptions
|
|
12
|
+
* @property {string} [name] - Optional name to identify the task
|
|
13
|
+
* @property {number} interval - Time interval in milliseconds between executions
|
|
14
|
+
* @property {boolean} [immediate] - Whether to execute the task immediately upon creation
|
|
15
|
+
* @property {boolean} [once] - Whether the task should only execute once
|
|
16
|
+
*/
|
|
17
|
+
export type TaskOptions = {
|
|
18
|
+
name?: string;
|
|
19
|
+
interval: number;
|
|
20
|
+
immediate?: boolean;
|
|
21
|
+
once?: boolean;
|
|
22
|
+
};
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
package/package.json
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@kyo-services/schedulewise",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"engines": {
|
|
6
|
+
"node": ">=18.0.0"
|
|
7
|
+
},
|
|
8
|
+
"description": "A smart and efficient task scheduler for managing periodic jobs with precision timing and intelligent scheduling in both browser and Node.js environments",
|
|
9
|
+
"main": "dist/main.js",
|
|
10
|
+
"types": "dist/main.d.ts",
|
|
11
|
+
"scripts": {
|
|
12
|
+
"build": "tsc --project tsconfig.json",
|
|
13
|
+
"test": "jest --config jest.config.cjs",
|
|
14
|
+
"test:watch": "jest --config jest.config.cjs --watch",
|
|
15
|
+
"prepare": "husky install && npm run build",
|
|
16
|
+
"lint": "eslint . --ext .ts",
|
|
17
|
+
"lint:fix": "eslint . --ext .ts --fix",
|
|
18
|
+
"prepublishOnly": "npm run build && npm test"
|
|
19
|
+
},
|
|
20
|
+
"keywords": [
|
|
21
|
+
"task",
|
|
22
|
+
"scheduler",
|
|
23
|
+
"job",
|
|
24
|
+
"periodic",
|
|
25
|
+
"interval",
|
|
26
|
+
"timer",
|
|
27
|
+
"cron",
|
|
28
|
+
"typescript"
|
|
29
|
+
],
|
|
30
|
+
"author": "",
|
|
31
|
+
"license": "MIT",
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@commitlint/cli": "^19.8.1",
|
|
34
|
+
"@commitlint/config-conventional": "^19.8.1",
|
|
35
|
+
"@types/jest": "^29.5.14",
|
|
36
|
+
"@typescript-eslint/eslint-plugin": "^8.38.0",
|
|
37
|
+
"@typescript-eslint/parser": "^8.38.0",
|
|
38
|
+
"eslint": "^9.32.0",
|
|
39
|
+
"husky": "^9.1.7",
|
|
40
|
+
"jest": "^29.7.0",
|
|
41
|
+
"lint-staged": "^16.1.2",
|
|
42
|
+
"ts-jest": "^29.4.0",
|
|
43
|
+
"typescript": "^5.0.0",
|
|
44
|
+
"typescript-eslint": "^8.38.0"
|
|
45
|
+
},
|
|
46
|
+
"files": [
|
|
47
|
+
"dist/*.js",
|
|
48
|
+
"dist/*.d.ts",
|
|
49
|
+
"dist/*.map",
|
|
50
|
+
"src"
|
|
51
|
+
],
|
|
52
|
+
"repository": {
|
|
53
|
+
"type": "git",
|
|
54
|
+
"url": "git+https://github.com/kyo-services/schedulewise.git"
|
|
55
|
+
},
|
|
56
|
+
"bugs": {
|
|
57
|
+
"url": "https://github.com/kyo-services/schedulewise/issues"
|
|
58
|
+
},
|
|
59
|
+
"homepage": "https://github.com/kyo-services/schedulewise#readme",
|
|
60
|
+
"lint-staged": {
|
|
61
|
+
"*.ts": [
|
|
62
|
+
"eslint --fix",
|
|
63
|
+
"jest --findRelatedTests"
|
|
64
|
+
]
|
|
65
|
+
}
|
|
66
|
+
}
|
package/src/Task.ts
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { TaskCallback } from './types';
|
|
2
|
+
import { TaskScheduler } from './TaskScheduler';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Represents a scheduled task in the system
|
|
6
|
+
* @class Task
|
|
7
|
+
*/
|
|
8
|
+
export class Task {
|
|
9
|
+
private scheduler: TaskScheduler;
|
|
10
|
+
public lastExecutionTime: Date;
|
|
11
|
+
public executionCount: number;
|
|
12
|
+
public isEnabled: boolean;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Creates a new Task instance
|
|
16
|
+
* @param {TaskScheduler} scheduler - Reference to the scheduler instance
|
|
17
|
+
* @param {string} [name] - Optional name to identify the task
|
|
18
|
+
* @param {number} id - Unique identifier for the task
|
|
19
|
+
* @param {TaskCallback} callback - Function to be executed
|
|
20
|
+
* @param {number} interval - Time interval in milliseconds between executions
|
|
21
|
+
* @param {boolean} executeImmediately - Whether to execute immediately
|
|
22
|
+
* @param {boolean} isOneTime - Whether to execute only once
|
|
23
|
+
*/
|
|
24
|
+
constructor(
|
|
25
|
+
scheduler: TaskScheduler,
|
|
26
|
+
public readonly name: string | undefined,
|
|
27
|
+
public readonly id: number,
|
|
28
|
+
private callback: TaskCallback,
|
|
29
|
+
private interval: number,
|
|
30
|
+
private executeImmediately: boolean,
|
|
31
|
+
private isOneTime: boolean
|
|
32
|
+
) {
|
|
33
|
+
this.scheduler = scheduler;
|
|
34
|
+
this.executionCount = 0;
|
|
35
|
+
this.isEnabled = true;
|
|
36
|
+
this.lastExecutionTime = new Date(0);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Executes the task callback
|
|
41
|
+
* @param {Date} currentTime - Current timestamp
|
|
42
|
+
*/
|
|
43
|
+
public execute(currentTime: Date): void {
|
|
44
|
+
this.callback(currentTime, this.executionCount + 1);
|
|
45
|
+
this.lastExecutionTime = currentTime;
|
|
46
|
+
this.executionCount++;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Enables the task
|
|
51
|
+
* @returns {Task} The task instance for chaining
|
|
52
|
+
*/
|
|
53
|
+
public enable(): Task {
|
|
54
|
+
this.isEnabled = true;
|
|
55
|
+
return this;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Disables the task
|
|
60
|
+
* @returns {Task} The task instance for chaining
|
|
61
|
+
*/
|
|
62
|
+
public disable(): Task {
|
|
63
|
+
this.isEnabled = false;
|
|
64
|
+
return this;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Updates the task configuration
|
|
69
|
+
* @param {TaskOptions} options - New configuration options
|
|
70
|
+
* @param {TaskCallback} [newCallback] - Optional new callback function
|
|
71
|
+
* @returns {Task} The task instance for chaining
|
|
72
|
+
*/
|
|
73
|
+
public update(options: { interval: number, immediate?: boolean }, newCallback?: TaskCallback): Task {
|
|
74
|
+
this.interval = options.interval;
|
|
75
|
+
this.executeImmediately = options.immediate || false;
|
|
76
|
+
if (newCallback) this.callback = newCallback;
|
|
77
|
+
return this;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Removes the task from the scheduler
|
|
82
|
+
*/
|
|
83
|
+
public remove(): void {
|
|
84
|
+
this.scheduler.removeTask(this.id);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Gets the task's interval
|
|
89
|
+
* @returns {number} The interval in milliseconds
|
|
90
|
+
*/
|
|
91
|
+
public getInterval(): number {
|
|
92
|
+
return this.interval;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Checks if the task is ready to execute
|
|
97
|
+
* @param {number} elapsedTime - Time elapsed since last execution
|
|
98
|
+
* @returns {boolean} Whether the task should execute
|
|
99
|
+
*/
|
|
100
|
+
public shouldExecute(elapsedTime: number): boolean {
|
|
101
|
+
return elapsedTime >= this.interval && this.isEnabled;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Checks if the task should be removed after execution
|
|
106
|
+
* @returns {boolean} Whether the task is one-time
|
|
107
|
+
*/
|
|
108
|
+
public shouldRemove(): boolean {
|
|
109
|
+
return this.isOneTime;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { Task } from './Task';
|
|
2
|
+
import { TaskCallback, TaskOptions } from './types';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* A singleton class that manages scheduled tasks in the system
|
|
6
|
+
* @class TaskScheduler
|
|
7
|
+
*/
|
|
8
|
+
export class TaskScheduler {
|
|
9
|
+
private static instance: TaskScheduler;
|
|
10
|
+
private currentTimestamp: Date;
|
|
11
|
+
private scheduledTasks: Map<number | string, Task> = new Map();
|
|
12
|
+
private schedulerInterval: ReturnType<typeof setInterval> | null = null;
|
|
13
|
+
|
|
14
|
+
private constructor() {
|
|
15
|
+
this.currentTimestamp = new Date();
|
|
16
|
+
this.startTaskProcessor();
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Gets the singleton instance of TaskScheduler
|
|
21
|
+
* @returns {TaskScheduler} The singleton instance
|
|
22
|
+
*/
|
|
23
|
+
public static getInstance(): TaskScheduler {
|
|
24
|
+
if (!TaskScheduler.instance) TaskScheduler.instance = new TaskScheduler();
|
|
25
|
+
return TaskScheduler.instance;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Finds a task by its identifier (id or name)
|
|
30
|
+
* @param {number | string} identifier - The task's ID or name
|
|
31
|
+
* @returns {Task | undefined} The found task or undefined
|
|
32
|
+
*/
|
|
33
|
+
public findTask(identifier: number | string): Task | undefined {
|
|
34
|
+
return this.scheduledTasks.get(identifier);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Gets all scheduled tasks in the system
|
|
39
|
+
* @returns {Task[]} Array of all scheduled tasks
|
|
40
|
+
*/
|
|
41
|
+
public getAllTasks(): Task[] {
|
|
42
|
+
return Array.from(this.scheduledTasks.values());
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Creates and schedules a new task
|
|
47
|
+
* @param {TaskCallback} callback - Function to be executed
|
|
48
|
+
* @param {TaskOptions} options - Configuration options for the task
|
|
49
|
+
* @returns {Task} The created task instance
|
|
50
|
+
*/
|
|
51
|
+
public scheduleTask(callback: TaskCallback, options: TaskOptions): Task {
|
|
52
|
+
const taskId = Math.floor(Math.random() * 1000000);
|
|
53
|
+
const task = new Task(
|
|
54
|
+
this,
|
|
55
|
+
options.name,
|
|
56
|
+
taskId,
|
|
57
|
+
callback,
|
|
58
|
+
options.interval,
|
|
59
|
+
false,
|
|
60
|
+
options.once || false
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
this.scheduledTasks.set(taskId, task);
|
|
64
|
+
if (options.name) this.scheduledTasks.set(options.name, task);
|
|
65
|
+
|
|
66
|
+
if (options.immediate) {
|
|
67
|
+
task.execute(this.currentTimestamp);
|
|
68
|
+
task.lastExecutionTime = this.currentTimestamp;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return task;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Removes a task from the scheduler
|
|
76
|
+
* @param {number | string} identifier - The task's ID or name
|
|
77
|
+
*/
|
|
78
|
+
public removeTask(identifier: number | string): void {
|
|
79
|
+
const task = this.findTask(identifier);
|
|
80
|
+
if (!task) return;
|
|
81
|
+
|
|
82
|
+
this.scheduledTasks.delete(task.id);
|
|
83
|
+
if (task.name) this.scheduledTasks.delete(task.name);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Removes all tasks and stops the scheduler
|
|
88
|
+
*/
|
|
89
|
+
public clearAllTasks(): void {
|
|
90
|
+
if (this.schedulerInterval) {
|
|
91
|
+
clearInterval(this.schedulerInterval);
|
|
92
|
+
this.schedulerInterval = null;
|
|
93
|
+
}
|
|
94
|
+
this.scheduledTasks.clear();
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Updates current timestamp and processes tasks
|
|
99
|
+
* @internal For testing purposes
|
|
100
|
+
*/
|
|
101
|
+
public processTasksNow(): void {
|
|
102
|
+
const previousTimestamp = this.currentTimestamp;
|
|
103
|
+
this.currentTimestamp = new Date();
|
|
104
|
+
|
|
105
|
+
for (const task of this.scheduledTasks.values()) {
|
|
106
|
+
const elapsedTime = task.getInterval(); // Bir interval süresi geçmiş varsayalım
|
|
107
|
+
const shouldRun = task.shouldExecute(elapsedTime) &&
|
|
108
|
+
task.lastExecutionTime.getTime() <= previousTimestamp.getTime();
|
|
109
|
+
|
|
110
|
+
if (!shouldRun) continue;
|
|
111
|
+
|
|
112
|
+
task.execute(this.currentTimestamp);
|
|
113
|
+
if (task.shouldRemove()) {
|
|
114
|
+
this.removeTask(task.id);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Starts the task processor that executes scheduled tasks
|
|
121
|
+
* @private
|
|
122
|
+
*/
|
|
123
|
+
private startTaskProcessor(): void {
|
|
124
|
+
if (this.schedulerInterval) clearInterval(this.schedulerInterval);
|
|
125
|
+
|
|
126
|
+
this.schedulerInterval = setInterval(() => {
|
|
127
|
+
this.processTasksNow();
|
|
128
|
+
}, 100);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { setupTest, cleanupTest } from "./testUtils";
|
|
2
|
+
import { TaskScheduler } from "../TaskScheduler";
|
|
3
|
+
|
|
4
|
+
describe("Task Creation", () => {
|
|
5
|
+
let scheduler: TaskScheduler;
|
|
6
|
+
|
|
7
|
+
beforeEach(() => {
|
|
8
|
+
scheduler = setupTest();
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
afterEach(() => {
|
|
12
|
+
cleanupTest();
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it("should create a task with basic configuration", () => {
|
|
16
|
+
const callback = jest.fn();
|
|
17
|
+
const task = scheduler.scheduleTask(callback, { interval: 1000 });
|
|
18
|
+
|
|
19
|
+
expect(task).toBeDefined();
|
|
20
|
+
expect(task.getInterval()).toBe(1000);
|
|
21
|
+
expect(task.isEnabled).toBe(true);
|
|
22
|
+
expect(task.executionCount).toBe(0);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it("should create a task with immediate execution", () => {
|
|
26
|
+
const callback = jest.fn();
|
|
27
|
+
const task = scheduler.scheduleTask(callback, {
|
|
28
|
+
interval: 1000,
|
|
29
|
+
immediate: true,
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
expect(callback).toHaveBeenCalledTimes(1);
|
|
33
|
+
expect(task.executionCount).toBe(1);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it("should create a task with a name", () => {
|
|
37
|
+
const callback = jest.fn();
|
|
38
|
+
const task = scheduler.scheduleTask(callback, {
|
|
39
|
+
interval: 1000,
|
|
40
|
+
name: "testTask",
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
expect(task.name).toBe("testTask");
|
|
44
|
+
expect(scheduler.findTask("testTask")).toBe(task);
|
|
45
|
+
});
|
|
46
|
+
});
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { setupTest, cleanupTest } from "./testUtils";
|
|
2
|
+
import { TaskScheduler } from "../TaskScheduler";
|
|
3
|
+
|
|
4
|
+
describe("Task Execution", () => {
|
|
5
|
+
let scheduler: TaskScheduler;
|
|
6
|
+
|
|
7
|
+
beforeEach(() => {
|
|
8
|
+
scheduler = setupTest();
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
afterEach(() => {
|
|
12
|
+
cleanupTest();
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it("should execute task at regular intervals", () => {
|
|
16
|
+
const callback = jest.fn();
|
|
17
|
+
scheduler.scheduleTask(callback, { interval: 1000 });
|
|
18
|
+
|
|
19
|
+
// İlk çalıştırma
|
|
20
|
+
scheduler.processTasksNow();
|
|
21
|
+
expect(callback).toHaveBeenCalledTimes(1);
|
|
22
|
+
|
|
23
|
+
// İkinci çalıştırma
|
|
24
|
+
scheduler.processTasksNow();
|
|
25
|
+
expect(callback).toHaveBeenCalledTimes(2);
|
|
26
|
+
|
|
27
|
+
// Üçüncü çalıştırma
|
|
28
|
+
scheduler.processTasksNow();
|
|
29
|
+
expect(callback).toHaveBeenCalledTimes(3);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it("should execute task only once when configured", () => {
|
|
33
|
+
const callback = jest.fn();
|
|
34
|
+
scheduler.scheduleTask(callback, {
|
|
35
|
+
interval: 1000,
|
|
36
|
+
once: true,
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
// İlk çalıştırma
|
|
40
|
+
scheduler.processTasksNow();
|
|
41
|
+
expect(callback).toHaveBeenCalledTimes(1);
|
|
42
|
+
|
|
43
|
+
// İkinci çalıştırma - çağrılmamalı
|
|
44
|
+
scheduler.processTasksNow();
|
|
45
|
+
expect(callback).toHaveBeenCalledTimes(1);
|
|
46
|
+
|
|
47
|
+
// Üçüncü çalıştırma - çağrılmamalı
|
|
48
|
+
scheduler.processTasksNow();
|
|
49
|
+
expect(callback).toHaveBeenCalledTimes(1);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it("should not execute disabled tasks", () => {
|
|
53
|
+
const callback = jest.fn();
|
|
54
|
+
const task = scheduler.scheduleTask(callback, { interval: 1000 });
|
|
55
|
+
task.disable();
|
|
56
|
+
|
|
57
|
+
// İlk çalıştırma
|
|
58
|
+
scheduler.processTasksNow();
|
|
59
|
+
expect(callback).toHaveBeenCalledTimes(0);
|
|
60
|
+
|
|
61
|
+
// İkinci çalıştırma
|
|
62
|
+
scheduler.processTasksNow();
|
|
63
|
+
expect(callback).toHaveBeenCalledTimes(0);
|
|
64
|
+
|
|
65
|
+
// Üçüncü çalıştırma
|
|
66
|
+
scheduler.processTasksNow();
|
|
67
|
+
expect(callback).toHaveBeenCalledTimes(0);
|
|
68
|
+
});
|
|
69
|
+
});
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { setupTest, cleanupTest } from "./testUtils";
|
|
2
|
+
import { TaskScheduler } from "../TaskScheduler";
|
|
3
|
+
|
|
4
|
+
describe("Task Management", () => {
|
|
5
|
+
let scheduler: TaskScheduler;
|
|
6
|
+
|
|
7
|
+
beforeEach(() => {
|
|
8
|
+
scheduler = setupTest();
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
afterEach(() => {
|
|
12
|
+
cleanupTest();
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it("should enable and disable tasks", () => {
|
|
16
|
+
const callback = jest.fn();
|
|
17
|
+
const task = scheduler.scheduleTask(callback, { interval: 1000 });
|
|
18
|
+
|
|
19
|
+
// İlk çalıştırma - aktif
|
|
20
|
+
scheduler.processTasksNow();
|
|
21
|
+
expect(callback).toHaveBeenCalledTimes(1);
|
|
22
|
+
|
|
23
|
+
task.disable();
|
|
24
|
+
scheduler.processTasksNow();
|
|
25
|
+
expect(callback).toHaveBeenCalledTimes(1);
|
|
26
|
+
|
|
27
|
+
task.enable();
|
|
28
|
+
scheduler.processTasksNow();
|
|
29
|
+
expect(callback).toHaveBeenCalledTimes(2);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it("should remove tasks", () => {
|
|
33
|
+
const callback = jest.fn();
|
|
34
|
+
const task = scheduler.scheduleTask(callback, {
|
|
35
|
+
interval: 1000,
|
|
36
|
+
name: "testTask",
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
// İlk çalıştırma
|
|
40
|
+
scheduler.processTasksNow();
|
|
41
|
+
expect(callback).toHaveBeenCalledTimes(1);
|
|
42
|
+
|
|
43
|
+
task.remove();
|
|
44
|
+
|
|
45
|
+
expect(scheduler.findTask(task.id)).toBeUndefined();
|
|
46
|
+
expect(scheduler.findTask("testTask")).toBeUndefined();
|
|
47
|
+
|
|
48
|
+
// Task silindiği için çağrılmamalı
|
|
49
|
+
scheduler.processTasksNow();
|
|
50
|
+
expect(callback).toHaveBeenCalledTimes(1);
|
|
51
|
+
|
|
52
|
+
scheduler.processTasksNow();
|
|
53
|
+
expect(callback).toHaveBeenCalledTimes(1);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it("should clear all tasks", () => {
|
|
57
|
+
const callback1 = jest.fn();
|
|
58
|
+
const callback2 = jest.fn();
|
|
59
|
+
|
|
60
|
+
scheduler.scheduleTask(callback1, { interval: 1000 });
|
|
61
|
+
scheduler.scheduleTask(callback2, { interval: 1000 });
|
|
62
|
+
|
|
63
|
+
scheduler.clearAllTasks();
|
|
64
|
+
|
|
65
|
+
// Tüm tasklar silindiği için çağrılmamalı
|
|
66
|
+
scheduler.processTasksNow();
|
|
67
|
+
expect(callback1).not.toHaveBeenCalled();
|
|
68
|
+
|
|
69
|
+
scheduler.processTasksNow();
|
|
70
|
+
expect(callback2).not.toHaveBeenCalled();
|
|
71
|
+
|
|
72
|
+
// Task listesi boş olmalı
|
|
73
|
+
expect(scheduler.getAllTasks()).toHaveLength(0);
|
|
74
|
+
});
|
|
75
|
+
});
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { TaskScheduler } from "../TaskScheduler";
|
|
2
|
+
|
|
3
|
+
let scheduler: TaskScheduler;
|
|
4
|
+
let OriginalDate: DateConstructor;
|
|
5
|
+
|
|
6
|
+
export const mockDate = (date: Date): void => {
|
|
7
|
+
OriginalDate = global.Date;
|
|
8
|
+
const MockDate = class extends OriginalDate {
|
|
9
|
+
constructor(...args: ConstructorParameters<typeof Date>) {
|
|
10
|
+
super(...args);
|
|
11
|
+
return date;
|
|
12
|
+
}
|
|
13
|
+
static now(): number {
|
|
14
|
+
return date.getTime();
|
|
15
|
+
}
|
|
16
|
+
} as unknown as DateConstructor;
|
|
17
|
+
global.Date = MockDate;
|
|
18
|
+
jest.useFakeTimers();
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export const restoreDate = (): void => {
|
|
22
|
+
jest.useRealTimers();
|
|
23
|
+
global.Date = OriginalDate;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export const setupTest = (): TaskScheduler => {
|
|
27
|
+
const now = new Date();
|
|
28
|
+
mockDate(now);
|
|
29
|
+
scheduler = TaskScheduler.getInstance();
|
|
30
|
+
return scheduler;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export const cleanupTest = (): void => {
|
|
34
|
+
restoreDate();
|
|
35
|
+
if (scheduler) {
|
|
36
|
+
scheduler.clearAllTasks();
|
|
37
|
+
}
|
|
38
|
+
};
|
package/src/index.ts
ADDED
package/src/types.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Callback function type for scheduled tasks
|
|
3
|
+
* @callback TaskCallback
|
|
4
|
+
* @param {Date} [executionTime] - Timestamp when the task was executed
|
|
5
|
+
* @param {number} [executionCount] - Number of times the task has been executed
|
|
6
|
+
* @returns {void}
|
|
7
|
+
*/
|
|
8
|
+
export type TaskCallback = (executionTime?: Date, executionCount?: number) => void;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Configuration options for creating a scheduled task
|
|
12
|
+
* @typedef {Object} TaskOptions
|
|
13
|
+
* @property {string} [name] - Optional name to identify the task
|
|
14
|
+
* @property {number} interval - Time interval in milliseconds between executions
|
|
15
|
+
* @property {boolean} [immediate] - Whether to execute the task immediately upon creation
|
|
16
|
+
* @property {boolean} [once] - Whether the task should only execute once
|
|
17
|
+
*/
|
|
18
|
+
export type TaskOptions = {
|
|
19
|
+
name?: string;
|
|
20
|
+
interval: number;
|
|
21
|
+
immediate?: boolean;
|
|
22
|
+
once?: boolean;
|
|
23
|
+
};
|