jw-automator 4.0.0 → 6.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/docs/MIGRATION.md CHANGED
@@ -1,3 +1,85 @@
1
+ # Migration Guide
2
+
3
+ ## v5.0.0 - Storage API Simplification & Moratorium-Based Persistence
4
+
5
+ ### Breaking Change: Storage API Simplified
6
+
7
+ The pluggable storage adapter pattern has been removed in favor of a simpler direct configuration.
8
+
9
+ **Old API (v4):**
10
+ ```javascript
11
+ const automator = new Automator({
12
+ storage: Automator.storage.file('./tasks.json')
13
+ });
14
+
15
+ const automator = new Automator({
16
+ storage: Automator.storage.memory()
17
+ });
18
+ ```
19
+
20
+ **New API (v5):**
21
+ ```javascript
22
+ // File storage
23
+ const automator = new Automator({
24
+ storageFile: './tasks.json'
25
+ });
26
+
27
+ // Memory-only mode
28
+ const automator = new Automator({
29
+ // No storageFile option = memory-only
30
+ });
31
+ ```
32
+
33
+ ### New: Moratorium-Based Persistence
34
+
35
+ v5 introduces a moratorium-based persistence state machine that reduces disk wear:
36
+
37
+ - **CRUD operations** (add/update/remove) save immediately and start a moratorium period
38
+ - **Task execution** (state progression) marks state as dirty and saves if moratorium has expired
39
+ - If moratorium is active, dirty state waits until moratorium ends, then saves automatically
40
+ - **Default `saveInterval`** changed from 5000ms to **15000ms (15 seconds)**
41
+ - `saveInterval` now defines the moratorium period (minimum cooling time between saves)
42
+ - `stop()` always saves immediately if dirty, ignoring any active moratorium
43
+
44
+ **Why this matters:** Reduces disk writes from task execution (critical for SD cards/flash media) while ensuring CRUD changes are persisted immediately. The moratorium-based approach eliminates wasteful periodic polling.
45
+
46
+ ### Changes Required
47
+
48
+ 1. **File storage:** Replace `storage: Automator.storage.file(path)` with `storageFile: path`
49
+ 2. **Memory storage:** Remove `storage: Automator.storage.memory()` entirely (omit `storageFile`)
50
+ 3. **Custom storage adapters:** No longer supported via plug-in interface
51
+ 4. **`saveInterval` default:** Changed from 5000ms to 15000ms (update if you relied on the old default)
52
+
53
+ ### Custom Storage Migration
54
+
55
+ If you were using custom storage adapters, use this pattern instead:
56
+
57
+ ```javascript
58
+ const automator = new Automator(); // Memory-only
59
+
60
+ // Load from custom source on initialization
61
+ automator.seed(async (auto) => {
62
+ const tasks = await yourCustomLoad();
63
+ tasks.forEach(task => auto.addTask(task));
64
+ });
65
+
66
+ // Save on updates
67
+ automator.on('update', async () => {
68
+ const tasks = automator.getTasks();
69
+ await yourCustomSave(tasks);
70
+ });
71
+ ```
72
+
73
+ ### Removed
74
+
75
+ - `Automator.storage.file()` static method
76
+ - `Automator.storage.memory()` static method
77
+ - `FileStorage` class (integrated into Automator)
78
+ - `MemoryStorage` class (no longer needed)
79
+ - Custom storage adapter interface (`{ load(), save() }`)
80
+
81
+ ---
82
+
1
83
  # Migration Guide: v3 to v4
2
84
 
3
85
  This guide helps you migrate from jw-automator v3 to v4.
@@ -6,7 +88,7 @@ This guide helps you migrate from jw-automator v3 to v4.
6
88
 
7
89
  ## Overview
8
90
 
9
- jw-automator v4 is a significant refinement of v3's architecture, focusing on making the scheduler's behavior even more predictable and robust out-of-the-box. While v3 represented a complete rewrite, v4 introduces breaking changes by refining default behaviors and error handling for action specifications.
91
+ jw-automator v4 is a significant refinement of v3's architecture, focusing on making the scheduler's behavior even more predictable and robust out-of-the-box. While v3 represented a complete rewrite, v4 introduces breaking changes by refining default behaviors and error handling for task specifications.
10
92
 
11
93
  v3 was the widely-deployed production version. v4 builds upon that foundation with enhanced predictability.
12
94
 
@@ -17,18 +99,18 @@ v3 was the widely-deployed production version. v4 builds upon that foundation wi
17
99
  ### 1. Default `catchUpWindow` Behavior Changed
18
100
 
19
101
  **v3:** If `catchUpWindow` was not specified, it defaulted to `"unlimited"` (catch up all missed executions).
20
- **v4:** If `catchUpWindow` is not specified, it now uses a **smart default** based on the action type:
21
- - For **recurring actions**, it defaults to the **duration of the recurrence interval** (e.g., an hourly action gets a 1-hour `catchUpWindow`).
22
- - For **one-time actions**, it defaults to **`0`** (skip all missed executions).
102
+ **v4:** If `catchUpWindow` is not specified, it now uses a **smart default** based on the task type:
103
+ - For **recurring tasks**, it defaults to the **duration of the recurrence interval** (e.g., an hourly task gets a 1-hour `catchUpWindow`).
104
+ - For **one-time tasks**, it defaults to **`0`** (skip all missed executions).
23
105
 
24
- **Impact:** User applications that relied on the implicit "unlimited" catch-up for all actions in v3 might now see actions being skipped or fast-forwarded more aggressively. If you desire the old "unlimited" catch-up, you must explicitly set `catchUpWindow: "unlimited"`.
106
+ **Impact:** User applications that relied on the implicit "unlimited" catch-up for all tasks in v3 might now see tasks being skipped or fast-forwarded more aggressively. If you desire the old "unlimited" catch-up, you must explicitly set `catchUpWindow: "unlimited"`.
25
107
 
26
108
  ### 2. Invalid `repeat.type` Throws a Fatal Error
27
109
 
28
110
  **v3:** If `repeat.type` was missing or invalid (e.g., a typo like `'horu'`), Automator would defensively coerce it to `'day'` and emit an `error` event.
29
111
  **v4:** An invalid or missing `repeat.type` now throws a hard `Error` immediately.
30
112
 
31
- **Impact:** Code that previously succeeded by silently allowing `repeat.type` coercions will now fail fast, forcing explicit correction. This ensures that the action's intent is never misinterpreted.
113
+ **Impact:** Code that previously succeeded by silently allowing `repeat.type` coercions will now fail fast, forcing explicit correction. This ensures that the task's intent is never misinterpreted.
32
114
 
33
115
  ### 3. Coercion Events Changed from `error` to `warning`
34
116
 
@@ -37,36 +119,64 @@ v3 was the widely-deployed production version. v4 builds upon that foundation wi
37
119
 
38
120
  **Impact:** If your application was listening for `automator.on('error', ...)` to catch these coercion notifications, you must now update your event listener to `automator.on('warning', ...)` to continue receiving them.
39
121
 
122
+ ### 4. API Method Names Changed (action → task)
123
+
124
+ **v3:** Methods used "action" terminology: `addAction`, `updateActionByID`, `getActions`, etc.
125
+ **v4:** All methods now use "task" terminology: `addTask`, `updateTaskByID`, `getTasks`, etc.
126
+
127
+ **Impact:** All API calls need to be updated to use the new method names.
128
+
129
+ ### 5. Event Name Changed
130
+
131
+ **v3:** Task execution emitted an `'action'` event.
132
+ **v4:** Task execution now emits a `'task'` event.
133
+
134
+ **Impact:** Update event listeners from `automator.on('action', ...)` to `automator.on('task', ...)`.
135
+
136
+ ### 6. Event Object Properties Changed
137
+
138
+ **v3:** Event objects contained `actionId` and `action` properties.
139
+ **v4:** Event objects now contain `taskId` and `task` properties.
140
+
141
+ **Impact:** Update code that accesses these properties in event handlers.
142
+
143
+ ### 7. State Structure Changed
144
+
145
+ **v3:** State contained an `actions` array.
146
+ **v4:** State now contains a `tasks` array.
147
+
148
+ **Impact:** Custom storage adapters need to return `{ tasks: [...] }` instead of `{ actions: [...] }`. Existing stored state files need migration.
149
+
40
150
  ---
41
151
 
42
- ## Breaking Changes from v3 to v4
152
+ ## Breaking Changes from v2 to v3
43
153
 
44
154
  ### 1. Constructor and Initialization
45
155
 
46
156
  **v2:**
47
157
  ```javascript
48
158
  const automator = require('jw-automator');
49
- automator.init({ file: './actions.json' });
159
+ automator.init({ file: './tasks.json' });
50
160
  ```
51
161
 
52
162
  **v3:**
53
163
  ```javascript
54
164
  const Automator = require('jw-automator');
55
165
  const automator = new Automator({
56
- storage: Automator.storage.file('./actions.json')
166
+ storage: Automator.storage.file('./tasks.json')
57
167
  });
58
168
  ```
59
169
 
60
- ### 2. Action Structure
170
+ ### 2. Task Structure
61
171
 
62
172
  **v2:**
63
- Action structure was less formalized.
173
+ Task structure was less formalized.
64
174
 
65
175
  **v3:**
66
176
  ```javascript
67
177
  {
68
178
  id: 1, // Auto-generated
69
- name: 'My Action', // Optional
179
+ name: 'My Task', // Optional
70
180
  cmd: 'commandName', // Required
71
181
  payload: {}, // Optional
72
182
  date: Date, // Required (or null for immediate)
@@ -105,84 +215,86 @@ Various event names.
105
215
  **v3:**
106
216
  Standardized events:
107
217
  - `ready` - Scheduler started
108
- - `action` - Action executed
109
- - `update` - Action added/updated/removed
218
+ - `task` - Task executed
219
+ - `update` - Task added/updated/removed
110
220
  - `error` - Error occurred
111
221
  - `debug` - Debug information
112
222
 
113
223
  ### 5. API Methods
114
224
 
115
- #### Adding Actions
225
+ #### Adding Tasks
116
226
 
117
227
  **v2:**
118
228
  ```javascript
119
- automator.addAction(actionObject);
229
+ automator.addTask(taskObject);
120
230
  ```
121
231
 
122
232
  **v3:**
123
233
  ```javascript
124
- const id = automator.addAction(actionSpec);
125
- // Returns the action ID
234
+ const id = automator.addTask(taskSpec);
235
+ // Returns the task ID
126
236
  ```
127
237
 
128
- #### Getting Actions
238
+ #### Getting Tasks
129
239
 
130
240
  **v2:**
131
241
  ```javascript
132
- const actions = automator.getActions();
242
+ const tasks = automator.getTasks();
133
243
  ```
134
244
 
135
245
  **v3:**
136
246
  ```javascript
137
- const actions = automator.getActions(); // Deep copy
138
- const action = automator.getActionByID(id);
139
- const actions = automator.getActionsByName('name');
247
+ const tasks = automator.getTasks(); // Deep copy
248
+ const task = automator.getTaskByID(id);
249
+ const tasks = automator.getTasksByName('name');
140
250
  ```
141
251
 
142
- #### Removing Actions
252
+ #### Removing Tasks
143
253
 
144
254
  **v2:**
145
255
  ```javascript
146
- automator.removeAction(id);
256
+ automator.removeTask(id);
147
257
  ```
148
258
 
149
259
  **v3:**
150
260
  ```javascript
151
- automator.removeActionByID(id);
152
- automator.removeActionByName('name'); // Returns count removed
261
+ automator.removeTaskByID(id);
262
+ automator.removeTaskByName('name'); // Returns count removed
153
263
  ```
154
264
 
155
- #### Updating Actions
265
+ #### Updating Tasks
156
266
 
157
267
  **v2:**
158
268
  Limited update capability.
159
269
 
160
270
  **v3:**
161
271
  ```javascript
162
- automator.updateActionByID(id, {
272
+ automator.updateTaskByID(id, {
163
273
  name: 'New Name',
164
274
  repeat: { type: 'hour', interval: 2 }
165
275
  });
166
276
 
167
- automator.updateActionByName('Old Name', {
277
+ automator.updateTaskByName('Old Name', {
168
278
  name: 'New Name'
169
279
  });
170
280
  ```
171
281
 
172
282
  ---
173
283
 
174
- ## New Features in v3
175
-
176
284
  ## New Features in v4
177
285
 
178
286
  ### 1. Smart `catchUpWindow` Defaults
179
287
 
180
- The new intelligent default system for `catchUpWindow` means that for most actions, you no longer need to explicitly define this property to get predictable, sensible behavior. It automatically adapts based on whether your action is one-time or recurring.
288
+ The new intelligent default system for `catchUpWindow` means that for most tasks, you no longer need to explicitly define this property to get predictable, sensible behavior. It automatically adapts based on whether your task is one-time or recurring.
181
289
 
182
290
  ### 2. Dedicated `warning` Event
183
291
 
184
292
  A new `warning` event (`automator.on('warning', ...)`) provides a clearer channel for non-fatal feedback about defensive coercions. This allows developers to distinguish between critical runtime errors and minor data corrections.
185
293
 
294
+ ### 3. Cleaner "Task" Terminology
295
+
296
+ The rename from "action" to "task" provides clearer, more intuitive naming throughout the API.
297
+
186
298
  ---
187
299
 
188
300
  ## New Features in v3
@@ -192,7 +304,7 @@ A new `warning` event (`automator.on('warning', ...)`) provides a clearer channe
192
304
  Preview future schedules without running them:
193
305
 
194
306
  ```javascript
195
- const events = automator.getActionsInRange(
307
+ const events = automator.getTasksInRange(
196
308
  new Date('2025-05-01'),
197
309
  new Date('2025-05-07')
198
310
  );
@@ -212,7 +324,7 @@ const automator = new Automator({
212
324
 
213
325
  // File storage
214
326
  const automator = new Automator({
215
- storage: Automator.storage.file('./actions.json')
327
+ storage: Automator.storage.file('./tasks.json')
216
328
  });
217
329
 
218
330
  // Custom storage
@@ -224,13 +336,13 @@ const automator = new Automator({
224
336
  });
225
337
  ```
226
338
 
227
- ### 3. Action Description
339
+ ### 3. Task Description
228
340
 
229
- Human-readable action summaries:
341
+ Human-readable task summaries:
230
342
 
231
343
  ```javascript
232
- console.log(automator.describeAction(1));
233
- // Action #1 - Morning Lights
344
+ console.log(automator.describeTask(1));
345
+ // Task #1 - Morning Lights
234
346
  // Command: turnLightOn
235
347
  // Next run: 5/1/2025, 7:00:00 AM
236
348
  // Executions: 15
@@ -241,15 +353,15 @@ console.log(automator.describeAction(1));
241
353
 
242
354
  ### 4. Better Event Payloads
243
355
 
244
- Action events include rich metadata:
356
+ Task events include rich metadata:
245
357
 
246
358
  ```javascript
247
- automator.on('action', (event) => {
359
+ automator.on('task', (event) => {
248
360
  console.log(event);
249
361
  // {
250
- // type: 'action',
251
- // actionId: 1,
252
- // name: 'My Action',
362
+ // type: 'task',
363
+ // taskId: 1,
364
+ // name: 'My Task',
253
365
  // cmd: 'myCmd',
254
366
  // payload: {},
255
367
  // scheduledTime: Date,
@@ -265,7 +377,7 @@ Fine-tune persistence:
265
377
 
266
378
  ```javascript
267
379
  const automator = new Automator({
268
- storage: Automator.storage.file('./actions.json'),
380
+ storage: Automator.storage.file('./tasks.json'),
269
381
  autoSave: true,
270
382
  saveInterval: 10000 // Save every 10 seconds
271
383
  });
@@ -279,20 +391,69 @@ const automator = new Automator({
279
391
 
280
392
  Thoroughly review the "Breaking Changes from v3 to v4" section above. Identify which changes affect your application.
281
393
 
282
- ### Step 2: Review `catchUpWindow` Usage
394
+ ### Step 2: Update API Method Names
283
395
 
284
- If your v3 application relied on the implicit "unlimited" catch-up for actions where `catchUpWindow` was not explicitly set, you will need to:
285
-
286
- - **Explicitly set `catchUpWindow: "unlimited"`** for those actions if you wish to maintain the old behavior.
287
- - Otherwise, understand that these actions will now use the new smart defaults (recurrence interval for recurring, `0` for one-time actions).
396
+ Replace all "action" method calls with "task" equivalents:
397
+ - `addAction()` → `addTask()`
398
+ - `updateActionByID()` `updateTaskByID()`
399
+ - `updateActionByName()` `updateTaskByName()`
400
+ - `removeActionByID()` → `removeTaskByID()`
401
+ - `removeActionByName()` → `removeTaskByName()`
402
+ - `getActions()` → `getTasks()`
403
+ - `getActionsByName()` → `getTasksByName()`
404
+ - `getActionByID()` → `getTaskByID()`
405
+ - `getActionsInRange()` → `getTasksInRange()`
406
+ - `describeAction()` → `describeTask()`
288
407
 
289
408
  ### Step 3: Update Event Listeners
290
409
 
410
+ Change event listeners from `'action'` to `'task'`:
411
+ ```javascript
412
+ // v3
413
+ automator.on('action', (event) => { ... });
414
+
415
+ // v4
416
+ automator.on('task', (event) => { ... });
417
+ ```
418
+
419
+ ### Step 4: Update Event Property Access
420
+
421
+ Update code that accesses event properties:
422
+ ```javascript
423
+ // v3
424
+ event.actionId
425
+ event.action
426
+
427
+ // v4
428
+ event.taskId
429
+ event.task
430
+ ```
431
+
432
+ ### Step 5: Migrate Stored State
433
+
434
+ If using file storage, update existing state files:
435
+ ```javascript
436
+ // v3 format
437
+ { "actions": [...] }
438
+
439
+ // v4 format
440
+ { "tasks": [...] }
441
+ ```
442
+
443
+ ### Step 6: Review `catchUpWindow` Usage
444
+
445
+ If your v3 application relied on the implicit "unlimited" catch-up for tasks where `catchUpWindow` was not explicitly set, you will need to:
446
+
447
+ - **Explicitly set `catchUpWindow: "unlimited"`** for those tasks if you wish to maintain the old behavior.
448
+ - Otherwise, understand that these tasks will now use the new smart defaults (recurrence interval for recurring, `0` for one-time tasks).
449
+
450
+ ### Step 7: Update Warning Event Listeners
451
+
291
452
  If your application was listening for `automator.on('error', ...)` to catch notifications about defensive coercions (e.g., invalid `repeat.interval`), you must now update your code to listen for `automator.on('warning', ...)` to receive these non-fatal messages.
292
453
 
293
- ### Step 4: Correct Invalid `repeat.type` Definitions
454
+ ### Step 8: Correct Invalid `repeat.type` Definitions
294
455
 
295
- If your application previously used actions with invalid or missing `repeat.type` values that were silently corrected in v3, you must now explicitly fix these `repeat.type` values. Failure to do so will result in a hard `Error` when the action is added or updated in v4.
456
+ If your application previously used tasks with invalid or missing `repeat.type` values that were silently corrected in v3, you must now explicitly fix these `repeat.type` values. Failure to do so will result in a hard `Error` when the task is added or updated in v4.
296
457
 
297
458
  ---
298
459
 
@@ -300,15 +461,18 @@ If your application previously used actions with invalid or missing `repeat.type
300
461
 
301
462
  ### What's the Same (v3 to v4)
302
463
 
303
- - **Core concepts**: Schedule actions with recurrence
464
+ - **Core concepts**: Schedule tasks with recurrence
304
465
  - **Recurrence types**: `second`, `minute`, `hour`, `day`, `week`, `month`, `year`
305
466
  - **Local time**: Still operates in local time
306
- - **API methods**: Names and signatures of `addAction()`, `updateActionByID()`, etc., remain the same.
307
467
  - **Function registration**: Still use `addFunction()`
308
468
  - **Legacy `unBuffered`**: Still supported as an alias, mapping to `catchUpWindow`.
309
469
 
310
470
  ### What's Different (v3 to v4)
311
471
 
472
+ - **API terminology**: "action" → "task" throughout
473
+ - **Event name**: `'action'` → `'task'`
474
+ - **Event properties**: `actionId` → `taskId`, `action` → `task`
475
+ - **State structure**: `actions` → `tasks`
312
476
  - **`catchUpWindow` Default Behavior**: Now smart and context-aware, instead of always `"unlimited"`.
313
477
  - **`repeat.type` Validation**: Invalid types now throw a fatal `Error`.
314
478
  - **Event Handling**: Coercions now emit `warning` events instead of `error` events.