jw-automator 2.0.0 → 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.
@@ -0,0 +1,411 @@
1
+ # Migration Guide: v2 to v3
2
+
3
+ This guide helps you migrate from jw-automator v2 to v3.
4
+
5
+ ---
6
+
7
+ ## Overview
8
+
9
+ jw-automator v3 is a **complete clean-room rewrite** with improved semantics, better DST handling, and a cleaner API. While the core concepts remain the same, there are breaking changes from v2.
10
+
11
+ v2 was the widely-deployed production version. v3 represents a ground-up reimplementation that preserves the philosophy while modernizing the architecture.
12
+
13
+ ---
14
+
15
+ ## Breaking Changes
16
+
17
+ ### 1. Constructor and Initialization
18
+
19
+ **v2:**
20
+ ```javascript
21
+ const automator = require('jw-automator');
22
+ automator.init({ file: './actions.json' });
23
+ ```
24
+
25
+ **v3:**
26
+ ```javascript
27
+ const Automator = require('jw-automator');
28
+ const automator = new Automator({
29
+ storage: Automator.storage.file('./actions.json')
30
+ });
31
+ ```
32
+
33
+ ### 2. Action Structure
34
+
35
+ **v2:**
36
+ Action structure was less formalized.
37
+
38
+ **v3:**
39
+ ```javascript
40
+ {
41
+ id: 1, // Auto-generated
42
+ name: 'My Action', // Optional
43
+ cmd: 'commandName', // Required
44
+ payload: {}, // Optional
45
+ date: Date, // Required (or null for immediate)
46
+ unBuffered: false, // Default false
47
+ repeat: { // Optional
48
+ type: 'day',
49
+ interval: 1,
50
+ limit: null,
51
+ endDate: null,
52
+ dstPolicy: 'once' // NEW in v3
53
+ },
54
+ count: 0 // Execution counter
55
+ }
56
+ ```
57
+
58
+ ### 3. DST Policy
59
+
60
+ **v2:**
61
+ DST behavior was implicit and sometimes unpredictable.
62
+
63
+ **v3:**
64
+ Explicit DST policy for fall-back scenarios:
65
+ ```javascript
66
+ repeat: {
67
+ type: 'hour',
68
+ interval: 1,
69
+ dstPolicy: 'once' // or 'twice'
70
+ }
71
+ ```
72
+
73
+ ### 4. Event Names
74
+
75
+ **v2:**
76
+ Various event names.
77
+
78
+ **v3:**
79
+ Standardized events:
80
+ - `ready` - Scheduler started
81
+ - `action` - Action executed
82
+ - `update` - Action added/updated/removed
83
+ - `error` - Error occurred
84
+ - `debug` - Debug information
85
+
86
+ ### 5. API Methods
87
+
88
+ #### Adding Actions
89
+
90
+ **v2:**
91
+ ```javascript
92
+ automator.addAction(actionObject);
93
+ ```
94
+
95
+ **v3:**
96
+ ```javascript
97
+ const id = automator.addAction(actionSpec);
98
+ // Returns the action ID
99
+ ```
100
+
101
+ #### Getting Actions
102
+
103
+ **v2:**
104
+ ```javascript
105
+ const actions = automator.getActions();
106
+ ```
107
+
108
+ **v3:**
109
+ ```javascript
110
+ const actions = automator.getActions(); // Deep copy
111
+ const action = automator.getActionByID(id);
112
+ const actions = automator.getActionsByName('name');
113
+ ```
114
+
115
+ #### Removing Actions
116
+
117
+ **v2:**
118
+ ```javascript
119
+ automator.removeAction(id);
120
+ ```
121
+
122
+ **v3:**
123
+ ```javascript
124
+ automator.removeActionByID(id);
125
+ automator.removeActionByName('name'); // Returns count removed
126
+ ```
127
+
128
+ #### Updating Actions
129
+
130
+ **v2:**
131
+ Limited update capability.
132
+
133
+ **v3:**
134
+ ```javascript
135
+ automator.updateActionByID(id, {
136
+ name: 'New Name',
137
+ repeat: { type: 'hour', interval: 2 }
138
+ });
139
+
140
+ automator.updateActionByName('Old Name', {
141
+ name: 'New Name'
142
+ });
143
+ ```
144
+
145
+ ---
146
+
147
+ ## New Features in v3
148
+
149
+ ### 1. Simulation
150
+
151
+ Preview future schedules without running them:
152
+
153
+ ```javascript
154
+ const events = automator.getActionsInRange(
155
+ new Date('2025-05-01'),
156
+ new Date('2025-05-07')
157
+ );
158
+
159
+ console.log(`${events.length} events will occur`);
160
+ ```
161
+
162
+ ### 2. Pluggable Storage
163
+
164
+ Choose or create storage backends:
165
+
166
+ ```javascript
167
+ // Memory storage (no persistence)
168
+ const automator = new Automator({
169
+ storage: Automator.storage.memory()
170
+ });
171
+
172
+ // File storage
173
+ const automator = new Automator({
174
+ storage: Automator.storage.file('./actions.json')
175
+ });
176
+
177
+ // Custom storage
178
+ const automator = new Automator({
179
+ storage: {
180
+ load: () => { /* load from DB */ },
181
+ save: (state) => { /* save to DB */ }
182
+ }
183
+ });
184
+ ```
185
+
186
+ ### 3. Action Description
187
+
188
+ Human-readable action summaries:
189
+
190
+ ```javascript
191
+ console.log(automator.describeAction(1));
192
+ // Action #1 - Morning Lights
193
+ // Command: turnLightOn
194
+ // Next run: 5/1/2025, 7:00:00 AM
195
+ // Executions: 15
196
+ // Buffered: true
197
+ // Recurrence: day
198
+ // DST policy: once
199
+ ```
200
+
201
+ ### 4. Better Event Payloads
202
+
203
+ Action events include rich metadata:
204
+
205
+ ```javascript
206
+ automator.on('action', (event) => {
207
+ console.log(event);
208
+ // {
209
+ // type: 'action',
210
+ // actionId: 1,
211
+ // name: 'My Action',
212
+ // cmd: 'myCmd',
213
+ // payload: {},
214
+ // scheduledTime: Date,
215
+ // actualTime: Date,
216
+ // count: 5
217
+ // }
218
+ });
219
+ ```
220
+
221
+ ### 5. Auto-Save Control
222
+
223
+ Fine-tune persistence:
224
+
225
+ ```javascript
226
+ const automator = new Automator({
227
+ storage: Automator.storage.file('./actions.json'),
228
+ autoSave: true,
229
+ saveInterval: 10000 // Save every 10 seconds
230
+ });
231
+ ```
232
+
233
+ ---
234
+
235
+ ## Migration Steps
236
+
237
+ ### Step 1: Update Initialization
238
+
239
+ Replace your v2 initialization with v3 constructor:
240
+
241
+ ```javascript
242
+ // Before
243
+ const automator = require('jw-automator');
244
+ automator.init({ file: './actions.json' });
245
+
246
+ // After
247
+ const Automator = require('jw-automator');
248
+ const automator = new Automator({
249
+ storage: Automator.storage.file('./actions.json')
250
+ });
251
+ ```
252
+
253
+ ### Step 2: Update Action Definitions
254
+
255
+ Add `dstPolicy` to actions with repeat:
256
+
257
+ ```javascript
258
+ // Before
259
+ automator.addAction({
260
+ cmd: 'myCmd',
261
+ date: new Date(),
262
+ repeat: { type: 'day', interval: 1 }
263
+ });
264
+
265
+ // After
266
+ automator.addAction({
267
+ cmd: 'myCmd',
268
+ date: new Date(),
269
+ repeat: {
270
+ type: 'day',
271
+ interval: 1,
272
+ dstPolicy: 'once' // Explicitly choose DST behavior
273
+ }
274
+ });
275
+ ```
276
+
277
+ ### Step 3: Update Event Listeners
278
+
279
+ Standardize event names:
280
+
281
+ ```javascript
282
+ // Before
283
+ automator.on('actionExecuted', (data) => { ... });
284
+
285
+ // After
286
+ automator.on('action', (event) => { ... });
287
+ ```
288
+
289
+ ### Step 4: Update API Calls
290
+
291
+ Use new method names:
292
+
293
+ ```javascript
294
+ // Before
295
+ automator.removeAction(id);
296
+
297
+ // After
298
+ automator.removeActionByID(id);
299
+ ```
300
+
301
+ ### Step 5: Test DST Behavior
302
+
303
+ Review actions that run during DST transitions and set appropriate `dstPolicy`:
304
+
305
+ - `'once'` - Run only the first occurrence during fall-back (recommended default)
306
+ - `'twice'` - Run both occurrences during fall-back
307
+
308
+ ### Step 6: Leverage New Features
309
+
310
+ Consider using:
311
+ - `getActionsInRange()` for calendar previews
312
+ - `describeAction()` for debugging
313
+ - Custom storage adapters for database persistence
314
+ - Update events for logging changes
315
+
316
+ ---
317
+
318
+ ## Compatibility Notes
319
+
320
+ ### What's the Same
321
+
322
+ - **Core concept**: Schedule actions with recurrence
323
+ - **Recurrence types**: second, minute, hour, day, week, month, year
324
+ - **Local time**: Still operates in local time
325
+ - **Buffered/unbuffered**: Still supported (as `unBuffered` flag)
326
+ - **Function registration**: Still use `addFunction()`
327
+
328
+ ### What's Different
329
+
330
+ - **Constructor**: Now uses `new Automator()`
331
+ - **Storage**: Explicitly configured
332
+ - **DST**: Explicit policy required
333
+ - **Events**: Standardized names and payloads
334
+ - **API**: More consistent naming (`ByID`, `ByName`)
335
+ - **IDs**: Auto-generated, not user-provided
336
+ - **State**: Better separation of spec vs. state
337
+
338
+ ---
339
+
340
+ ## Example: Complete Migration
341
+
342
+ **v2 Code:**
343
+ ```javascript
344
+ const automator = require('jw-automator');
345
+ automator.init({ file: './actions.json' });
346
+
347
+ automator.addFunction('turnLightOn', () => {
348
+ console.log('Light on');
349
+ });
350
+
351
+ automator.addAction({
352
+ cmd: 'turnLightOn',
353
+ date: new Date('2025-05-01T07:00:00'),
354
+ repeat: { type: 'day', interval: 1 }
355
+ });
356
+
357
+ automator.start();
358
+ ```
359
+
360
+ **v3 Code:**
361
+ ```javascript
362
+ const Automator = require('jw-automator');
363
+
364
+ const automator = new Automator({
365
+ storage: Automator.storage.file('./actions.json')
366
+ });
367
+
368
+ automator.addFunction('turnLightOn', () => {
369
+ console.log('Light on');
370
+ });
371
+
372
+ automator.addAction({
373
+ name: 'Morning Lights',
374
+ cmd: 'turnLightOn',
375
+ date: new Date('2025-05-01T07:00:00'),
376
+ unBuffered: false,
377
+ repeat: {
378
+ type: 'day',
379
+ interval: 1,
380
+ dstPolicy: 'once'
381
+ }
382
+ });
383
+
384
+ automator.start();
385
+ ```
386
+
387
+ ---
388
+
389
+ ## Getting Help
390
+
391
+ If you encounter issues during migration:
392
+
393
+ 1. Check the [README](../README.md) for full API documentation
394
+ 2. Review the [Architecture](./ARCHITECTURE.md) for design understanding
395
+ 3. Run the examples in the `examples/` directory
396
+ 4. File an issue on GitHub
397
+
398
+ ---
399
+
400
+ ## Why Rewrite?
401
+
402
+ v3 addresses several issues from v2:
403
+
404
+ - **Infinite loops**: Better safety guards
405
+ - **DST bugs**: Explicit, predictable handling
406
+ - **Catch-up logic**: More reliable offline behavior
407
+ - **Testability**: Deterministic core engine
408
+ - **Maintainability**: Cleaner architecture
409
+ - **Extensibility**: Pluggable storage, better API
410
+
411
+ The rewrite provides a solid foundation for long-term reliability.