@push.rocks/taskbuffer 3.2.0 → 3.4.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.
Files changed (49) hide show
  1. package/LICENSE +1 -1
  2. package/dist_ts/00_commitinfo_data.js +1 -1
  3. package/dist_ts_web/ts/index.d.ts +13 -0
  4. package/dist_ts_web/ts/index.js +12 -0
  5. package/dist_ts_web/ts/taskbuffer.classes.bufferrunner.d.ts +8 -0
  6. package/dist_ts_web/ts/taskbuffer.classes.bufferrunner.js +28 -0
  7. package/dist_ts_web/ts/taskbuffer.classes.cyclecounter.d.ts +13 -0
  8. package/dist_ts_web/ts/taskbuffer.classes.cyclecounter.js +31 -0
  9. package/dist_ts_web/ts/taskbuffer.classes.distributedcoordinator.d.ts +27 -0
  10. package/dist_ts_web/ts/taskbuffer.classes.distributedcoordinator.js +5 -0
  11. package/dist_ts_web/ts/taskbuffer.classes.task.d.ts +86 -0
  12. package/dist_ts_web/ts/taskbuffer.classes.task.js +257 -0
  13. package/dist_ts_web/ts/taskbuffer.classes.taskchain.d.ts +14 -0
  14. package/dist_ts_web/ts/taskbuffer.classes.taskchain.js +51 -0
  15. package/dist_ts_web/ts/taskbuffer.classes.taskdebounced.d.ts +10 -0
  16. package/dist_ts_web/ts/taskbuffer.classes.taskdebounced.js +20 -0
  17. package/dist_ts_web/ts/taskbuffer.classes.taskmanager.d.ts +49 -0
  18. package/dist_ts_web/ts/taskbuffer.classes.taskmanager.js +208 -0
  19. package/dist_ts_web/ts/taskbuffer.classes.taskonce.d.ts +11 -0
  20. package/dist_ts_web/ts/taskbuffer.classes.taskonce.js +20 -0
  21. package/dist_ts_web/ts/taskbuffer.classes.taskparallel.d.ts +7 -0
  22. package/dist_ts_web/ts/taskbuffer.classes.taskparallel.js +23 -0
  23. package/dist_ts_web/ts/taskbuffer.classes.taskrunner.d.ts +30 -0
  24. package/dist_ts_web/ts/taskbuffer.classes.taskrunner.js +54 -0
  25. package/dist_ts_web/ts/taskbuffer.classes.taskstep.d.ts +27 -0
  26. package/dist_ts_web/ts/taskbuffer.classes.taskstep.js +37 -0
  27. package/dist_ts_web/ts/taskbuffer.interfaces.d.ts +36 -0
  28. package/dist_ts_web/ts/taskbuffer.interfaces.js +2 -0
  29. package/dist_ts_web/ts/taskbuffer.logging.d.ts +2 -0
  30. package/dist_ts_web/ts/taskbuffer.logging.js +3 -0
  31. package/dist_ts_web/ts/taskbuffer.plugins.d.ts +8 -0
  32. package/dist_ts_web/ts/taskbuffer.plugins.js +9 -0
  33. package/dist_ts_web/ts_web/00_commitinfo_data.d.ts +8 -0
  34. package/dist_ts_web/ts_web/00_commitinfo_data.js +9 -0
  35. package/dist_ts_web/ts_web/demorunner.d.ts +1 -0
  36. package/dist_ts_web/ts_web/demorunner.js +33 -0
  37. package/dist_ts_web/ts_web/elements/taskbuffer-dashboard.demo.d.ts +2 -0
  38. package/dist_ts_web/ts_web/elements/taskbuffer-dashboard.demo.js +285 -0
  39. package/dist_ts_web/ts_web/index.d.ts +2 -0
  40. package/dist_ts_web/ts_web/index.js +3 -0
  41. package/dist_ts_web/ts_web/taskbuffer-dashboard.d.ts +24 -0
  42. package/dist_ts_web/ts_web/taskbuffer-dashboard.js +557 -0
  43. package/package.json +5 -4
  44. package/readme.md +412 -938
  45. package/ts/00_commitinfo_data.ts +1 -1
  46. package/ts_web/00_commitinfo_data.ts +1 -1
  47. package/ts_web/elements/taskbuffer-dashboard.demo.ts +311 -0
  48. package/ts_web/index.ts +12 -0
  49. package/ts_web/taskbuffer-dashboard.ts +541 -0
@@ -0,0 +1,541 @@
1
+ import { DeesElement, customElement, html, css, property, state, cssManager } from '@design.estate/dees-element';
2
+ import type { TaskManager, ITaskMetadata, IScheduledTaskInfo } from '../ts/index.js';
3
+
4
+ /**
5
+ * A web component that displays TaskManager tasks with progress visualization
6
+ */
7
+ @customElement('taskbuffer-dashboard')
8
+ export class TaskbufferDashboard extends DeesElement {
9
+ // Properties
10
+ @property({ type: Object })
11
+ public taskManager: TaskManager | null = null;
12
+
13
+ @property({ type: Number })
14
+ public refreshInterval: number = 1000; // milliseconds
15
+
16
+ // Internal state
17
+ @state()
18
+ private tasks: ITaskMetadata[] = [];
19
+
20
+ @state()
21
+ private scheduledTasks: IScheduledTaskInfo[] = [];
22
+
23
+ @state()
24
+ private isRunning: boolean = false;
25
+
26
+ private refreshTimer: any;
27
+
28
+ // Styles
29
+ static styles = [
30
+ cssManager.defaultStyles,
31
+ css`
32
+ :host {
33
+ display: block;
34
+ font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
35
+ }
36
+
37
+ .dashboard-container {
38
+ padding: 24px;
39
+ background: ${cssManager.bdTheme('#ffffff', '#0a0a0a')};
40
+ min-height: 100vh;
41
+ }
42
+
43
+ .dashboard-header {
44
+ margin-bottom: 32px;
45
+ }
46
+
47
+ .dashboard-title {
48
+ font-size: 28px;
49
+ font-weight: 700;
50
+ color: ${cssManager.bdTheme('#09090b', '#fafafa')};
51
+ margin-bottom: 8px;
52
+ display: flex;
53
+ align-items: center;
54
+ gap: 12px;
55
+ }
56
+
57
+ .status-indicator {
58
+ width: 12px;
59
+ height: 12px;
60
+ border-radius: 50%;
61
+ background: ${cssManager.bdTheme('#22c55e', '#22c55e')};
62
+ animation: pulse 2s infinite;
63
+ }
64
+
65
+ .status-indicator.inactive {
66
+ background: ${cssManager.bdTheme('#94a3b8', '#475569')};
67
+ animation: none;
68
+ }
69
+
70
+ @keyframes pulse {
71
+ 0%, 100% {
72
+ opacity: 1;
73
+ }
74
+ 50% {
75
+ opacity: 0.5;
76
+ }
77
+ }
78
+
79
+ .dashboard-subtitle {
80
+ font-size: 14px;
81
+ color: ${cssManager.bdTheme('#71717a', '#a1a1aa')};
82
+ }
83
+
84
+ .tasks-section {
85
+ margin-bottom: 48px;
86
+ }
87
+
88
+ .section-title {
89
+ font-size: 20px;
90
+ font-weight: 600;
91
+ color: ${cssManager.bdTheme('#18181b', '#e4e4e7')};
92
+ margin-bottom: 16px;
93
+ }
94
+
95
+ .tasks-grid {
96
+ display: grid;
97
+ grid-template-columns: repeat(auto-fill, minmax(350px, 1fr));
98
+ gap: 16px;
99
+ }
100
+
101
+ .task-card {
102
+ background: ${cssManager.bdTheme('#f8fafc', '#18181b')};
103
+ border: 1px solid ${cssManager.bdTheme('#e2e8f0', '#27272a')};
104
+ border-radius: 12px;
105
+ padding: 20px;
106
+ transition: all 0.2s ease;
107
+ }
108
+
109
+ .task-card:hover {
110
+ transform: translateY(-2px);
111
+ box-shadow: 0 4px 12px ${cssManager.bdTheme('rgba(0,0,0,0.1)', 'rgba(0,0,0,0.3)')};
112
+ }
113
+
114
+ .task-header {
115
+ display: flex;
116
+ justify-content: space-between;
117
+ align-items: flex-start;
118
+ margin-bottom: 12px;
119
+ }
120
+
121
+ .task-name {
122
+ font-size: 16px;
123
+ font-weight: 600;
124
+ color: ${cssManager.bdTheme('#0f172a', '#f1f5f9')};
125
+ }
126
+
127
+ .task-status {
128
+ display: inline-block;
129
+ padding: 4px 8px;
130
+ border-radius: 6px;
131
+ font-size: 11px;
132
+ font-weight: 600;
133
+ text-transform: uppercase;
134
+ letter-spacing: 0.5px;
135
+ }
136
+
137
+ .task-status.idle {
138
+ background: ${cssManager.bdTheme('#f1f5f9', '#27272a')};
139
+ color: ${cssManager.bdTheme('#64748b', '#94a3b8')};
140
+ }
141
+
142
+ .task-status.running {
143
+ background: ${cssManager.bdTheme('#dbeafe', '#1e3a8a')};
144
+ color: ${cssManager.bdTheme('#1e40af', '#93c5fd')};
145
+ }
146
+
147
+ .task-status.completed {
148
+ background: ${cssManager.bdTheme('#dcfce7', '#14532d')};
149
+ color: ${cssManager.bdTheme('#15803d', '#86efac')};
150
+ }
151
+
152
+ .task-status.failed {
153
+ background: ${cssManager.bdTheme('#fee2e2', '#7f1d1d')};
154
+ color: ${cssManager.bdTheme('#dc2626', '#fca5a5')};
155
+ }
156
+
157
+ .task-info {
158
+ display: flex;
159
+ gap: 16px;
160
+ margin-bottom: 16px;
161
+ font-size: 13px;
162
+ color: ${cssManager.bdTheme('#64748b', '#94a3b8')};
163
+ }
164
+
165
+ .task-info-item {
166
+ display: flex;
167
+ align-items: center;
168
+ gap: 4px;
169
+ }
170
+
171
+ .task-info-icon {
172
+ width: 14px;
173
+ height: 14px;
174
+ opacity: 0.7;
175
+ }
176
+
177
+ .progress-container {
178
+ margin-bottom: 16px;
179
+ }
180
+
181
+ .progress-label {
182
+ display: flex;
183
+ justify-content: space-between;
184
+ margin-bottom: 8px;
185
+ font-size: 13px;
186
+ color: ${cssManager.bdTheme('#475569', '#cbd5e1')};
187
+ }
188
+
189
+ .progress-bar {
190
+ height: 8px;
191
+ background: ${cssManager.bdTheme('#e2e8f0', '#27272a')};
192
+ border-radius: 4px;
193
+ overflow: hidden;
194
+ position: relative;
195
+ }
196
+
197
+ .progress-fill {
198
+ height: 100%;
199
+ background: linear-gradient(90deg, #3b82f6, #6366f1);
200
+ border-radius: 4px;
201
+ transition: width 0.3s ease;
202
+ position: relative;
203
+ overflow: hidden;
204
+ }
205
+
206
+ .progress-fill::after {
207
+ content: '';
208
+ position: absolute;
209
+ top: 0;
210
+ left: 0;
211
+ right: 0;
212
+ bottom: 0;
213
+ background: linear-gradient(
214
+ 90deg,
215
+ transparent,
216
+ rgba(255, 255, 255, 0.3),
217
+ transparent
218
+ );
219
+ animation: shimmer 2s infinite;
220
+ }
221
+
222
+ @keyframes shimmer {
223
+ 0% {
224
+ transform: translateX(-100%);
225
+ }
226
+ 100% {
227
+ transform: translateX(100%);
228
+ }
229
+ }
230
+
231
+ .steps-container {
232
+ border-top: 1px solid ${cssManager.bdTheme('#e2e8f0', '#27272a')};
233
+ padding-top: 12px;
234
+ }
235
+
236
+ .steps-title {
237
+ font-size: 12px;
238
+ font-weight: 600;
239
+ color: ${cssManager.bdTheme('#64748b', '#94a3b8')};
240
+ margin-bottom: 8px;
241
+ text-transform: uppercase;
242
+ letter-spacing: 0.5px;
243
+ }
244
+
245
+ .step-item {
246
+ display: flex;
247
+ align-items: center;
248
+ gap: 8px;
249
+ padding: 6px 0;
250
+ font-size: 13px;
251
+ }
252
+
253
+ .step-indicator {
254
+ width: 20px;
255
+ height: 20px;
256
+ border-radius: 50%;
257
+ display: flex;
258
+ align-items: center;
259
+ justify-content: center;
260
+ font-size: 10px;
261
+ font-weight: 600;
262
+ flex-shrink: 0;
263
+ }
264
+
265
+ .step-indicator.pending {
266
+ background: ${cssManager.bdTheme('#f1f5f9', '#27272a')};
267
+ color: ${cssManager.bdTheme('#94a3b8', '#64748b')};
268
+ border: 2px solid ${cssManager.bdTheme('#cbd5e1', '#3f3f46')};
269
+ }
270
+
271
+ .step-indicator.active {
272
+ background: linear-gradient(135deg, #3b82f6, #6366f1);
273
+ color: white;
274
+ animation: pulse 1.5s infinite;
275
+ }
276
+
277
+ .step-indicator.completed {
278
+ background: ${cssManager.bdTheme('#22c55e', '#22c55e')};
279
+ color: white;
280
+ }
281
+
282
+ .step-details {
283
+ flex: 1;
284
+ }
285
+
286
+ .step-name {
287
+ font-weight: 500;
288
+ color: ${cssManager.bdTheme('#1e293b', '#e2e8f0')};
289
+ }
290
+
291
+ .step-description {
292
+ font-size: 11px;
293
+ color: ${cssManager.bdTheme('#64748b', '#94a3b8')};
294
+ margin-top: 2px;
295
+ }
296
+
297
+ .step-percentage {
298
+ font-size: 11px;
299
+ font-weight: 600;
300
+ color: ${cssManager.bdTheme('#94a3b8', '#64748b')};
301
+ }
302
+
303
+ .empty-state {
304
+ text-align: center;
305
+ padding: 48px;
306
+ color: ${cssManager.bdTheme('#94a3b8', '#64748b')};
307
+ }
308
+
309
+ .empty-state-icon {
310
+ width: 64px;
311
+ height: 64px;
312
+ margin: 0 auto 16px;
313
+ opacity: 0.3;
314
+ }
315
+
316
+ .empty-state-text {
317
+ font-size: 16px;
318
+ margin-bottom: 8px;
319
+ }
320
+
321
+ .empty-state-subtext {
322
+ font-size: 14px;
323
+ color: ${cssManager.bdTheme('#cbd5e1', '#475569')};
324
+ }
325
+
326
+ .scheduled-section {
327
+ margin-top: 32px;
328
+ }
329
+
330
+ .schedule-info {
331
+ display: flex;
332
+ align-items: center;
333
+ gap: 8px;
334
+ margin-top: 8px;
335
+ font-size: 12px;
336
+ color: ${cssManager.bdTheme('#64748b', '#94a3b8')};
337
+ }
338
+
339
+ .schedule-icon {
340
+ width: 14px;
341
+ height: 14px;
342
+ }
343
+ `,
344
+ ];
345
+
346
+ // Lifecycle
347
+ async connectedCallback() {
348
+ await super.connectedCallback();
349
+ this.startRefreshing();
350
+ }
351
+
352
+ async disconnectedCallback() {
353
+ await super.disconnectedCallback();
354
+ this.stopRefreshing();
355
+ }
356
+
357
+ // Methods
358
+ private startRefreshing() {
359
+ if (this.refreshTimer) {
360
+ clearInterval(this.refreshTimer);
361
+ }
362
+
363
+ this.updateData();
364
+ this.refreshTimer = setInterval(() => {
365
+ this.updateData();
366
+ }, this.refreshInterval);
367
+ this.isRunning = true;
368
+ }
369
+
370
+ private stopRefreshing() {
371
+ if (this.refreshTimer) {
372
+ clearInterval(this.refreshTimer);
373
+ this.refreshTimer = null;
374
+ }
375
+ this.isRunning = false;
376
+ }
377
+
378
+ private updateData() {
379
+ if (!this.taskManager) {
380
+ this.tasks = [];
381
+ this.scheduledTasks = [];
382
+ return;
383
+ }
384
+
385
+ this.tasks = this.taskManager.getAllTasksMetadata();
386
+ this.scheduledTasks = this.taskManager.getScheduledTasks();
387
+ }
388
+
389
+ private formatNextRun(date: Date): string {
390
+ const now = new Date();
391
+ const diff = date.getTime() - now.getTime();
392
+
393
+ if (diff < 0) return 'Past due';
394
+ if (diff < 60000) return 'Less than a minute';
395
+ if (diff < 3600000) return `${Math.floor(diff / 60000)} minutes`;
396
+ if (diff < 86400000) return `${Math.floor(diff / 3600000)} hours`;
397
+ return `${Math.floor(diff / 86400000)} days`;
398
+ }
399
+
400
+ private formatDuration(ms?: number): string {
401
+ if (!ms) return '-';
402
+ if (ms < 1000) return `${ms}ms`;
403
+ if (ms < 60000) return `${(ms / 1000).toFixed(1)}s`;
404
+ return `${(ms / 60000).toFixed(1)}m`;
405
+ }
406
+
407
+ // Render
408
+ render() {
409
+ return html`
410
+ <div class="dashboard-container">
411
+ <div class="dashboard-header">
412
+ <div class="dashboard-title">
413
+ <span>TaskBuffer Dashboard</span>
414
+ <span class="status-indicator ${this.isRunning ? '' : 'inactive'}"></span>
415
+ </div>
416
+ <div class="dashboard-subtitle">
417
+ ${this.tasks.length} task${this.tasks.length !== 1 ? 's' : ''} registered
418
+ ${this.scheduledTasks.length > 0 ? ` • ${this.scheduledTasks.length} scheduled` : ''}
419
+ </div>
420
+ </div>
421
+
422
+ <div class="tasks-section">
423
+ <h2 class="section-title">Active Tasks</h2>
424
+
425
+ ${this.tasks.length > 0 ? html`
426
+ <div class="tasks-grid">
427
+ ${this.tasks.map(task => this.renderTaskCard(task))}
428
+ </div>
429
+ ` : html`
430
+ <div class="empty-state">
431
+ <svg class="empty-state-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
432
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
433
+ d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2" />
434
+ </svg>
435
+ <div class="empty-state-text">No tasks registered</div>
436
+ <div class="empty-state-subtext">Tasks will appear here when added to the TaskManager</div>
437
+ </div>
438
+ `}
439
+ </div>
440
+
441
+ ${this.scheduledTasks.length > 0 ? html`
442
+ <div class="scheduled-section">
443
+ <h2 class="section-title">Scheduled Tasks</h2>
444
+ <div class="tasks-grid">
445
+ ${this.scheduledTasks.map(task => this.renderScheduledTaskCard(task))}
446
+ </div>
447
+ </div>
448
+ ` : ''}
449
+ </div>
450
+ `;
451
+ }
452
+
453
+ private renderTaskCard(task: ITaskMetadata) {
454
+ return html`
455
+ <div class="task-card">
456
+ <div class="task-header">
457
+ <div class="task-name">${task.name || 'Unnamed Task'}</div>
458
+ <div class="task-status ${task.status}">${task.status}</div>
459
+ </div>
460
+
461
+ <div class="task-info">
462
+ <div class="task-info-item">
463
+ <svg class="task-info-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
464
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
465
+ d="M13 10V3L4 14h7v7l9-11h-7z" />
466
+ </svg>
467
+ <span>Run ${task.runCount || 0} times</span>
468
+ </div>
469
+ ${task.buffered ? html`
470
+ <div class="task-info-item">
471
+ <svg class="task-info-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
472
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
473
+ d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10" />
474
+ </svg>
475
+ <span>Buffer: ${task.bufferMax || 0}</span>
476
+ </div>
477
+ ` : ''}
478
+ </div>
479
+
480
+ ${task.steps && task.steps.length > 0 ? html`
481
+ <div class="progress-container">
482
+ <div class="progress-label">
483
+ <span>Progress</span>
484
+ <span>${task.currentProgress || 0}%</span>
485
+ </div>
486
+ <div class="progress-bar">
487
+ <div class="progress-fill" style="width: ${task.currentProgress || 0}%"></div>
488
+ </div>
489
+ </div>
490
+
491
+ <div class="steps-container">
492
+ <div class="steps-title">Steps</div>
493
+ ${task.steps.map(step => html`
494
+ <div class="step-item">
495
+ <div class="step-indicator ${step.status}">
496
+ ${step.status === 'completed' ? '✓' :
497
+ step.status === 'active' ? '•' :
498
+ ''}
499
+ </div>
500
+ <div class="step-details">
501
+ <div class="step-name">${step.name}</div>
502
+ ${step.description ? html`
503
+ <div class="step-description">${step.description}</div>
504
+ ` : ''}
505
+ </div>
506
+ <div class="step-percentage">${step.percentage}%</div>
507
+ </div>
508
+ `)}
509
+ </div>
510
+ ` : ''}
511
+ </div>
512
+ `;
513
+ }
514
+
515
+ private renderScheduledTaskCard(task: IScheduledTaskInfo) {
516
+ return html`
517
+ <div class="task-card">
518
+ <div class="task-header">
519
+ <div class="task-name">${task.name}</div>
520
+ <div class="task-status idle">scheduled</div>
521
+ </div>
522
+
523
+ <div class="schedule-info">
524
+ <svg class="schedule-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
525
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
526
+ d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" />
527
+ </svg>
528
+ <span>Next run: ${this.formatNextRun(task.nextRun)}</span>
529
+ </div>
530
+
531
+ <div class="schedule-info">
532
+ <svg class="schedule-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
533
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
534
+ d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" />
535
+ </svg>
536
+ <span>Schedule: ${task.schedule}</span>
537
+ </div>
538
+ </div>
539
+ `;
540
+ }
541
+ }