hyper-scheduler 1.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.
Files changed (76) hide show
  1. package/.editorconfig +21 -0
  2. package/.eslintrc.cjs +26 -0
  3. package/GEMINI.md +1 -0
  4. package/README.md +38 -0
  5. package/docs/.vitepress/config.ts +52 -0
  6. package/docs/README.md +120 -0
  7. package/docs/api/devtools.md +232 -0
  8. package/docs/api/index.md +178 -0
  9. package/docs/api/scheduler.md +322 -0
  10. package/docs/api/task.md +439 -0
  11. package/docs/api/types.md +365 -0
  12. package/docs/examples/index.md +295 -0
  13. package/docs/guide/best-practices.md +436 -0
  14. package/docs/guide/core-concepts.md +363 -0
  15. package/docs/guide/getting-started.md +138 -0
  16. package/docs/index.md +33 -0
  17. package/docs/public/logo.svg +54 -0
  18. package/examples/browser/index.html +354 -0
  19. package/examples/node/simple.js +36 -0
  20. package/examples/react-demo/index.html +12 -0
  21. package/examples/react-demo/package.json +23 -0
  22. package/examples/react-demo/src/App.css +212 -0
  23. package/examples/react-demo/src/App.jsx +160 -0
  24. package/examples/react-demo/src/main.jsx +9 -0
  25. package/examples/react-demo/vite.config.ts +12 -0
  26. package/examples/react-demo/yarn.lock +752 -0
  27. package/examples/vue-demo/index.html +12 -0
  28. package/examples/vue-demo/package.json +21 -0
  29. package/examples/vue-demo/src/App.vue +373 -0
  30. package/examples/vue-demo/src/main.ts +4 -0
  31. package/examples/vue-demo/vite.config.ts +13 -0
  32. package/examples/vue-demo/yarn.lock +375 -0
  33. package/package.json +51 -0
  34. package/src/constants.ts +18 -0
  35. package/src/core/retry-strategy.ts +28 -0
  36. package/src/core/scheduler.ts +601 -0
  37. package/src/core/task-registry.ts +58 -0
  38. package/src/index.ts +74 -0
  39. package/src/platform/browser/browser-timer.ts +66 -0
  40. package/src/platform/browser/main-thread-timer.ts +16 -0
  41. package/src/platform/browser/worker.ts +31 -0
  42. package/src/platform/node/debug-cli.ts +19 -0
  43. package/src/platform/node/node-timer.ts +15 -0
  44. package/src/platform/timer-strategy.ts +19 -0
  45. package/src/plugins/dev-tools.ts +101 -0
  46. package/src/types.ts +115 -0
  47. package/src/ui/components/devtools.ts +525 -0
  48. package/src/ui/components/floating-trigger.ts +102 -0
  49. package/src/ui/components/icons.ts +16 -0
  50. package/src/ui/components/resizer.ts +129 -0
  51. package/src/ui/components/task-detail.ts +228 -0
  52. package/src/ui/components/task-header.ts +319 -0
  53. package/src/ui/components/task-list.ts +416 -0
  54. package/src/ui/components/timeline.ts +364 -0
  55. package/src/ui/debug-panel.ts +56 -0
  56. package/src/ui/i18n/en.ts +76 -0
  57. package/src/ui/i18n/index.ts +42 -0
  58. package/src/ui/i18n/zh.ts +76 -0
  59. package/src/ui/store/dev-tools-store.ts +191 -0
  60. package/src/ui/styles/theme.css.ts +56 -0
  61. package/src/ui/styles.ts +43 -0
  62. package/src/utils/cron-lite.ts +221 -0
  63. package/src/utils/cron.ts +20 -0
  64. package/src/utils/id.ts +10 -0
  65. package/src/utils/schedule.ts +93 -0
  66. package/src/vite-env.d.ts +1 -0
  67. package/stats.html +4949 -0
  68. package/tests/integration/Debug.test.ts +58 -0
  69. package/tests/unit/Plugin.test.ts +16 -0
  70. package/tests/unit/RetryStrategy.test.ts +21 -0
  71. package/tests/unit/Scheduler.test.ts +38 -0
  72. package/tests/unit/schedule.test.ts +70 -0
  73. package/tests/unit/ui/DevToolsStore.test.ts +67 -0
  74. package/tsconfig.json +28 -0
  75. package/vite.config.ts +51 -0
  76. package/vitest.config.ts +24 -0
@@ -0,0 +1,12 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>Hyper Scheduler Vue Demo</title>
7
+ </head>
8
+ <body>
9
+ <div id="app"></div>
10
+ <script type="module" src="/src/main.ts"></script>
11
+ </body>
12
+ </html>
@@ -0,0 +1,21 @@
1
+ {
2
+ "name": "vue-demo",
3
+ "private": true,
4
+ "version": "0.0.0",
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "vite",
8
+ "build": "vue-tsc && vite build",
9
+ "preview": "vite preview"
10
+ },
11
+ "dependencies": {
12
+ "vue": "^3.4.0",
13
+ "hyper-scheduler": "file:../../"
14
+ },
15
+ "devDependencies": {
16
+ "@vitejs/plugin-vue": "^5.0.0",
17
+ "typescript": "^5.2.0",
18
+ "vite": "^5.0.0",
19
+ "vue-tsc": "^2.0.0"
20
+ }
21
+ }
@@ -0,0 +1,373 @@
1
+ <script setup lang="ts">
2
+ import { ref, onMounted, onUnmounted, watch, nextTick } from 'vue'
3
+ import { Scheduler, DevTools } from 'hyper-scheduler'
4
+
5
+ interface LogItem {
6
+ time: string
7
+ msg: string
8
+ type: string
9
+ }
10
+
11
+ const logs = ref<LogItem[]>([])
12
+ const scheduler = ref<Scheduler | null>(null)
13
+ const isRunning = ref(false)
14
+ const logBoxRef = ref<HTMLElement | null>(null)
15
+
16
+ const addLog = (msg: string, type: string = 'info') => {
17
+ const time = new Date().toLocaleTimeString('zh-CN', {
18
+ hour12: false,
19
+ hour: '2-digit',
20
+ minute: '2-digit',
21
+ second: '2-digit'
22
+ })
23
+
24
+ logs.value.push({ time, msg, type })
25
+ if (logs.value.length > 50) logs.value.shift()
26
+ }
27
+
28
+ // 自动滚动
29
+ watch(() => logs.value.length, () => {
30
+ nextTick(() => {
31
+ if (logBoxRef.value) {
32
+ const el = logBoxRef.value
33
+ // 简单的自动滚动逻辑:如果接近底部,则自动滚动
34
+ const isNearBottom = el.scrollHeight - el.scrollTop - el.clientHeight < 100
35
+ if (isNearBottom || logs.value.length < 5) {
36
+ el.scrollTop = el.scrollHeight
37
+ }
38
+ }
39
+ })
40
+ })
41
+
42
+ onMounted(() => {
43
+ // 创建调度器 - DevTools 仅在开发环境加载
44
+ scheduler.value = new Scheduler({
45
+ debug: true,
46
+ plugins: [new DevTools({
47
+ theme: 'auto',
48
+ language: 'zh',
49
+ dockPosition: 'bottom',
50
+ defaultZoom: 2,
51
+ trigger: {
52
+ position: 'bottom-left',
53
+ backgroundColor: '#10b981',
54
+ textColor: '#ffffff'
55
+ }
56
+ })]
57
+ })
58
+
59
+ // 主线程心跳 (明确指定 driver: 'main')
60
+ scheduler.value.createTask({
61
+ id: 'main-heartbeat',
62
+ schedule: '3s',
63
+ options: { driver: 'main' },
64
+ handler: () => addLog('❤️ [Main] 主线程心跳检测正常', 'error')
65
+ })
66
+
67
+ // Worker 线程心跳 (默认即为 Worker 驱动)
68
+ scheduler.value.createTask({
69
+ id: 'worker-heartbeat',
70
+ schedule: '5s',
71
+ handler: () => addLog('💙 [Worker] 后台线程任务执行中', 'info')
72
+ })
73
+
74
+ addLog('✨ 系统就绪,等待启动指令...', 'info')
75
+ })
76
+
77
+ onUnmounted(() => {
78
+ if (scheduler.value) {
79
+ scheduler.value.stop()
80
+ }
81
+ })
82
+
83
+ const handleToggle = () => {
84
+ if (!scheduler.value) return
85
+
86
+ if (isRunning.value) {
87
+ scheduler.value.stop()
88
+ addLog('⏹️ 调度器系统已停止', 'info')
89
+ } else {
90
+ scheduler.value.start()
91
+ addLog('🚀 调度器系统已启动', 'success')
92
+ }
93
+ isRunning.value = !isRunning.value
94
+ }
95
+ </script>
96
+
97
+ <template>
98
+ <div class="dashboard">
99
+ <!-- 左侧控制区 -->
100
+ <div class="control-panel">
101
+ <div>
102
+ <div class="header">
103
+ <h1>Hyper Scheduler</h1>
104
+ <p>双线程任务调度演示 (Vue)</p>
105
+ </div>
106
+
107
+ <div class="task-status">
108
+ <div class="status-item">
109
+ <span class="status-dot dot-main"></span>
110
+ <div>
111
+ <strong>主线程任务</strong>
112
+ <div class="status-desc">每 3 秒 (driver: 'main')</div>
113
+ </div>
114
+ </div>
115
+ <div class="status-item">
116
+ <span class="status-dot dot-worker"></span>
117
+ <div>
118
+ <strong>Worker 任务</strong>
119
+ <div class="status-desc">每 5 秒 (driver: 'worker')</div>
120
+ </div>
121
+ </div>
122
+ </div>
123
+ </div>
124
+
125
+ <div>
126
+ <div class="actions">
127
+ <button
128
+ :class="isRunning ? 'btn-stop' : 'btn-start'"
129
+ @click="handleToggle"
130
+ >
131
+ <span style="margin-right: 8px">{{ isRunning ? '⏹' : '▶' }}</span>
132
+ {{ isRunning ? '停止调度器' : '启动调度器' }}
133
+ </button>
134
+ </div>
135
+ <div class="info-tip">
136
+ 💡 点击右下角悬浮球打开调试面板
137
+ </div>
138
+ </div>
139
+ </div>
140
+
141
+ <!-- 右侧日志区 -->
142
+ <div class="log-panel">
143
+ <div class="log-header">
144
+ <h2>
145
+ <span>📋</span> 执行日志
146
+ </h2>
147
+ <div style="font-size: 12px; color: #64748b">实时监控中...</div>
148
+ </div>
149
+ <div class="log-box" ref="logBoxRef">
150
+ <div v-for="(log, index) in logs" :key="index" :class="`log-item log-type-${log.type}`">
151
+ <span class="log-time">{{ log.time }}</span>
152
+ <span class="log-content">{{ log.msg }}</span>
153
+ </div>
154
+ </div>
155
+ </div>
156
+ </div>
157
+ </template>
158
+
159
+ <style>
160
+ :root {
161
+ --bg-color: #f8fafc;
162
+ --card-bg: #ffffff;
163
+ --text-primary: #1e293b;
164
+ --text-secondary: #64748b;
165
+ --accent-color: #3b82f6;
166
+ --accent-hover: #2563eb;
167
+ --danger-color: #ef4444;
168
+ --danger-hover: #dc2626;
169
+ --success-color: #10b981;
170
+ --border-radius: 12px;
171
+ --shadow-md: 0 4px 6px -1px rgba(0,0,0,0.1), 0 2px 4px -1px rgba(0,0,0,0.06);
172
+ --font-mono: 'SFMono-Regular', Consolas, 'Lxgw WenKai', 'Liberation Mono', Menlo, monospace;
173
+ }
174
+
175
+ * {
176
+ margin: 0;
177
+ padding: 0;
178
+ box-sizing: border-box;
179
+ }
180
+
181
+ body {
182
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
183
+ background: var(--bg-color);
184
+ color: var(--text-primary);
185
+ min-height: 100vh;
186
+ display: flex;
187
+ align-items: center;
188
+ justify-content: center;
189
+ padding: 20px;
190
+ }
191
+ </style>
192
+
193
+ <style scoped>
194
+ .dashboard {
195
+ display: grid;
196
+ grid-template-columns: 300px 1fr;
197
+ gap: 24px;
198
+ width: 1000px;
199
+ height: 600px;
200
+ background: var(--bg-color);
201
+ }
202
+
203
+ /* 左侧控制面板 */
204
+ .control-panel {
205
+ background: var(--card-bg);
206
+ border-radius: var(--border-radius);
207
+ padding: 24px;
208
+ box-shadow: var(--shadow-md);
209
+ display: flex;
210
+ flex-direction: column;
211
+ justify-content: space-between;
212
+ }
213
+
214
+ .header h1 {
215
+ font-size: 24px;
216
+ font-weight: 700;
217
+ margin-bottom: 8px;
218
+ background: linear-gradient(135deg, #3b82f6 0%, #8b5cf6 100%);
219
+ -webkit-background-clip: text;
220
+ -webkit-text-fill-color: transparent;
221
+ }
222
+
223
+ .header p {
224
+ color: var(--text-secondary);
225
+ font-size: 14px;
226
+ margin-bottom: 24px;
227
+ }
228
+
229
+ .task-status {
230
+ margin-bottom: auto;
231
+ }
232
+
233
+ .status-item {
234
+ display: flex;
235
+ align-items: center;
236
+ padding: 12px;
237
+ background: #f1f5f9;
238
+ border-radius: 8px;
239
+ margin-bottom: 12px;
240
+ font-size: 14px;
241
+ }
242
+
243
+ .status-dot {
244
+ width: 8px;
245
+ height: 8px;
246
+ border-radius: 50%;
247
+ margin-right: 10px;
248
+ }
249
+
250
+ .status-desc {
251
+ font-size: 12px;
252
+ color: #64748b;
253
+ margin-top: 2px;
254
+ }
255
+
256
+ .dot-main { background: #ef4444; box-shadow: 0 0 8px rgba(239, 68, 68, 0.4); }
257
+ .dot-worker { background: #3b82f6; box-shadow: 0 0 8px rgba(59, 130, 246, 0.4); }
258
+
259
+ .actions {
260
+ display: flex;
261
+ flex-direction: column;
262
+ gap: 12px;
263
+ }
264
+
265
+ button {
266
+ display: flex;
267
+ align-items: center;
268
+ justify-content: center;
269
+ width: 100%;
270
+ padding: 12px;
271
+ border: none;
272
+ border-radius: 8px;
273
+ font-size: 14px;
274
+ font-weight: 600;
275
+ cursor: pointer;
276
+ transition: all 0.2s ease;
277
+ }
278
+
279
+ .btn-start {
280
+ background: var(--accent-color);
281
+ color: white;
282
+ }
283
+ .btn-start:hover { background: var(--accent-hover); transform: translateY(-1px); }
284
+ .btn-start:active { transform: translateY(0); }
285
+
286
+ .btn-stop {
287
+ background: #fff;
288
+ color: var(--danger-color);
289
+ border: 1px solid var(--danger-color);
290
+ }
291
+ .btn-stop:hover { background: #fef2f2; }
292
+
293
+ .info-tip {
294
+ margin-top: 20px;
295
+ font-size: 12px;
296
+ color: var(--text-secondary);
297
+ text-align: center;
298
+ background: #f8fafc;
299
+ padding: 8px;
300
+ border-radius: 6px;
301
+ border: 1px dashed #cbd5e1;
302
+ }
303
+
304
+ /* 右侧日志面板 */
305
+ .log-panel {
306
+ background: #1e293b; /* 深色背景用于日志 */
307
+ border-radius: var(--border-radius);
308
+ box-shadow: var(--shadow-md);
309
+ display: flex;
310
+ flex-direction: column;
311
+ overflow: hidden;
312
+ }
313
+
314
+ .log-header {
315
+ padding: 16px 24px;
316
+ border-bottom: 1px solid #334155;
317
+ display: flex;
318
+ justify-content: space-between;
319
+ align-items: center;
320
+ background: #0f172a;
321
+ }
322
+
323
+ .log-header h2 {
324
+ color: #e2e8f0;
325
+ font-size: 16px;
326
+ font-weight: 600;
327
+ display: flex;
328
+ align-items: center;
329
+ gap: 8px;
330
+ }
331
+
332
+ .log-box {
333
+ flex: 1;
334
+ padding: 20px;
335
+ overflow-y: auto;
336
+ font-family: var(--font-mono);
337
+ font-size: 13px;
338
+ line-height: 1.8;
339
+ }
340
+
341
+ /* 滚动条美化 */
342
+ .log-box::-webkit-scrollbar { width: 6px; }
343
+ .log-box::-webkit-scrollbar-track { background: #1e293b; }
344
+ .log-box::-webkit-scrollbar-thumb { background: #475569; border-radius: 3px; }
345
+
346
+ /* 日志条目样式 */
347
+ .log-item {
348
+ display: flex;
349
+ margin-bottom: 6px;
350
+ animation: fadeIn 0.3s ease;
351
+ }
352
+
353
+ @keyframes fadeIn {
354
+ from { opacity: 0; transform: translateY(5px); }
355
+ to { opacity: 1; transform: translateY(0); }
356
+ }
357
+
358
+ .log-time {
359
+ color: #64748b;
360
+ margin-right: 12px;
361
+ min-width: 85px;
362
+ }
363
+
364
+ .log-content { color: #e2e8f0; }
365
+ .log-type-success .log-content { color: #4ade80; }
366
+ .log-type-info .log-content { color: #60a5fa; }
367
+ .log-type-error .log-content { color: #f87171; }
368
+
369
+ @media (max-width: 768px) {
370
+ .dashboard { grid-template-columns: 1fr; height: auto; }
371
+ .log-panel { height: 400px; }
372
+ }
373
+ </style>
@@ -0,0 +1,4 @@
1
+ import { createApp } from 'vue'
2
+ import App from './App.vue'
3
+
4
+ createApp(App).mount('#app')
@@ -0,0 +1,13 @@
1
+ import { defineConfig } from 'vite'
2
+ import vue from '@vitejs/plugin-vue'
3
+
4
+ export default defineConfig({
5
+ plugins: [vue()],
6
+ server: {
7
+ port: 3001
8
+ },
9
+ optimizeDeps: {
10
+ // Don't exclude hyper-scheduler to allow proper module resolution
11
+ // exclude: ['hyper-scheduler']
12
+ }
13
+ })