jw-automator 1.0.2 → 3.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.
@@ -0,0 +1,407 @@
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
+
141
+ ---
142
+
143
+ ## New Features in v3
144
+
145
+ ### 1. Simulation
146
+
147
+ Preview future schedules without running them:
148
+
149
+ ```javascript
150
+ const events = automator.getActionsInRange(
151
+ new Date('2025-05-01'),
152
+ new Date('2025-05-07')
153
+ );
154
+
155
+ console.log(`${events.length} events will occur`);
156
+ ```
157
+
158
+ ### 2. Pluggable Storage
159
+
160
+ Choose or create storage backends:
161
+
162
+ ```javascript
163
+ // Memory storage (no persistence)
164
+ const automator = new Automator({
165
+ storage: Automator.storage.memory()
166
+ });
167
+
168
+ // File storage
169
+ const automator = new Automator({
170
+ storage: Automator.storage.file('./actions.json')
171
+ });
172
+
173
+ // Custom storage
174
+ const automator = new Automator({
175
+ storage: {
176
+ load: () => { /* load from DB */ },
177
+ save: (state) => { /* save to DB */ }
178
+ }
179
+ });
180
+ ```
181
+
182
+ ### 3. Action Description
183
+
184
+ Human-readable action summaries:
185
+
186
+ ```javascript
187
+ console.log(automator.describeAction(1));
188
+ // Action #1 - Morning Lights
189
+ // Command: turnLightOn
190
+ // Next run: 5/1/2025, 7:00:00 AM
191
+ // Executions: 15
192
+ // Buffered: true
193
+ // Recurrence: day
194
+ // DST policy: once
195
+ ```
196
+
197
+ ### 4. Better Event Payloads
198
+
199
+ Action events include rich metadata:
200
+
201
+ ```javascript
202
+ automator.on('action', (event) => {
203
+ console.log(event);
204
+ // {
205
+ // type: 'action',
206
+ // actionId: 1,
207
+ // name: 'My Action',
208
+ // cmd: 'myCmd',
209
+ // payload: {},
210
+ // scheduledTime: Date,
211
+ // actualTime: Date,
212
+ // count: 5
213
+ // }
214
+ });
215
+ ```
216
+
217
+ ### 5. Auto-Save Control
218
+
219
+ Fine-tune persistence:
220
+
221
+ ```javascript
222
+ const automator = new Automator({
223
+ storage: Automator.storage.file('./actions.json'),
224
+ autoSave: true,
225
+ saveInterval: 10000 // Save every 10 seconds
226
+ });
227
+ ```
228
+
229
+ ---
230
+
231
+ ## Migration Steps
232
+
233
+ ### Step 1: Update Initialization
234
+
235
+ Replace your v2 initialization with v3 constructor:
236
+
237
+ ```javascript
238
+ // Before
239
+ const automator = require('jw-automator');
240
+ automator.init({ file: './actions.json' });
241
+
242
+ // After
243
+ const Automator = require('jw-automator');
244
+ const automator = new Automator({
245
+ storage: Automator.storage.file('./actions.json')
246
+ });
247
+ ```
248
+
249
+ ### Step 2: Update Action Definitions
250
+
251
+ Add `dstPolicy` to actions with repeat:
252
+
253
+ ```javascript
254
+ // Before
255
+ automator.addAction({
256
+ cmd: 'myCmd',
257
+ date: new Date(),
258
+ repeat: { type: 'day', interval: 1 }
259
+ });
260
+
261
+ // After
262
+ automator.addAction({
263
+ cmd: 'myCmd',
264
+ date: new Date(),
265
+ repeat: {
266
+ type: 'day',
267
+ interval: 1,
268
+ dstPolicy: 'once' // Explicitly choose DST behavior
269
+ }
270
+ });
271
+ ```
272
+
273
+ ### Step 3: Update Event Listeners
274
+
275
+ Standardize event names:
276
+
277
+ ```javascript
278
+ // Before
279
+ automator.on('actionExecuted', (data) => { ... });
280
+
281
+ // After
282
+ automator.on('action', (event) => { ... });
283
+ ```
284
+
285
+ ### Step 4: Update API Calls
286
+
287
+ Use new method names:
288
+
289
+ ```javascript
290
+ // Before
291
+ automator.removeAction(id);
292
+
293
+ // After
294
+ automator.removeActionByID(id);
295
+ ```
296
+
297
+ ### Step 5: Test DST Behavior
298
+
299
+ Review actions that run during DST transitions and set appropriate `dstPolicy`:
300
+
301
+ - `'once'` - Run only the first occurrence during fall-back (recommended default)
302
+ - `'twice'` - Run both occurrences during fall-back
303
+
304
+ ### Step 6: Leverage New Features
305
+
306
+ Consider using:
307
+ - `getActionsInRange()` for calendar previews
308
+ - `describeAction()` for debugging
309
+ - Custom storage adapters for database persistence
310
+ - Update events for logging changes
311
+
312
+ ---
313
+
314
+ ## Compatibility Notes
315
+
316
+ ### What's the Same
317
+
318
+ - **Core concept**: Schedule actions with recurrence
319
+ - **Recurrence types**: second, minute, hour, day, week, month, year
320
+ - **Local time**: Still operates in local time
321
+ - **Buffered/unbuffered**: Still supported (as `unBuffered` flag)
322
+ - **Function registration**: Still use `addFunction()`
323
+
324
+ ### What's Different
325
+
326
+ - **Constructor**: Now uses `new Automator()`
327
+ - **Storage**: Explicitly configured
328
+ - **DST**: Explicit policy required
329
+ - **Events**: Standardized names and payloads
330
+ - **API**: More consistent naming (`ByID`, `ByName`)
331
+ - **IDs**: Auto-generated, not user-provided
332
+ - **State**: Better separation of spec vs. state
333
+
334
+ ---
335
+
336
+ ## Example: Complete Migration
337
+
338
+ **v2 Code:**
339
+ ```javascript
340
+ const automator = require('jw-automator');
341
+ automator.init({ file: './actions.json' });
342
+
343
+ automator.addFunction('turnLightOn', () => {
344
+ console.log('Light on');
345
+ });
346
+
347
+ automator.addAction({
348
+ cmd: 'turnLightOn',
349
+ date: new Date('2025-05-01T07:00:00'),
350
+ repeat: { type: 'day', interval: 1 }
351
+ });
352
+
353
+ automator.start();
354
+ ```
355
+
356
+ **v3 Code:**
357
+ ```javascript
358
+ const Automator = require('jw-automator');
359
+
360
+ const automator = new Automator({
361
+ storage: Automator.storage.file('./actions.json')
362
+ });
363
+
364
+ automator.addFunction('turnLightOn', () => {
365
+ console.log('Light on');
366
+ });
367
+
368
+ automator.addAction({
369
+ name: 'Morning Lights',
370
+ cmd: 'turnLightOn',
371
+ date: new Date('2025-05-01T07:00:00'),
372
+ unBuffered: false,
373
+ repeat: {
374
+ type: 'day',
375
+ interval: 1,
376
+ dstPolicy: 'once'
377
+ }
378
+ });
379
+
380
+ automator.start();
381
+ ```
382
+
383
+ ---
384
+
385
+ ## Getting Help
386
+
387
+ If you encounter issues during migration:
388
+
389
+ 1. Check the [README](../README.md) for full API documentation
390
+ 2. Review the [Architecture](./ARCHITECTURE.md) for design understanding
391
+ 3. Run the examples in the `examples/` directory
392
+ 4. File an issue on GitHub
393
+
394
+ ---
395
+
396
+ ## Why Rewrite?
397
+
398
+ v3 addresses several issues from v2:
399
+
400
+ - **Infinite loops**: Better safety guards
401
+ - **DST bugs**: Explicit, predictable handling
402
+ - **Catch-up logic**: More reliable offline behavior
403
+ - **Testability**: Deterministic core engine
404
+ - **Maintainability**: Cleaner architecture
405
+ - **Extensibility**: Pluggable storage, better API
406
+
407
+ The rewrite provides a solid foundation for long-term reliability.