@quarry-systems/drift-timer 0.1.0-alpha.1
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 +308 -0
- package/package.json +35 -0
package/README.md
ADDED
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
# MCG Timer Plugin
|
|
2
|
+
|
|
3
|
+
A comprehensive timer and scheduling plugin for Managed Cyclic Graph (MCG) that enables nodes to delay execution, wait for specific times, and schedule based on cron-like patterns.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- ✅ **Simple Delays**: Sleep for a specified duration
|
|
8
|
+
- ✅ **Scheduled Waits**: Wait until a specific date/time
|
|
9
|
+
- ✅ **Cron Patterns**: Daily, weekly, monthly scheduling
|
|
10
|
+
- ✅ **Business Days**: Skip weekends automatically
|
|
11
|
+
- ✅ **Flexible Storage**: Custom paths for timer metadata
|
|
12
|
+
- ✅ **Callbacks**: onStart and onComplete hooks
|
|
13
|
+
- ✅ **Two Usage Modes**: Plugin-based or action-based
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install @quarry-systems/drift-timer
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Quick Start
|
|
22
|
+
|
|
23
|
+
### Plugin-Based Approach
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
import { ManagedCyclicGraph } from '@quarry-systems/managed-cyclic-graph';
|
|
27
|
+
import { mcgTimerPlugin, sleep, dailyAt } from '@quarry-systems/drift-timer';
|
|
28
|
+
|
|
29
|
+
const graph = new ManagedCyclicGraph()
|
|
30
|
+
.use(mcgTimerPlugin)
|
|
31
|
+
|
|
32
|
+
.node('wait5sec', {
|
|
33
|
+
type: 'timernode',
|
|
34
|
+
meta: {
|
|
35
|
+
timer: sleep(5000) // Wait 5 seconds
|
|
36
|
+
}
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
.node('dailyReport', {
|
|
40
|
+
type: 'timernode',
|
|
41
|
+
meta: {
|
|
42
|
+
timer: dailyAt(9, 0) // Wait until 9:00 AM
|
|
43
|
+
}
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
.build();
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Action-Based Approach
|
|
50
|
+
|
|
51
|
+
```typescript
|
|
52
|
+
import { ManagedCyclicGraph } from '@quarry-systems/managed-cyclic-graph';
|
|
53
|
+
import { createTimerAction, sleep, nextBusinessDay } from '@quarry-systems/drift-timer';
|
|
54
|
+
|
|
55
|
+
const graph = new ManagedCyclicGraph()
|
|
56
|
+
.node('processOrder', {
|
|
57
|
+
execute: [
|
|
58
|
+
// ... process order logic
|
|
59
|
+
]
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
.node('waitForBusinessDay', {
|
|
63
|
+
execute: [
|
|
64
|
+
createTimerAction('waitForBusinessDay', nextBusinessDay(9, 0))
|
|
65
|
+
]
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
.node('sendReport', {
|
|
69
|
+
execute: [
|
|
70
|
+
// ... send report logic
|
|
71
|
+
]
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
.build();
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## API Reference
|
|
78
|
+
|
|
79
|
+
### Helper Functions
|
|
80
|
+
|
|
81
|
+
#### `sleep(ms: number)`
|
|
82
|
+
Sleep for a specified duration in milliseconds.
|
|
83
|
+
|
|
84
|
+
```typescript
|
|
85
|
+
sleep(5000) // Sleep for 5 seconds
|
|
86
|
+
sleep(60000) // Sleep for 1 minute
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
#### `waitUntil(date: Date | string | number)`
|
|
90
|
+
Wait until a specific date/time.
|
|
91
|
+
|
|
92
|
+
```typescript
|
|
93
|
+
waitUntil(new Date('2024-12-25T00:00:00'))
|
|
94
|
+
waitUntil('2024-12-25T00:00:00')
|
|
95
|
+
waitUntil(Date.now() + 3600000) // 1 hour from now
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
#### `dailyAt(hour: number, minute?: number)`
|
|
99
|
+
Wait until the next occurrence of a specific time each day.
|
|
100
|
+
|
|
101
|
+
```typescript
|
|
102
|
+
dailyAt(9, 0) // Wait until 9:00 AM
|
|
103
|
+
dailyAt(17, 30) // Wait until 5:30 PM
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
#### `weeklyAt(dayOfWeek: number, hour: number, minute?: number)`
|
|
107
|
+
Wait until the next occurrence of a specific day and time each week.
|
|
108
|
+
|
|
109
|
+
```typescript
|
|
110
|
+
weeklyAt(1, 9, 0) // Monday at 9:00 AM (0=Sunday, 1=Monday, ...)
|
|
111
|
+
weeklyAt(5, 17, 0) // Friday at 5:00 PM
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
#### `nextBusinessDay(hour?: number, minute?: number)`
|
|
115
|
+
Wait until the next business day (skips weekends) at the specified time.
|
|
116
|
+
|
|
117
|
+
```typescript
|
|
118
|
+
nextBusinessDay() // Next business day at midnight
|
|
119
|
+
nextBusinessDay(9, 0) // Next business day at 9:00 AM
|
|
120
|
+
nextBusinessDay(8, 30) // Next business day at 8:30 AM
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Configuration Options
|
|
124
|
+
|
|
125
|
+
```typescript
|
|
126
|
+
interface TimerConfig {
|
|
127
|
+
sleepMs?: number; // Sleep duration in milliseconds
|
|
128
|
+
waitUntil?: Date | string | number; // Target date/time
|
|
129
|
+
cronPattern?: CronPattern; // Cron-like scheduling
|
|
130
|
+
storePath?: string; // Custom storage path
|
|
131
|
+
onStart?: (ctx) => void; // Callback when timer starts
|
|
132
|
+
onComplete?: (ctx) => void; // Callback when timer completes
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### Cron Pattern Options
|
|
137
|
+
|
|
138
|
+
```typescript
|
|
139
|
+
interface CronPattern {
|
|
140
|
+
type: 'daily' | 'weekly' | 'monthly' | 'custom';
|
|
141
|
+
hour?: number; // Hour (0-23)
|
|
142
|
+
minute?: number; // Minute (0-59)
|
|
143
|
+
dayOfWeek?: number; // Day of week (0-6, Sunday=0)
|
|
144
|
+
dayOfMonth?: number; // Day of month (1-31)
|
|
145
|
+
skipWeekends?: boolean; // Skip Saturday/Sunday
|
|
146
|
+
expression?: string; // Custom cron expression (future)
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## Examples
|
|
151
|
+
|
|
152
|
+
### Retry with Exponential Backoff
|
|
153
|
+
|
|
154
|
+
```typescript
|
|
155
|
+
const graph = new ManagedCyclicGraph()
|
|
156
|
+
.use(mcgTimerPlugin)
|
|
157
|
+
|
|
158
|
+
.node('attempt1', {
|
|
159
|
+
execute: [/* try operation */]
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
.node('wait1', {
|
|
163
|
+
type: 'timernode',
|
|
164
|
+
meta: { timer: sleep(1000) } // Wait 1 second
|
|
165
|
+
})
|
|
166
|
+
|
|
167
|
+
.node('attempt2', {
|
|
168
|
+
execute: [/* retry operation */]
|
|
169
|
+
})
|
|
170
|
+
|
|
171
|
+
.node('wait2', {
|
|
172
|
+
type: 'timernode',
|
|
173
|
+
meta: { timer: sleep(2000) } // Wait 2 seconds
|
|
174
|
+
})
|
|
175
|
+
|
|
176
|
+
.node('attempt3', {
|
|
177
|
+
execute: [/* final retry */]
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
.build();
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### Daily Report Generation
|
|
184
|
+
|
|
185
|
+
```typescript
|
|
186
|
+
const graph = new ManagedCyclicGraph()
|
|
187
|
+
.use(mcgTimerPlugin)
|
|
188
|
+
|
|
189
|
+
.node('waitForMorning', {
|
|
190
|
+
type: 'timernode',
|
|
191
|
+
meta: {
|
|
192
|
+
timer: dailyAt(8, 0) // Every day at 8:00 AM
|
|
193
|
+
}
|
|
194
|
+
})
|
|
195
|
+
|
|
196
|
+
.node('generateReport', {
|
|
197
|
+
execute: [/* generate report */]
|
|
198
|
+
})
|
|
199
|
+
|
|
200
|
+
.node('sendReport', {
|
|
201
|
+
execute: [/* send report */]
|
|
202
|
+
})
|
|
203
|
+
|
|
204
|
+
// Loop back to wait for next day
|
|
205
|
+
.edge('sendReport', 'waitForMorning', 'any')
|
|
206
|
+
|
|
207
|
+
.build();
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### Weekend-Aware Processing
|
|
211
|
+
|
|
212
|
+
```typescript
|
|
213
|
+
const graph = new ManagedCyclicGraph()
|
|
214
|
+
.use(mcgTimerPlugin)
|
|
215
|
+
|
|
216
|
+
.node('checkData', {
|
|
217
|
+
execute: [/* check for new data */]
|
|
218
|
+
})
|
|
219
|
+
|
|
220
|
+
.node('waitForBusinessDay', {
|
|
221
|
+
type: 'timernode',
|
|
222
|
+
meta: {
|
|
223
|
+
timer: nextBusinessDay(9, 0)
|
|
224
|
+
}
|
|
225
|
+
})
|
|
226
|
+
|
|
227
|
+
.node('processData', {
|
|
228
|
+
execute: [/* process during business hours */]
|
|
229
|
+
})
|
|
230
|
+
|
|
231
|
+
.build();
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
### With Callbacks
|
|
235
|
+
|
|
236
|
+
```typescript
|
|
237
|
+
const graph = new ManagedCyclicGraph()
|
|
238
|
+
.use(mcgTimerPlugin)
|
|
239
|
+
|
|
240
|
+
.node('scheduledTask', {
|
|
241
|
+
type: 'timernode',
|
|
242
|
+
meta: {
|
|
243
|
+
timer: {
|
|
244
|
+
...dailyAt(10, 0),
|
|
245
|
+
onStart: (ctx) => {
|
|
246
|
+
console.log('Timer started, waiting until 10:00 AM');
|
|
247
|
+
},
|
|
248
|
+
onComplete: (ctx) => {
|
|
249
|
+
console.log('Timer completed, executing task now');
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
})
|
|
254
|
+
|
|
255
|
+
.build();
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
## Metadata Storage
|
|
259
|
+
|
|
260
|
+
Timer metadata is stored in the context at `data.timer.{nodeId}` by default:
|
|
261
|
+
|
|
262
|
+
```typescript
|
|
263
|
+
{
|
|
264
|
+
data: {
|
|
265
|
+
timer: {
|
|
266
|
+
wait5sec: {
|
|
267
|
+
startTime: 1701234567890,
|
|
268
|
+
targetTime: 1701234572890,
|
|
269
|
+
duration: 5000,
|
|
270
|
+
completed: true
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
You can customize the storage path:
|
|
278
|
+
|
|
279
|
+
```typescript
|
|
280
|
+
{
|
|
281
|
+
timer: {
|
|
282
|
+
...sleep(5000),
|
|
283
|
+
storePath: 'data.customPath.myTimer'
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
## Use Cases
|
|
289
|
+
|
|
290
|
+
- **Rate Limiting**: Add delays between API calls
|
|
291
|
+
- **Retry Logic**: Wait before retrying failed operations
|
|
292
|
+
- **Scheduled Tasks**: Run tasks at specific times
|
|
293
|
+
- **Business Hours**: Ensure operations run during business days
|
|
294
|
+
- **Cooldown Periods**: Enforce waiting periods between actions
|
|
295
|
+
- **Batch Processing**: Wait for batch windows
|
|
296
|
+
- **Compliance**: Respect time-based regulations
|
|
297
|
+
|
|
298
|
+
## Best Practices
|
|
299
|
+
|
|
300
|
+
1. **Use Business Day Scheduling** for operations that should only run during work hours
|
|
301
|
+
2. **Add Callbacks** for logging and monitoring timer events
|
|
302
|
+
3. **Store Metadata** in custom paths when you need to track multiple timers
|
|
303
|
+
4. **Combine with Guards** to create conditional timing logic
|
|
304
|
+
5. **Use Action-Based** approach when mixing timers with other actions
|
|
305
|
+
|
|
306
|
+
## License
|
|
307
|
+
|
|
308
|
+
ISC
|
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@quarry-systems/drift-timer",
|
|
3
|
+
"version": "0.1.0-alpha.1",
|
|
4
|
+
"description": "Timer and scheduling plugin for Drift",
|
|
5
|
+
"main": "./src/index.js",
|
|
6
|
+
"types": "./src/index.d.ts",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"build": "tsc -p .",
|
|
9
|
+
"clean": "rimraf dist || rm -rf dist",
|
|
10
|
+
"dev": "tsc -p . --watch",
|
|
11
|
+
"test": "vitest run",
|
|
12
|
+
"test:watch": "vitest"
|
|
13
|
+
},
|
|
14
|
+
"keywords": [
|
|
15
|
+
"drift",
|
|
16
|
+
"plugin",
|
|
17
|
+
"timer",
|
|
18
|
+
"delay",
|
|
19
|
+
"schedule",
|
|
20
|
+
"cron"
|
|
21
|
+
],
|
|
22
|
+
"author": "Brett Nye",
|
|
23
|
+
"license": "ISC",
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"typescript": "^5.0.0",
|
|
26
|
+
"vitest": "^2.1.0"
|
|
27
|
+
},
|
|
28
|
+
"files": [
|
|
29
|
+
"dist",
|
|
30
|
+
"README.md",
|
|
31
|
+
"LICENSE.md",
|
|
32
|
+
"CHANGELOG.md"
|
|
33
|
+
],
|
|
34
|
+
"type": "commonjs"
|
|
35
|
+
}
|