hyper-scheduler 1.0.0 → 1.0.1

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 (80) hide show
  1. package/dist/devtools-4xVHTaUz.js +2161 -0
  2. package/dist/devtools-DdQ1I25H.cjs +1 -0
  3. package/dist/index.cjs +1 -0
  4. package/dist/index.js +995 -0
  5. package/dist/index.umd.cjs +1 -0
  6. package/package.json +7 -1
  7. package/.editorconfig +0 -21
  8. package/.eslintrc.cjs +0 -26
  9. package/GEMINI.md +0 -1
  10. package/docs/.vitepress/config.ts +0 -52
  11. package/docs/README.md +0 -120
  12. package/docs/api/devtools.md +0 -232
  13. package/docs/api/index.md +0 -178
  14. package/docs/api/scheduler.md +0 -322
  15. package/docs/api/task.md +0 -439
  16. package/docs/api/types.md +0 -365
  17. package/docs/examples/index.md +0 -295
  18. package/docs/guide/best-practices.md +0 -436
  19. package/docs/guide/core-concepts.md +0 -363
  20. package/docs/guide/getting-started.md +0 -138
  21. package/docs/index.md +0 -33
  22. package/docs/public/logo.svg +0 -54
  23. package/examples/browser/index.html +0 -354
  24. package/examples/node/simple.js +0 -36
  25. package/examples/react-demo/index.html +0 -12
  26. package/examples/react-demo/package.json +0 -23
  27. package/examples/react-demo/src/App.css +0 -212
  28. package/examples/react-demo/src/App.jsx +0 -160
  29. package/examples/react-demo/src/main.jsx +0 -9
  30. package/examples/react-demo/vite.config.ts +0 -12
  31. package/examples/react-demo/yarn.lock +0 -752
  32. package/examples/vue-demo/index.html +0 -12
  33. package/examples/vue-demo/package.json +0 -21
  34. package/examples/vue-demo/src/App.vue +0 -373
  35. package/examples/vue-demo/src/main.ts +0 -4
  36. package/examples/vue-demo/vite.config.ts +0 -13
  37. package/examples/vue-demo/yarn.lock +0 -375
  38. package/src/constants.ts +0 -18
  39. package/src/core/retry-strategy.ts +0 -28
  40. package/src/core/scheduler.ts +0 -601
  41. package/src/core/task-registry.ts +0 -58
  42. package/src/index.ts +0 -74
  43. package/src/platform/browser/browser-timer.ts +0 -66
  44. package/src/platform/browser/main-thread-timer.ts +0 -16
  45. package/src/platform/browser/worker.ts +0 -31
  46. package/src/platform/node/debug-cli.ts +0 -19
  47. package/src/platform/node/node-timer.ts +0 -15
  48. package/src/platform/timer-strategy.ts +0 -19
  49. package/src/plugins/dev-tools.ts +0 -101
  50. package/src/types.ts +0 -115
  51. package/src/ui/components/devtools.ts +0 -525
  52. package/src/ui/components/floating-trigger.ts +0 -102
  53. package/src/ui/components/icons.ts +0 -16
  54. package/src/ui/components/resizer.ts +0 -129
  55. package/src/ui/components/task-detail.ts +0 -228
  56. package/src/ui/components/task-header.ts +0 -319
  57. package/src/ui/components/task-list.ts +0 -416
  58. package/src/ui/components/timeline.ts +0 -364
  59. package/src/ui/debug-panel.ts +0 -56
  60. package/src/ui/i18n/en.ts +0 -76
  61. package/src/ui/i18n/index.ts +0 -42
  62. package/src/ui/i18n/zh.ts +0 -76
  63. package/src/ui/store/dev-tools-store.ts +0 -191
  64. package/src/ui/styles/theme.css.ts +0 -56
  65. package/src/ui/styles.ts +0 -43
  66. package/src/utils/cron-lite.ts +0 -221
  67. package/src/utils/cron.ts +0 -20
  68. package/src/utils/id.ts +0 -10
  69. package/src/utils/schedule.ts +0 -93
  70. package/src/vite-env.d.ts +0 -1
  71. package/stats.html +0 -4949
  72. package/tests/integration/Debug.test.ts +0 -58
  73. package/tests/unit/Plugin.test.ts +0 -16
  74. package/tests/unit/RetryStrategy.test.ts +0 -21
  75. package/tests/unit/Scheduler.test.ts +0 -38
  76. package/tests/unit/schedule.test.ts +0 -70
  77. package/tests/unit/ui/DevToolsStore.test.ts +0 -67
  78. package/tsconfig.json +0 -28
  79. package/vite.config.ts +0 -51
  80. package/vitest.config.ts +0 -24
@@ -1,416 +0,0 @@
1
- import { themeStyles } from '../styles/theme.css';
2
- import { TaskSnapshot } from '../../types';
3
- import { TaskStatus } from '../../constants';
4
- import { ICONS } from './icons';
5
- import { t } from '../i18n';
6
-
7
- export class TaskList extends HTMLElement {
8
- private _shadow: ShadowRoot;
9
- private _tasks: TaskSnapshot[] = [];
10
- private _lastExecutionTimes: Map<string, number> = new Map();
11
-
12
- constructor() {
13
- super();
14
- this._shadow = this.attachShadow({ mode: 'open' });
15
- }
16
-
17
- connectedCallback() {
18
- this.render();
19
- }
20
-
21
- set tasks(map: Map<string, TaskSnapshot>) {
22
- const newTasks = Array.from(map.values());
23
-
24
- // Track execution count changes to show flash animation
25
- newTasks.forEach(task => {
26
- const oldTask = this._tasks.find(t => t.id === task.id);
27
- if (oldTask && task.executionCount > oldTask.executionCount) {
28
- this._lastExecutionTimes.set(task.id, Date.now());
29
- }
30
- });
31
-
32
- this._tasks = newTasks;
33
- this.renderRows();
34
- }
35
-
36
- filter(text: string, map: Map<string, TaskSnapshot>) {
37
- const all = Array.from(map.values());
38
- if (!text) {
39
- this._tasks = all;
40
- } else {
41
- const lower = text.toLowerCase();
42
- this._tasks = all.filter(t =>
43
- t.id.toLowerCase().includes(lower) ||
44
- t.tags.some(tag => tag.toLowerCase().includes(lower))
45
- );
46
- }
47
- this.renderRows();
48
- }
49
-
50
- // Method to update table headers when language changes
51
- updateHeaders() {
52
- const thead = this._shadow.querySelector('thead');
53
- if (thead) {
54
- thead.innerHTML = `
55
- <tr>
56
- <th style="width:40px">#</th>
57
- <th style="min-width:150px">${t('list.idTags')}</th>
58
- <th style="width:150px">${t('list.status')}</th>
59
- <th style="width:70px">${t('list.driver')}</th>
60
- <th style="width:100px">${t('list.schedule')}</th>
61
- <th style="width:60px">${t('list.count')}</th>
62
- <th style="width:100px">${t('list.lastRun')}</th>
63
- <th style="width:100px">${t('list.actions')}</th>
64
- </tr>
65
- `;
66
- }
67
-
68
- // Update tip
69
- const tip = this._shadow.querySelector('.tip');
70
- if (tip) {
71
- tip.textContent = t('list.tip');
72
- }
73
-
74
- // Re-render rows to update status text
75
- this.renderRows();
76
- }
77
-
78
- private getStatusIcon(status: string, taskId: string) {
79
- const lastExec = this._lastExecutionTimes.get(taskId);
80
- const isRecentlyExecuted = lastExec && (Date.now() - lastExec < 1000);
81
-
82
- // idle = 正在调度中(等待下次执行)
83
- // running = 正在执行 handler
84
- // stopped = 已停止,不会再调度(需要手动启动)
85
- // error = 执行出错
86
- switch (status) {
87
- case TaskStatus.RUNNING:
88
- return `<span style="color:var(--hs-primary)">🔵</span> <strong>${t('status.running')}</strong>`;
89
- case TaskStatus.STOPPED:
90
- return `<span style="color:var(--hs-text-secondary)">⚪</span> ${t('status.stopped')}`;
91
- case TaskStatus.IDLE:
92
- if (isRecentlyExecuted) {
93
- return `<span class="status-flash" style="color:var(--hs-success)">🟢</span> ${t('status.idle')}`;
94
- }
95
- return `<span style="color:var(--hs-success)">🟢</span> ${t('status.idle')}`;
96
- case TaskStatus.ERROR:
97
- return `<span style="color:var(--hs-warning)">🟠</span> ${t('status.error')}`;
98
- default:
99
- return status;
100
- }
101
- }
102
-
103
- private formatSchedule(schedule: string | number): string {
104
- if (typeof schedule === 'number') {
105
- return `${schedule}ms`;
106
- }
107
- if (schedule && (schedule.includes('*') || schedule.includes(' '))) {
108
- return schedule.length > 15 ? schedule.substring(0, 12) + '...' : schedule;
109
- }
110
- return schedule || '-';
111
- }
112
-
113
- private formatTime(timestamp: number | null): string {
114
- if (!timestamp) return '-';
115
- const date = new Date(timestamp);
116
- return date.toLocaleTimeString('en-US', {
117
- hour12: false,
118
- hour: '2-digit',
119
- minute: '2-digit',
120
- second: '2-digit'
121
- }) + '.' + date.getMilliseconds().toString().padStart(3, '0');
122
- }
123
-
124
- private getDriverBadge(driver: 'worker' | 'main' | undefined): string {
125
- const d = driver || 'worker';
126
- if (d === 'worker') {
127
- return `<span class="driver-badge worker" title="${t('list.driverWorker')}">W</span>`;
128
- }
129
- return `<span class="driver-badge main" title="${t('list.driverMain')}">M</span>`;
130
- }
131
-
132
- private renderRows() {
133
- const tbody = this._shadow.querySelector('tbody');
134
- if (!tbody) return;
135
-
136
- // Simple full re-render for MVP
137
- tbody.innerHTML = this._tasks.map((task, index) => {
138
- const lastExec = this._lastExecutionTimes.get(task.id);
139
- const isRecentlyExecuted = lastExec && (Date.now() - lastExec < 1000);
140
- const rowClass = isRecentlyExecuted ? 'recently-executed' : '';
141
-
142
- return `
143
- <tr data-id="${task.id}" class="${rowClass}">
144
- <td class="col-num">${index + 1}</td>
145
- <td class="col-id">
146
- <div class="task-id">${task.id}</div>
147
- <div class="tags">
148
- ${task.tags && task.tags.length > 0
149
- ? task.tags.map(t => `<span class="tag">${t}</span>`).join('')
150
- : `<span class="no-tags">${t('list.noTags')}</span>`}
151
- </div>
152
- </td>
153
- <td>${this.getStatusIcon(task.status, task.id)}</td>
154
- <td>${this.getDriverBadge((task as any).driver)}</td>
155
- <td>${this.formatSchedule(task.schedule)}</td>
156
- <td>${task.executionCount || 0}</td>
157
- <td>${this.formatTime(task.lastRun)}</td>
158
- <td class="col-actions">
159
- <div class="action-group">
160
- <button class="btn-icon" data-action="trigger" title="${t('actions.trigger')}" ${task.status === TaskStatus.RUNNING ? 'disabled' : ''}>${ICONS.trigger}</button>
161
- ${task.status === TaskStatus.STOPPED || task.status === TaskStatus.ERROR
162
- ? `<button class="btn-icon success" data-action="start" title="${t('actions.start')}">${ICONS.resume}</button>`
163
- : `<button class="btn-icon warning" data-action="stop" title="${t('actions.stop')}" ${task.status === TaskStatus.RUNNING ? 'disabled' : ''}>${ICONS.pause}</button>`
164
- }
165
- <button class="btn-icon danger" data-action="remove" title="${t('actions.remove')}">${ICONS.remove}</button>
166
- </div>
167
- </td>
168
- </tr>
169
- `;
170
- }).join('');
171
- }
172
-
173
- render() {
174
- this._shadow.innerHTML = `
175
- <style>
176
- ${themeStyles}
177
- :host {
178
- display: flex;
179
- flex-direction: column;
180
- height: 100%;
181
- background: var(--hs-bg);
182
- }
183
- .table-container {
184
- flex: 1;
185
- min-height: 0;
186
- overflow-y: auto;
187
- overflow-x: auto;
188
- position: relative;
189
- }
190
- table {
191
- width: 100%;
192
- border-collapse: collapse;
193
- font-size: var(--hs-font-size);
194
- color: var(--hs-text);
195
- }
196
- thead {
197
- position: sticky;
198
- top: 0;
199
- z-index: 2;
200
- background: var(--hs-bg);
201
- }
202
- th {
203
- text-align: left;
204
- padding: 8px 12px;
205
- border-bottom: 2px solid var(--hs-border);
206
- color: var(--hs-text-secondary);
207
- font-weight: 600;
208
- background: var(--hs-bg);
209
- font-size: 11px;
210
- text-transform: uppercase;
211
- white-space: nowrap;
212
- }
213
- th:last-child {
214
- position: sticky;
215
- right: 0;
216
- background: var(--hs-bg);
217
- box-shadow: -2px 0 4px rgba(0,0,0,0.1);
218
- }
219
- td {
220
- padding: 8px 12px;
221
- border-bottom: 1px solid var(--hs-border);
222
- vertical-align: middle;
223
- }
224
- tr:hover {
225
- background: var(--hs-bg-secondary);
226
- cursor: pointer;
227
- }
228
- tr.recently-executed {
229
- animation: flash-row 1s ease-out;
230
- }
231
- @keyframes flash-row {
232
- 0% { background: rgba(34, 197, 94, 0.2); }
233
- 100% { background: transparent; }
234
- }
235
- .status-flash {
236
- animation: flash-icon 1s ease-out;
237
- }
238
- @keyframes flash-icon {
239
- 0%, 50% { opacity: 1; }
240
- 25%, 75% { opacity: 0.3; }
241
- }
242
- .tip {
243
- padding: 12px;
244
- text-align: center;
245
- font-size: 11px;
246
- color: var(--hs-text-secondary);
247
- border-top: 1px solid var(--hs-border);
248
- background: var(--hs-bg);
249
- }
250
- .task-id {
251
- font-weight: 600;
252
- }
253
- .tags {
254
- display: flex;
255
- gap: 4px;
256
- margin-top: 4px;
257
- }
258
- .tag {
259
- background: var(--hs-bg-secondary);
260
- border: 1px solid var(--hs-border);
261
- border-radius: 10px;
262
- padding: 2px 8px;
263
- font-size: 10px;
264
- color: var(--hs-text-secondary);
265
- }
266
- .no-tags {
267
- font-size: 10px;
268
- color: var(--hs-text-secondary);
269
- font-style: italic;
270
- }
271
- .driver-badge {
272
- display: inline-flex;
273
- align-items: center;
274
- justify-content: center;
275
- width: 22px;
276
- height: 22px;
277
- border-radius: 4px;
278
- font-size: 11px;
279
- font-weight: 600;
280
- font-family: monospace;
281
- }
282
- .driver-badge.worker {
283
- background: rgba(34, 197, 94, 0.15);
284
- color: var(--hs-success);
285
- border: 1px solid var(--hs-success);
286
- }
287
- .driver-badge.main {
288
- background: rgba(245, 158, 11, 0.15);
289
- color: var(--hs-warning);
290
- border: 1px solid var(--hs-warning);
291
- }
292
- .col-num {
293
- width: 40px;
294
- color: var(--hs-text-secondary);
295
- font-size: 11px;
296
- }
297
- .col-actions {
298
- width: 100px;
299
- position: sticky;
300
- right: 0;
301
- background: var(--hs-bg);
302
- box-shadow: -2px 0 4px rgba(0,0,0,0.1);
303
- }
304
- .action-group {
305
- display: flex;
306
- gap: 4px;
307
- }
308
- .btn-icon {
309
- background: transparent;
310
- border: 1px solid var(--hs-border);
311
- color: var(--hs-text-secondary);
312
- border-radius: 4px;
313
- width: 24px;
314
- height: 24px;
315
- display: flex;
316
- align-items: center;
317
- justify-content: center;
318
- cursor: pointer;
319
- padding: 0;
320
- flex-shrink: 0;
321
- }
322
- .btn-icon svg {
323
- width: 14px;
324
- height: 14px;
325
- display: block;
326
- }
327
- .btn-icon:hover {
328
- background: var(--hs-primary);
329
- color: white;
330
- border-color: var(--hs-primary);
331
- }
332
- .btn-trigger:hover {
333
- background: var(--hs-success);
334
- border-color: var(--hs-success);
335
- }
336
- .btn-pause:hover {
337
- background: var(--hs-warning);
338
- border-color: var(--hs-warning);
339
- }
340
- .btn-resume:hover {
341
- background: var(--hs-success);
342
- border-color: var(--hs-success);
343
- }
344
- .btn-remove:hover {
345
- color: white;
346
- background: var(--hs-danger);
347
- border-color: var(--hs-danger);
348
- }
349
- .btn-icon:disabled {
350
- opacity: 0.4;
351
- cursor: not-allowed;
352
- }
353
- .btn-icon:disabled:hover {
354
- background: transparent;
355
- color: var(--hs-text-secondary);
356
- border-color: var(--hs-border);
357
- }
358
- </style>
359
- <div class="table-container">
360
- <table>
361
- <thead>
362
- <tr>
363
- <th style="width:40px">#</th>
364
- <th style="min-width:150px">${t('list.idTags')}</th>
365
- <th style="width:150px">${t('list.status')}</th>
366
- <th style="width:70px">${t('list.driver')}</th>
367
- <th style="width:100px">${t('list.schedule')}</th>
368
- <th style="width:60px">${t('list.count')}</th>
369
- <th style="width:100px">${t('list.lastRun')}</th>
370
- <th style="width:100px">${t('list.actions')}</th>
371
- </tr>
372
- </thead>
373
- <tbody>
374
- <!-- Rows -->
375
- </tbody>
376
- </table>
377
- </div>
378
- <div class="tip">
379
- ${t('list.tip')}
380
- </div>
381
- `;
382
-
383
- this._shadow.addEventListener('click', (e) => {
384
- const target = e.target as HTMLElement;
385
- const btn = target.closest('button');
386
-
387
- // Handle Action Buttons
388
- if (btn) {
389
- const action = btn.dataset.action;
390
- const tr = btn.closest('tr');
391
- if (!action || !tr) return;
392
- const id = (tr as HTMLElement).dataset.id;
393
-
394
- this.dispatchEvent(new CustomEvent('task-action', {
395
- detail: { action, id },
396
- bubbles: true,
397
- composed: true
398
- }));
399
- return;
400
- }
401
-
402
- // Handle Row Click (Selection)
403
- const tr = target.closest('tr');
404
- if (tr && !target.closest('.col-actions')) {
405
- const id = (tr as HTMLElement).dataset.id;
406
- this.dispatchEvent(new CustomEvent('task-select', {
407
- detail: id,
408
- bubbles: true,
409
- composed: true
410
- }));
411
- }
412
- });
413
- }
414
- }
415
-
416
- customElements.define('hs-task-list', TaskList);