0
# Event Management
1
2
Observable-based event system for monitoring task state changes, progress updates, lifecycle events, and custom event handling throughout task execution.
3
4
## Capabilities
5
6
### ListrEventManager
7
8
Global event manager for Listr-level events and cross-task communication.
9
10
```typescript { .api }
11
/**
12
* Global event manager for Listr-level events
13
* Extends the base EventManager with Listr-specific functionality
14
*/
15
class ListrEventManager extends EventManager {
16
/**
17
* Listen to Listr-level events
18
* @param event - Event name to listen for
19
* @param listener - Event handler function
20
*/
21
on(event: string, listener: (...args: any[]) => void): this;
22
23
/**
24
* Listen to event once
25
* @param event - Event name to listen for
26
* @param listener - Event handler function
27
*/
28
once(event: string, listener: (...args: any[]) => void): this;
29
30
/**
31
* Emit an event
32
* @param event - Event name to emit
33
* @param args - Arguments to pass to listeners
34
*/
35
emit(event: string, ...args: any[]): boolean;
36
37
/**
38
* Remove event listener
39
* @param event - Event name
40
* @param listener - Specific listener to remove
41
*/
42
off(event: string, listener: (...args: any[]) => void): this;
43
44
/**
45
* Remove all listeners for an event
46
* @param event - Event name
47
*/
48
removeAllListeners(event?: string): this;
49
}
50
```
51
52
### ListrTaskEventManager
53
54
Task-specific event manager for individual task lifecycle and state events.
55
56
```typescript { .api }
57
/**
58
* Task-specific event manager for individual task events
59
* Handles task lifecycle, state changes, and output events
60
*/
61
class ListrTaskEventManager extends EventManager {
62
/**
63
* Listen to task-specific events
64
* @param event - Task event type
65
* @param listener - Event handler function
66
*/
67
on(event: ListrTaskEventType, listener: (data: any) => void): this;
68
69
/**
70
* Listen to task event once
71
* @param event - Task event type
72
* @param listener - Event handler function
73
*/
74
once(event: ListrTaskEventType, listener: (data: any) => void): this;
75
76
/**
77
* Emit a task event
78
* @param event - Task event type
79
* @param data - Event data
80
*/
81
emit(event: ListrTaskEventType, data: any): boolean;
82
}
83
```
84
85
### Base EventManager
86
87
Generic event management functionality providing the foundation for all event handling.
88
89
```typescript { .api }
90
/**
91
* Generic base event manager
92
* Provides core event handling functionality
93
*/
94
class EventManager {
95
/**
96
* Add event listener
97
* @param event - Event name
98
* @param listener - Event handler function
99
*/
100
on(event: string, listener: (...args: any[]) => void): this;
101
102
/**
103
* Add one-time event listener
104
* @param event - Event name
105
* @param listener - Event handler function
106
*/
107
once(event: string, listener: (...args: any[]) => void): this;
108
109
/**
110
* Emit an event to all listeners
111
* @param event - Event name
112
* @param args - Arguments to pass to listeners
113
*/
114
emit(event: string, ...args: any[]): boolean;
115
116
/**
117
* Remove specific event listener
118
* @param event - Event name
119
* @param listener - Listener function to remove
120
*/
121
off(event: string, listener: (...args: any[]) => void): this;
122
123
/**
124
* Remove all listeners for an event or all events
125
* @param event - Optional specific event name
126
*/
127
removeAllListeners(event?: string): this;
128
129
/**
130
* Get array of listeners for an event
131
* @param event - Event name
132
*/
133
listeners(event: string): Function[];
134
135
/**
136
* Get count of listeners for an event
137
* @param event - Event name
138
*/
139
listenerCount(event: string): number;
140
}
141
```
142
143
### Task Event Types
144
145
Enumeration of all possible task-related events that can be monitored.
146
147
```typescript { .api }
148
/**
149
* Task event types for monitoring task lifecycle
150
*/
151
enum ListrTaskEventType {
152
/** Task title changed */
153
TITLE = 'TITLE',
154
/** Task state changed */
155
STATE = 'STATE',
156
/** Task enabled status changed */
157
ENABLED = 'ENABLED',
158
/** Subtask added or modified */
159
SUBTASK = 'SUBTASK',
160
/** Prompt started or updated */
161
PROMPT = 'PROMPT',
162
/** Task output updated */
163
OUTPUT = 'OUTPUT',
164
/** Task message updated */
165
MESSAGE = 'MESSAGE',
166
/** Task closed or completed */
167
CLOSED = 'CLOSED'
168
}
169
```
170
171
### Event Data Interfaces
172
173
Type definitions for event data structures passed to event handlers.
174
175
```typescript { .api }
176
/**
177
* Base event data structure
178
*/
179
interface ListrEvent {
180
/** Timestamp when event occurred */
181
timestamp: Date;
182
/** Task ID that generated the event */
183
taskId: string;
184
/** Event type */
185
type: ListrTaskEventType;
186
}
187
188
/**
189
* Task state change event data
190
*/
191
interface ListrTaskStateEvent extends ListrEvent {
192
type: ListrTaskEventType.STATE;
193
/** Previous task state */
194
previousState: ListrTaskState;
195
/** New task state */
196
newState: ListrTaskState;
197
/** Additional state metadata */
198
metadata?: Record<string, any>;
199
}
200
201
/**
202
* Task title change event data
203
*/
204
interface ListrTaskTitleEvent extends ListrEvent {
205
type: ListrTaskEventType.TITLE;
206
/** Previous title */
207
previousTitle?: string;
208
/** New title */
209
newTitle: string;
210
}
211
212
/**
213
* Task output event data
214
*/
215
interface ListrTaskOutputEvent extends ListrEvent {
216
type: ListrTaskEventType.OUTPUT;
217
/** Output content */
218
output: string;
219
/** Whether this is persistent output */
220
persistent?: boolean;
221
}
222
223
/**
224
* Task message event data
225
*/
226
interface ListrTaskMessageEvent extends ListrEvent {
227
type: ListrTaskEventType.MESSAGE;
228
/** Message content */
229
message: string;
230
/** Message level or type */
231
level?: 'info' | 'warn' | 'error';
232
}
233
```
234
235
**Usage Examples:**
236
237
### Basic Event Listening
238
239
```typescript
240
import { Listr, ListrTaskEventType } from "listr2";
241
242
const tasks = new Listr([
243
{
244
title: "Task with events",
245
task: async (ctx, task) => {
246
task.output = "Starting...";
247
await new Promise(resolve => setTimeout(resolve, 1000));
248
task.output = "Processing...";
249
await new Promise(resolve => setTimeout(resolve, 1000));
250
task.output = "Completed!";
251
}
252
}
253
]);
254
255
// Listen to task state changes
256
tasks.events.on('TASK_STATE_CHANGED', (event) => {
257
console.log(`Task ${event.taskId} changed from ${event.previousState} to ${event.newState}`);
258
});
259
260
// Listen to task output updates
261
tasks.events.on('TASK_OUTPUT_UPDATED', (event) => {
262
console.log(`Task ${event.taskId} output: ${event.output}`);
263
});
264
265
await tasks.run();
266
```
267
268
### Advanced Event Monitoring
269
270
```typescript
271
import { Listr, ListrTaskEventType, ListrTaskState } from "listr2";
272
273
interface MonitoringContext {
274
startTime: Date;
275
completedTasks: number;
276
failedTasks: number;
277
}
278
279
const tasks = new Listr<MonitoringContext>([
280
{
281
title: "Task 1",
282
task: () => new Promise(resolve => setTimeout(resolve, 1000))
283
},
284
{
285
title: "Task 2",
286
task: () => new Promise((resolve, reject) => {
287
setTimeout(() => Math.random() > 0.5 ? resolve(undefined) : reject(new Error("Random failure")), 1000);
288
})
289
},
290
{
291
title: "Task 3",
292
task: () => new Promise(resolve => setTimeout(resolve, 1000))
293
}
294
]);
295
296
// Track task completion statistics
297
tasks.events.on('TASK_STATE_CHANGED', (event) => {
298
if (event.newState === ListrTaskState.COMPLETED) {
299
console.log(`β Task completed: ${event.taskId}`);
300
} else if (event.newState === ListrTaskState.FAILED) {
301
console.log(`β Task failed: ${event.taskId}`);
302
} else if (event.newState === ListrTaskState.STARTED) {
303
console.log(`π Task started: ${event.taskId}`);
304
}
305
});
306
307
// Monitor overall progress
308
let totalTasks = 0;
309
let completedTasks = 0;
310
311
tasks.events.on('TASK_CREATED', () => totalTasks++);
312
tasks.events.on('TASK_STATE_CHANGED', (event) => {
313
if (event.newState === ListrTaskState.COMPLETED || event.newState === ListrTaskState.SKIPPED) {
314
completedTasks++;
315
console.log(`Progress: ${completedTasks}/${totalTasks} tasks completed`);
316
}
317
});
318
319
const result = await tasks.run({
320
startTime: new Date(),
321
completedTasks: 0,
322
failedTasks: 0
323
});
324
```
325
326
### Custom Event Emitters
327
328
```typescript
329
import { Listr } from "listr2";
330
331
const tasks = new Listr([
332
{
333
title: "Task with custom events",
334
task: async (ctx, task) => {
335
// Emit custom events through the task's event manager
336
task.events.emit('CUSTOM_PROGRESS', { progress: 0, message: "Starting..." });
337
338
for (let i = 1; i <= 5; i++) {
339
await new Promise(resolve => setTimeout(resolve, 500));
340
task.events.emit('CUSTOM_PROGRESS', {
341
progress: i * 20,
342
message: `Step ${i}/5 completed`
343
});
344
task.output = `Progress: ${i * 20}%`;
345
}
346
347
task.events.emit('CUSTOM_COMPLETE', {
348
duration: Date.now() - startTime,
349
result: "success"
350
});
351
}
352
}
353
]);
354
355
// Listen to custom events
356
tasks.events.on('CUSTOM_PROGRESS', (data) => {
357
console.log(`Custom progress: ${data.progress}% - ${data.message}`);
358
});
359
360
tasks.events.on('CUSTOM_COMPLETE', (data) => {
361
console.log(`Custom completion: ${data.result} (took ${data.duration}ms)`);
362
});
363
364
const startTime = Date.now();
365
await tasks.run();
366
```
367
368
### Event-Based Task Coordination
369
370
```typescript
371
import { Listr } from "listr2";
372
373
interface SharedContext {
374
phase1Complete: boolean;
375
phase2Data?: any;
376
finalResults?: any[];
377
}
378
379
const tasks = new Listr<SharedContext>([
380
{
381
title: "Phase 1: Preparation",
382
task: async (ctx, task) => {
383
task.output = "Preparing data...";
384
await new Promise(resolve => setTimeout(resolve, 1000));
385
386
ctx.phase1Complete = true;
387
388
// Emit event to signal phase 1 completion
389
task.events.emit('PHASE_1_COMPLETE', {
390
timestamp: new Date(),
391
data: "preparation-data"
392
});
393
394
task.output = "Phase 1 completed";
395
}
396
},
397
{
398
title: "Phase 2: Processing",
399
task: async (ctx, task) => {
400
// Wait for phase 1 to complete
401
if (!ctx.phase1Complete) {
402
await new Promise(resolve => {
403
task.events.once('PHASE_1_COMPLETE', resolve);
404
});
405
}
406
407
task.output = "Processing data...";
408
await new Promise(resolve => setTimeout(resolve, 1500));
409
410
ctx.phase2Data = { processed: true, items: 10 };
411
412
task.events.emit('PHASE_2_COMPLETE', ctx.phase2Data);
413
task.output = "Phase 2 completed";
414
}
415
},
416
{
417
title: "Phase 3: Finalization",
418
task: async (ctx, task) => {
419
// Wait for phase 2 data
420
if (!ctx.phase2Data) {
421
await new Promise(resolve => {
422
task.events.once('PHASE_2_COMPLETE', (data) => {
423
ctx.phase2Data = data;
424
resolve(undefined);
425
});
426
});
427
}
428
429
task.output = "Finalizing results...";
430
await new Promise(resolve => setTimeout(resolve, 800));
431
432
ctx.finalResults = Array.from({ length: ctx.phase2Data.items }, (_, i) => `result-${i}`);
433
task.output = `Finalized ${ctx.finalResults.length} results`;
434
}
435
}
436
]);
437
438
await tasks.run({ phase1Complete: false });
439
```
440
441
### Error Event Handling
442
443
```typescript
444
import { Listr, ListrTaskState } from "listr2";
445
446
const tasks = new Listr([
447
{
448
title: "Potentially failing task",
449
retry: 3,
450
task: async (ctx, task) => {
451
if (Math.random() < 0.7) {
452
throw new Error("Simulated failure");
453
}
454
return "Success";
455
}
456
}
457
]);
458
459
// Handle error events
460
tasks.events.on('TASK_ERROR', (event) => {
461
console.log(`Task error: ${event.error.message}`);
462
console.log(`Will retry: ${event.willRetry}`);
463
console.log(`Retry count: ${event.retryCount}`);
464
});
465
466
// Handle retry events
467
tasks.events.on('TASK_RETRY', (event) => {
468
console.log(`Retrying task: ${event.taskId} (attempt ${event.attempt})`);
469
});
470
471
// Handle state changes for detailed error tracking
472
tasks.events.on('TASK_STATE_CHANGED', (event) => {
473
if (event.newState === ListrTaskState.FAILED) {
474
console.log(`Task failed permanently: ${event.taskId}`);
475
} else if (event.newState === ListrTaskState.RETRY) {
476
console.log(`Task entering retry state: ${event.taskId}`);
477
}
478
});
479
480
try {
481
await tasks.run();
482
} catch (error) {
483
console.log("Task list failed:", error.message);
484
}
485
```
486
487
## Types
488
489
```typescript { .api }
490
/**
491
* Event listener function type
492
*/
493
type EventListener = (...args: any[]) => void;
494
495
/**
496
* Event map interface for type-safe event handling
497
*/
498
interface ListrEventMap {
499
/** Task state changed */
500
[ListrTaskEventType.STATE]: ListrTaskStateEvent;
501
/** Task title changed */
502
[ListrTaskEventType.TITLE]: ListrTaskTitleEvent;
503
/** Task output updated */
504
[ListrTaskEventType.OUTPUT]: ListrTaskOutputEvent;
505
/** Task message updated */
506
[ListrTaskEventType.MESSAGE]: ListrTaskMessageEvent;
507
/** Task enabled status changed */
508
[ListrTaskEventType.ENABLED]: { taskId: string; enabled: boolean };
509
/** Subtask event */
510
[ListrTaskEventType.SUBTASK]: { taskId: string; subtask: any };
511
/** Prompt event */
512
[ListrTaskEventType.PROMPT]: { taskId: string; prompt: any };
513
/** Task closed */
514
[ListrTaskEventType.CLOSED]: { taskId: string; timestamp: Date };
515
}
516
517
/**
518
* Task event map interface for task-specific events
519
*/
520
interface ListrTaskEventMap {
521
/** Task was created */
522
TASK_CREATED: { taskId: string; title: string };
523
/** Task state changed */
524
TASK_STATE_CHANGED: ListrTaskStateEvent;
525
/** Task encountered an error */
526
TASK_ERROR: { taskId: string; error: Error; willRetry: boolean; retryCount: number };
527
/** Task is retrying */
528
TASK_RETRY: { taskId: string; attempt: number; maxAttempts: number };
529
/** Task output was updated */
530
TASK_OUTPUT_UPDATED: ListrTaskOutputEvent;
531
}
532
```