hyper-scheduler 1.0.0 → 1.1.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/README.md +17 -0
- package/dist/devtools-Bxtz0rO_.cjs +1 -0
- package/dist/devtools-ByJU-Gv1.js +2505 -0
- package/dist/index.cjs +1 -0
- package/dist/index.js +1048 -0
- package/dist/index.umd.cjs +1 -0
- package/docs/.vitepress/cache/deps/_metadata.json +31 -0
- package/docs/.vitepress/cache/deps/chunk-EKBJ2FPM.js +12798 -0
- package/docs/.vitepress/cache/deps/chunk-EKBJ2FPM.js.map +7 -0
- package/docs/.vitepress/cache/deps/package.json +3 -0
- package/docs/.vitepress/cache/deps/vitepress___@vue_devtools-api.js +4505 -0
- package/docs/.vitepress/cache/deps/vitepress___@vue_devtools-api.js.map +7 -0
- package/docs/.vitepress/cache/deps/vitepress___@vueuse_core.js +9731 -0
- package/docs/.vitepress/cache/deps/vitepress___@vueuse_core.js.map +7 -0
- package/docs/.vitepress/cache/deps/vue.js +347 -0
- package/docs/.vitepress/cache/deps/vue.js.map +7 -0
- package/docs/.vitepress/config.ts +4 -0
- package/docs/.vitepress/theme/components/DemoFrame.vue +111 -0
- package/docs/.vitepress/theme/custom.css +6 -0
- package/docs/.vitepress/theme/index.ts +10 -0
- package/docs/api/devtools.md +13 -0
- package/docs/api/scheduler.md +28 -8
- package/docs/examples/index.md +55 -8
- package/docs/guide/getting-started.md +38 -0
- package/package.json +13 -4
- package/.editorconfig +0 -21
- package/.eslintrc.cjs +0 -26
- package/GEMINI.md +0 -1
- package/examples/browser/index.html +0 -354
- package/examples/node/simple.js +0 -36
- package/examples/react-demo/index.html +0 -12
- package/examples/react-demo/package.json +0 -23
- package/examples/react-demo/src/App.css +0 -212
- package/examples/react-demo/src/App.jsx +0 -160
- package/examples/react-demo/src/main.jsx +0 -9
- package/examples/react-demo/vite.config.ts +0 -12
- package/examples/react-demo/yarn.lock +0 -752
- package/examples/vue-demo/index.html +0 -12
- package/examples/vue-demo/package.json +0 -21
- package/examples/vue-demo/src/App.vue +0 -373
- package/examples/vue-demo/src/main.ts +0 -4
- package/examples/vue-demo/vite.config.ts +0 -13
- package/examples/vue-demo/yarn.lock +0 -375
- package/src/constants.ts +0 -18
- package/src/core/retry-strategy.ts +0 -28
- package/src/core/scheduler.ts +0 -601
- package/src/core/task-registry.ts +0 -58
- package/src/index.ts +0 -74
- package/src/platform/browser/browser-timer.ts +0 -66
- package/src/platform/browser/main-thread-timer.ts +0 -16
- package/src/platform/browser/worker.ts +0 -31
- package/src/platform/node/debug-cli.ts +0 -19
- package/src/platform/node/node-timer.ts +0 -15
- package/src/platform/timer-strategy.ts +0 -19
- package/src/plugins/dev-tools.ts +0 -101
- package/src/types.ts +0 -115
- package/src/ui/components/devtools.ts +0 -525
- package/src/ui/components/floating-trigger.ts +0 -102
- package/src/ui/components/icons.ts +0 -16
- package/src/ui/components/resizer.ts +0 -129
- package/src/ui/components/task-detail.ts +0 -228
- package/src/ui/components/task-header.ts +0 -319
- package/src/ui/components/task-list.ts +0 -416
- package/src/ui/components/timeline.ts +0 -364
- package/src/ui/debug-panel.ts +0 -56
- package/src/ui/i18n/en.ts +0 -76
- package/src/ui/i18n/index.ts +0 -42
- package/src/ui/i18n/zh.ts +0 -76
- package/src/ui/store/dev-tools-store.ts +0 -191
- package/src/ui/styles/theme.css.ts +0 -56
- package/src/ui/styles.ts +0 -43
- package/src/utils/cron-lite.ts +0 -221
- package/src/utils/cron.ts +0 -20
- package/src/utils/id.ts +0 -10
- package/src/utils/schedule.ts +0 -93
- package/src/vite-env.d.ts +0 -1
- package/stats.html +0 -4949
- package/tests/integration/Debug.test.ts +0 -58
- package/tests/unit/Plugin.test.ts +0 -16
- package/tests/unit/RetryStrategy.test.ts +0 -21
- package/tests/unit/Scheduler.test.ts +0 -38
- package/tests/unit/schedule.test.ts +0 -70
- package/tests/unit/ui/DevToolsStore.test.ts +0 -67
- package/tsconfig.json +0 -28
- package/vite.config.ts +0 -51
- package/vitest.config.ts +0 -24
package/docs/examples/index.md
CHANGED
|
@@ -2,6 +2,22 @@
|
|
|
2
2
|
|
|
3
3
|
本示例展示了 `Hyper Scheduler` 的核心能力:**双线程任务调度**。你可以同时在主线程(Main Thread)和 Web Worker 线程中运行任务,互不干扰。
|
|
4
4
|
|
|
5
|
+
## 在线演示
|
|
6
|
+
|
|
7
|
+
以下示例展示了完全相同的“双线程心跳监控”逻辑在不同框架中的运行效果。你可以直接在这里交互,或点击右上角图标在新窗口打开。
|
|
8
|
+
|
|
9
|
+
### Browser (原生 JS)
|
|
10
|
+
|
|
11
|
+
<DemoFrame path="/examples/browser/" :devPort="3003" devPath="/examples/browser/index.html" title="examples/browser/index.html" />
|
|
12
|
+
|
|
13
|
+
### Vue 3 Demo
|
|
14
|
+
|
|
15
|
+
<DemoFrame path="/examples/vue-demo/" :devPort="3001" title="examples/vue-demo" />
|
|
16
|
+
|
|
17
|
+
### React Demo
|
|
18
|
+
|
|
19
|
+
<DemoFrame path="/examples/react-demo/" :devPort="3002" title="examples/react-demo" />
|
|
20
|
+
|
|
5
21
|
## 安装与引入
|
|
6
22
|
|
|
7
23
|
### 1. 安装
|
|
@@ -9,12 +25,12 @@
|
|
|
9
25
|
使用你喜欢的包管理器安装 `hyper-scheduler`:
|
|
10
26
|
|
|
11
27
|
```bash
|
|
28
|
+
# yarn (推荐)
|
|
29
|
+
yarn add hyper-scheduler
|
|
30
|
+
|
|
12
31
|
# npm
|
|
13
32
|
npm install hyper-scheduler
|
|
14
33
|
|
|
15
|
-
# yarn
|
|
16
|
-
yarn add hyper-scheduler
|
|
17
|
-
|
|
18
34
|
# pnpm
|
|
19
35
|
pnpm add hyper-scheduler
|
|
20
36
|
```
|
|
@@ -107,7 +123,11 @@ const { Scheduler } = require('hyper-scheduler');
|
|
|
107
123
|
scheduler.createTask({
|
|
108
124
|
id: 'main-heartbeat',
|
|
109
125
|
schedule: '3s',
|
|
110
|
-
options: {
|
|
126
|
+
options: {
|
|
127
|
+
driver: 'main',
|
|
128
|
+
namespace: 'ui',
|
|
129
|
+
runImmediately: true
|
|
130
|
+
},
|
|
111
131
|
handler: () => log('❤️ [Main] 主线程心跳检测正常', 'error')
|
|
112
132
|
});
|
|
113
133
|
|
|
@@ -115,6 +135,9 @@ const { Scheduler } = require('hyper-scheduler');
|
|
|
115
135
|
scheduler.createTask({
|
|
116
136
|
id: 'worker-heartbeat',
|
|
117
137
|
schedule: '5s',
|
|
138
|
+
options: {
|
|
139
|
+
namespace: 'background'
|
|
140
|
+
},
|
|
118
141
|
handler: () => log('💙 [Worker] 后台线程任务执行中', 'info')
|
|
119
142
|
});
|
|
120
143
|
|
|
@@ -150,7 +173,11 @@ console.log('✨ 系统就绪,等待启动指令...');
|
|
|
150
173
|
scheduler.createTask({
|
|
151
174
|
id: 'main-heartbeat',
|
|
152
175
|
schedule: '3s',
|
|
153
|
-
options: {
|
|
176
|
+
options: {
|
|
177
|
+
driver: 'main',
|
|
178
|
+
namespace: 'ui',
|
|
179
|
+
runImmediately: true
|
|
180
|
+
},
|
|
154
181
|
handler: () => {
|
|
155
182
|
console.log(`[${time()}] ❤️ [Main] 主线程心跳检测正常`);
|
|
156
183
|
}
|
|
@@ -160,6 +187,9 @@ scheduler.createTask({
|
|
|
160
187
|
scheduler.createTask({
|
|
161
188
|
id: 'worker-heartbeat',
|
|
162
189
|
schedule: '5s',
|
|
190
|
+
options: {
|
|
191
|
+
namespace: 'background'
|
|
192
|
+
},
|
|
163
193
|
handler: () => {
|
|
164
194
|
console.log(`[${time()}] 💙 [Worker] 后台线程任务执行中`);
|
|
165
195
|
}
|
|
@@ -189,16 +219,25 @@ function App() {
|
|
|
189
219
|
});
|
|
190
220
|
|
|
191
221
|
// 2. 注册任务
|
|
222
|
+
// 主线程心跳 (明确指定 driver: 'main')
|
|
192
223
|
schedulerRef.current.createTask({
|
|
193
224
|
id: 'main-heartbeat',
|
|
194
225
|
schedule: '3s',
|
|
195
|
-
options: {
|
|
226
|
+
options: {
|
|
227
|
+
driver: 'main',
|
|
228
|
+
namespace: 'ui',
|
|
229
|
+
runImmediately: true
|
|
230
|
+
},
|
|
196
231
|
handler: () => addLog('❤️ [Main] 主线程心跳检测正常', 'error')
|
|
197
232
|
});
|
|
198
233
|
|
|
234
|
+
// Worker 线程心跳 (默认即为 Worker 驱动)
|
|
199
235
|
schedulerRef.current.createTask({
|
|
200
236
|
id: 'worker-heartbeat',
|
|
201
237
|
schedule: '5s',
|
|
238
|
+
options: {
|
|
239
|
+
namespace: 'background'
|
|
240
|
+
},
|
|
202
241
|
handler: () => addLog('💙 [Worker] 后台线程任务执行中', 'info')
|
|
203
242
|
});
|
|
204
243
|
|
|
@@ -250,17 +289,25 @@ onMounted(() => {
|
|
|
250
289
|
plugins: [new DevTools({ theme: 'auto', language: 'zh' })]
|
|
251
290
|
})
|
|
252
291
|
|
|
253
|
-
//
|
|
292
|
+
// 主线程心跳 (明确指定 driver: 'main')
|
|
254
293
|
scheduler.value.createTask({
|
|
255
294
|
id: 'main-heartbeat',
|
|
256
295
|
schedule: '3s',
|
|
257
|
-
options: {
|
|
296
|
+
options: {
|
|
297
|
+
driver: 'main',
|
|
298
|
+
namespace: 'ui',
|
|
299
|
+
runImmediately: true
|
|
300
|
+
},
|
|
258
301
|
handler: () => addLog('❤️ [Main] 主线程心跳检测正常', 'error')
|
|
259
302
|
})
|
|
260
303
|
|
|
304
|
+
// Worker 线程心跳 (默认即为 Worker 驱动)
|
|
261
305
|
scheduler.value.createTask({
|
|
262
306
|
id: 'worker-heartbeat',
|
|
263
307
|
schedule: '5s',
|
|
308
|
+
options: {
|
|
309
|
+
namespace: 'background'
|
|
310
|
+
},
|
|
264
311
|
handler: () => addLog('💙 [Worker] 后台线程任务执行中', 'info')
|
|
265
312
|
})
|
|
266
313
|
})
|
|
@@ -78,6 +78,44 @@ scheduler.createTask({
|
|
|
78
78
|
scheduler.start();
|
|
79
79
|
```
|
|
80
80
|
|
|
81
|
+
## 进阶功能
|
|
82
|
+
|
|
83
|
+
### 1. 使用命名空间
|
|
84
|
+
|
|
85
|
+
可以将任务隔离到不同的命名空间中,以便进行批量管理(如启动、停止特定组的任务)。
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
scheduler.createTask({
|
|
89
|
+
id: 'system-log',
|
|
90
|
+
schedule: '1m',
|
|
91
|
+
handler: () => console.log('System Log'),
|
|
92
|
+
options: { namespace: 'system' }
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
scheduler.createTask({
|
|
96
|
+
id: 'user-notification',
|
|
97
|
+
schedule: '5m',
|
|
98
|
+
handler: () => console.log('User Notification'),
|
|
99
|
+
options: { namespace: 'user' }
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
// 仅启动 system 命名空间下的任务
|
|
103
|
+
scheduler.start('system');
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### 2. 立即触发
|
|
107
|
+
|
|
108
|
+
默认情况下,任务会在第一次调度时间到达时执行。如果你希望任务在调度器启动时(或创建时)立即执行一次,可以使用 `runImmediately` 选项。
|
|
109
|
+
|
|
110
|
+
```typescript
|
|
111
|
+
scheduler.createTask({
|
|
112
|
+
id: 'init-check',
|
|
113
|
+
schedule: '1h',
|
|
114
|
+
handler: () => console.log('Checking status...'),
|
|
115
|
+
options: { runImmediately: true }
|
|
116
|
+
});
|
|
117
|
+
```
|
|
118
|
+
|
|
81
119
|
## 浏览器环境
|
|
82
120
|
|
|
83
121
|
在浏览器中可以启用可视化调试工具,通过在 Scheduler 构造函数中传入 DevTools 插件实现:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hyper-scheduler",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "A lightweight, dependency-free (core) JavaScript task scheduler supporting Cron expressions and Web Workers.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.umd.js",
|
|
@@ -12,15 +12,23 @@
|
|
|
12
12
|
"require": "./dist/index.cjs"
|
|
13
13
|
}
|
|
14
14
|
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist",
|
|
17
|
+
"README.md",
|
|
18
|
+
"LICENSE",
|
|
19
|
+
"docs"
|
|
20
|
+
],
|
|
15
21
|
"scripts": {
|
|
16
22
|
"dev": "vite",
|
|
17
23
|
"docs:dev": "vitepress dev docs",
|
|
18
24
|
"docs:build": "vitepress build docs",
|
|
19
25
|
"example:node": "node examples/node/simple.js",
|
|
20
|
-
"example:browser": "vite
|
|
21
|
-
"example:vue": "
|
|
22
|
-
"example:react": "
|
|
26
|
+
"example:browser": "vite serve . --port 3003",
|
|
27
|
+
"example:vue": "cd examples/vue-demo && yarn && yarn dev",
|
|
28
|
+
"example:react": "cd examples/react-demo && yarn && yarn dev",
|
|
29
|
+
"dev:all": "concurrently \"yarn docs:dev\" \"yarn example:vue\" \"yarn example:react\" \"vite examples/browser --port 3003\"",
|
|
23
30
|
"build": "tsc && vite build",
|
|
31
|
+
"prepublishOnly": "yarn build",
|
|
24
32
|
"test": "vitest",
|
|
25
33
|
"lint": "eslint . --ext .ts"
|
|
26
34
|
},
|
|
@@ -38,6 +46,7 @@
|
|
|
38
46
|
"@typescript-eslint/eslint-plugin": "^8.48.0",
|
|
39
47
|
"@typescript-eslint/parser": "^8.48.0",
|
|
40
48
|
"@vitest/coverage-v8": "^4.0.14",
|
|
49
|
+
"concurrently": "^9.2.1",
|
|
41
50
|
"eslint": "^8.0.0",
|
|
42
51
|
"jsdom": "^27.2.0",
|
|
43
52
|
"rollup-plugin-visualizer": "^6.0.5",
|
package/.editorconfig
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
# EditorConfig is awesome: https://EditorConfig.org
|
|
2
|
-
|
|
3
|
-
# top-most EditorConfig file
|
|
4
|
-
root = true
|
|
5
|
-
|
|
6
|
-
# Unix-style newlines with a newline ending every file
|
|
7
|
-
[*]
|
|
8
|
-
end_of_line = lf
|
|
9
|
-
insert_final_newline = true
|
|
10
|
-
charset = utf-8
|
|
11
|
-
|
|
12
|
-
# TypeScript files
|
|
13
|
-
[*.ts]
|
|
14
|
-
indent_style = space
|
|
15
|
-
indent_size = 2
|
|
16
|
-
trim_trailing_whitespace = true
|
|
17
|
-
|
|
18
|
-
# Disable auto-fix for critical UI components
|
|
19
|
-
[src/ui/components/{Timeline,TaskList,DevTools}.ts]
|
|
20
|
-
# These files have specific logic that should not be auto-fixed
|
|
21
|
-
# Manual review required for any changes
|
package/.eslintrc.cjs
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
module.exports = {
|
|
2
|
-
root: true,
|
|
3
|
-
env: {
|
|
4
|
-
browser: true,
|
|
5
|
-
es2021: true,
|
|
6
|
-
node: true,
|
|
7
|
-
},
|
|
8
|
-
extends: [
|
|
9
|
-
'eslint:recommended',
|
|
10
|
-
'plugin:@typescript-eslint/recommended',
|
|
11
|
-
],
|
|
12
|
-
parser: '@typescript-eslint/parser',
|
|
13
|
-
parserOptions: {
|
|
14
|
-
ecmaVersion: 12,
|
|
15
|
-
sourceType: 'module',
|
|
16
|
-
},
|
|
17
|
-
plugins: [
|
|
18
|
-
'@typescript-eslint',
|
|
19
|
-
],
|
|
20
|
-
rules: {
|
|
21
|
-
'@typescript-eslint/no-explicit-any': 'off',
|
|
22
|
-
'@typescript-eslint/no-unused-vars': ['warn', { argsIgnorePattern: '^_' }],
|
|
23
|
-
'no-console': 'off',
|
|
24
|
-
},
|
|
25
|
-
ignorePatterns: ['dist/', 'coverage/', 'examples/'],
|
|
26
|
-
};
|
package/GEMINI.md
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
全部采用中文作为主要语言进行输出,包括文档和注释
|
|
@@ -1,354 +0,0 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="zh-CN">
|
|
3
|
-
|
|
4
|
-
<head>
|
|
5
|
-
<meta charset="UTF-8">
|
|
6
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
7
|
-
<title>Hyper Scheduler - 浏览器示例</title>
|
|
8
|
-
<style>
|
|
9
|
-
:root {
|
|
10
|
-
--bg-color: #f8fafc;
|
|
11
|
-
--card-bg: #ffffff;
|
|
12
|
-
--text-primary: #1e293b;
|
|
13
|
-
--text-secondary: #64748b;
|
|
14
|
-
--accent-color: #3b82f6;
|
|
15
|
-
--accent-hover: #2563eb;
|
|
16
|
-
--danger-color: #ef4444;
|
|
17
|
-
--danger-hover: #dc2626;
|
|
18
|
-
--success-color: #10b981;
|
|
19
|
-
--border-radius: 12px;
|
|
20
|
-
--shadow-sm: 0 1px 3px rgba(0,0,0,0.1);
|
|
21
|
-
--shadow-md: 0 4px 6px -1px rgba(0,0,0,0.1), 0 2px 4px -1px rgba(0,0,0,0.06);
|
|
22
|
-
--font-mono: 'SFMono-Regular', Consolas, 'Lxgw WenKai', 'Liberation Mono', Menlo, monospace;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
* {
|
|
26
|
-
margin: 0;
|
|
27
|
-
padding: 0;
|
|
28
|
-
box-sizing: border-box;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
body {
|
|
32
|
-
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
|
|
33
|
-
background: var(--bg-color);
|
|
34
|
-
color: var(--text-primary);
|
|
35
|
-
height: 100vh;
|
|
36
|
-
display: flex;
|
|
37
|
-
align-items: center;
|
|
38
|
-
justify-content: center;
|
|
39
|
-
padding: 20px;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
.dashboard {
|
|
43
|
-
display: grid;
|
|
44
|
-
grid-template-columns: 300px 1fr;
|
|
45
|
-
gap: 24px;
|
|
46
|
-
width: 100%;
|
|
47
|
-
max-width: 1000px;
|
|
48
|
-
height: 600px;
|
|
49
|
-
background: var(--bg-color);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/* 左侧控制面板 */
|
|
53
|
-
.control-panel {
|
|
54
|
-
background: var(--card-bg);
|
|
55
|
-
border-radius: var(--border-radius);
|
|
56
|
-
padding: 24px;
|
|
57
|
-
box-shadow: var(--shadow-md);
|
|
58
|
-
display: flex;
|
|
59
|
-
flex-direction: column;
|
|
60
|
-
justify-content: space-between;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
.header h1 {
|
|
64
|
-
font-size: 24px;
|
|
65
|
-
font-weight: 700;
|
|
66
|
-
margin-bottom: 8px;
|
|
67
|
-
background: linear-gradient(135deg, #3b82f6 0%, #8b5cf6 100%);
|
|
68
|
-
-webkit-background-clip: text;
|
|
69
|
-
-webkit-text-fill-color: transparent;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
.header p {
|
|
73
|
-
color: var(--text-secondary);
|
|
74
|
-
font-size: 14px;
|
|
75
|
-
margin-bottom: 24px;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
.task-status {
|
|
79
|
-
margin-bottom: auto;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
.status-item {
|
|
83
|
-
display: flex;
|
|
84
|
-
align-items: center;
|
|
85
|
-
padding: 12px;
|
|
86
|
-
background: #f1f5f9;
|
|
87
|
-
border-radius: 8px;
|
|
88
|
-
margin-bottom: 12px;
|
|
89
|
-
font-size: 14px;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
.status-dot {
|
|
93
|
-
width: 8px;
|
|
94
|
-
height: 8px;
|
|
95
|
-
border-radius: 50%;
|
|
96
|
-
margin-right: 10px;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
.dot-main { background: #ef4444; box-shadow: 0 0 8px rgba(239, 68, 68, 0.4); }
|
|
100
|
-
.dot-worker { background: #3b82f6; box-shadow: 0 0 8px rgba(59, 130, 246, 0.4); }
|
|
101
|
-
|
|
102
|
-
.actions {
|
|
103
|
-
display: flex;
|
|
104
|
-
flex-direction: column;
|
|
105
|
-
gap: 12px;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
/* 按钮状态控制 */
|
|
109
|
-
#btn-stop { display: none; }
|
|
110
|
-
body.running #btn-start { display: none; }
|
|
111
|
-
body.running #btn-stop { display: block; }
|
|
112
|
-
|
|
113
|
-
button {
|
|
114
|
-
display: flex;
|
|
115
|
-
align-items: center;
|
|
116
|
-
justify-content: center;
|
|
117
|
-
width: 100%;
|
|
118
|
-
padding: 12px;
|
|
119
|
-
border: none;
|
|
120
|
-
border-radius: 8px;
|
|
121
|
-
font-size: 14px;
|
|
122
|
-
font-weight: 600;
|
|
123
|
-
cursor: pointer;
|
|
124
|
-
transition: all 0.2s ease;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
.btn-start {
|
|
128
|
-
background: var(--accent-color);
|
|
129
|
-
color: white;
|
|
130
|
-
}
|
|
131
|
-
.btn-start:hover { background: var(--accent-hover); transform: translateY(-1px); }
|
|
132
|
-
.btn-start:active { transform: translateY(0); }
|
|
133
|
-
|
|
134
|
-
.btn-stop {
|
|
135
|
-
background: #fff;
|
|
136
|
-
color: var(--danger-color);
|
|
137
|
-
border: 1px solid var(--danger-color);
|
|
138
|
-
}
|
|
139
|
-
.btn-stop:hover { background: #fef2f2; }
|
|
140
|
-
|
|
141
|
-
.info-tip {
|
|
142
|
-
margin-top: 20px;
|
|
143
|
-
font-size: 12px;
|
|
144
|
-
color: var(--text-secondary);
|
|
145
|
-
text-align: center;
|
|
146
|
-
background: #f8fafc;
|
|
147
|
-
padding: 8px;
|
|
148
|
-
border-radius: 6px;
|
|
149
|
-
border: 1px dashed #cbd5e1;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
/* 右侧日志面板 */
|
|
153
|
-
.log-panel {
|
|
154
|
-
background: #1e293b; /* 深色背景用于日志 */
|
|
155
|
-
border-radius: var(--border-radius);
|
|
156
|
-
box-shadow: var(--shadow-md);
|
|
157
|
-
display: flex;
|
|
158
|
-
flex-direction: column;
|
|
159
|
-
overflow: hidden;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
.log-header {
|
|
163
|
-
padding: 16px 24px;
|
|
164
|
-
border-bottom: 1px solid #334155;
|
|
165
|
-
display: flex;
|
|
166
|
-
justify-content: space-between;
|
|
167
|
-
align-items: center;
|
|
168
|
-
background: #0f172a;
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
.log-header h2 {
|
|
172
|
-
color: #e2e8f0;
|
|
173
|
-
font-size: 16px;
|
|
174
|
-
font-weight: 600;
|
|
175
|
-
display: flex;
|
|
176
|
-
align-items: center;
|
|
177
|
-
gap: 8px;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
.log-box {
|
|
181
|
-
flex: 1;
|
|
182
|
-
padding: 20px;
|
|
183
|
-
overflow-y: auto;
|
|
184
|
-
font-family: var(--font-mono);
|
|
185
|
-
font-size: 13px;
|
|
186
|
-
line-height: 1.8;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
/* 滚动条美化 */
|
|
190
|
-
.log-box::-webkit-scrollbar { width: 6px; }
|
|
191
|
-
.log-box::-webkit-scrollbar-track { background: #1e293b; }
|
|
192
|
-
.log-box::-webkit-scrollbar-thumb { background: #475569; border-radius: 3px; }
|
|
193
|
-
|
|
194
|
-
/* 日志条目样式 */
|
|
195
|
-
.log-item {
|
|
196
|
-
display: flex;
|
|
197
|
-
margin-bottom: 6px;
|
|
198
|
-
animation: fadeIn 0.3s ease;
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
@keyframes fadeIn {
|
|
202
|
-
from { opacity: 0; transform: translateY(5px); }
|
|
203
|
-
to { opacity: 1; transform: translateY(0); }
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
.log-time {
|
|
207
|
-
color: #64748b;
|
|
208
|
-
margin-right: 12px;
|
|
209
|
-
min-width: 85px;
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
.log-content { color: #e2e8f0; }
|
|
213
|
-
.log-type-success .log-content { color: #4ade80; }
|
|
214
|
-
.log-type-info .log-content { color: #60a5fa; }
|
|
215
|
-
.log-type-error .log-content { color: #f87171; }
|
|
216
|
-
|
|
217
|
-
@media (max-width: 768px) {
|
|
218
|
-
.dashboard { grid-template-columns: 1fr; height: auto; }
|
|
219
|
-
body { height: auto; }
|
|
220
|
-
.log-panel { height: 400px; }
|
|
221
|
-
}
|
|
222
|
-
</style>
|
|
223
|
-
</head>
|
|
224
|
-
|
|
225
|
-
<body>
|
|
226
|
-
<div class="dashboard">
|
|
227
|
-
<!-- 左侧控制区 -->
|
|
228
|
-
<div class="control-panel">
|
|
229
|
-
<div>
|
|
230
|
-
<div class="header">
|
|
231
|
-
<h1>Hyper Scheduler</h1>
|
|
232
|
-
<p>双线程任务调度演示</p>
|
|
233
|
-
</div>
|
|
234
|
-
|
|
235
|
-
<div class="task-status">
|
|
236
|
-
<div class="status-item">
|
|
237
|
-
<span class="status-dot dot-main"></span>
|
|
238
|
-
<div>
|
|
239
|
-
<strong>主线程任务</strong>
|
|
240
|
-
<div style="font-size: 12px; color: #64748b; margin-top: 2px">每 3 秒 (driver: 'main')</div>
|
|
241
|
-
</div>
|
|
242
|
-
</div>
|
|
243
|
-
<div class="status-item">
|
|
244
|
-
<span class="status-dot dot-worker"></span>
|
|
245
|
-
<div>
|
|
246
|
-
<strong>Worker 任务</strong>
|
|
247
|
-
<div style="font-size: 12px; color: #64748b; margin-top: 2px">每 5 秒 (driver: 'worker')</div>
|
|
248
|
-
</div>
|
|
249
|
-
</div>
|
|
250
|
-
</div>
|
|
251
|
-
</div>
|
|
252
|
-
|
|
253
|
-
<div>
|
|
254
|
-
<div class="actions">
|
|
255
|
-
<button class="btn-start" id="btn-start">
|
|
256
|
-
<span style="margin-right: 8px">▶</span> 启动调度器
|
|
257
|
-
</button>
|
|
258
|
-
<button class="btn-stop" id="btn-stop">
|
|
259
|
-
<span style="margin-right: 8px">⏹</span> 停止调度器
|
|
260
|
-
</button>
|
|
261
|
-
</div>
|
|
262
|
-
<div class="info-tip">
|
|
263
|
-
💡 点击右下角悬浮球打开调试面板
|
|
264
|
-
</div>
|
|
265
|
-
</div>
|
|
266
|
-
</div>
|
|
267
|
-
|
|
268
|
-
<!-- 右侧日志区 -->
|
|
269
|
-
<div class="log-panel">
|
|
270
|
-
<div class="log-header">
|
|
271
|
-
<h2>
|
|
272
|
-
<span>📋</span> 执行日志
|
|
273
|
-
</h2>
|
|
274
|
-
<div style="font-size: 12px; color: #64748b">实时监控中...</div>
|
|
275
|
-
</div>
|
|
276
|
-
<div id="logs" class="log-box"></div>
|
|
277
|
-
</div>
|
|
278
|
-
</div>
|
|
279
|
-
|
|
280
|
-
<script src="../../dist/index.umd.cjs"></script>
|
|
281
|
-
<script>
|
|
282
|
-
const logBox = document.getElementById('logs');
|
|
283
|
-
|
|
284
|
-
function log(msg, type = 'info') {
|
|
285
|
-
const time = new Date().toLocaleTimeString('zh-CN', { hour12: false, hour: '2-digit', minute: '2-digit', second: '2-digit' });
|
|
286
|
-
|
|
287
|
-
const item = document.createElement('div');
|
|
288
|
-
item.className = `log-item log-type-${type}`;
|
|
289
|
-
|
|
290
|
-
item.innerHTML = `
|
|
291
|
-
<span class="log-time">${time}</span>
|
|
292
|
-
<span class="log-content">${msg}</span>
|
|
293
|
-
`;
|
|
294
|
-
|
|
295
|
-
logBox.appendChild(item);
|
|
296
|
-
|
|
297
|
-
// 保持最新的日志可见,但如果用户向上滚动了则不强制滚动
|
|
298
|
-
const isScrolledToBottom = logBox.scrollHeight - logBox.clientHeight <= logBox.scrollTop + 10;
|
|
299
|
-
if (isScrolledToBottom || logBox.children.length <= 2) {
|
|
300
|
-
logBox.scrollTop = logBox.scrollHeight;
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
const { Scheduler, DevTools } = window.HyperScheduler;
|
|
305
|
-
|
|
306
|
-
const scheduler = new Scheduler({
|
|
307
|
-
debug: true,
|
|
308
|
-
plugins: [new DevTools({
|
|
309
|
-
theme: 'auto',
|
|
310
|
-
language: 'zh',
|
|
311
|
-
trigger: {
|
|
312
|
-
position: 'bottom-right',
|
|
313
|
-
backgroundColor: '#3b82f6'
|
|
314
|
-
}
|
|
315
|
-
})]
|
|
316
|
-
});
|
|
317
|
-
|
|
318
|
-
// 主线程心跳 (明确指定 driver: 'main')
|
|
319
|
-
scheduler.createTask({
|
|
320
|
-
id: 'main-heartbeat',
|
|
321
|
-
schedule: '3s',
|
|
322
|
-
options: { driver: 'main' },
|
|
323
|
-
handler: () => {
|
|
324
|
-
log('❤️ [Main] 主线程心跳检测正常', 'error'); // 使用 error 样式红色,对应 UI 的红色点
|
|
325
|
-
}
|
|
326
|
-
});
|
|
327
|
-
|
|
328
|
-
// Worker 线程心跳 (默认即为 Worker 驱动)
|
|
329
|
-
scheduler.createTask({
|
|
330
|
-
id: 'worker-heartbeat',
|
|
331
|
-
schedule: '5s',
|
|
332
|
-
handler: () => {
|
|
333
|
-
log('💙 [Worker] 后台线程任务执行中', 'info'); // 使用 info 样式蓝色,对应 UI 的蓝色点
|
|
334
|
-
}
|
|
335
|
-
});
|
|
336
|
-
|
|
337
|
-
// 按钮事件
|
|
338
|
-
document.getElementById('btn-start').onclick = () => {
|
|
339
|
-
scheduler.start();
|
|
340
|
-
document.body.classList.add('running');
|
|
341
|
-
log('🚀 调度器系统已启动', 'success');
|
|
342
|
-
};
|
|
343
|
-
|
|
344
|
-
document.getElementById('btn-stop').onclick = () => {
|
|
345
|
-
scheduler.stop();
|
|
346
|
-
document.body.classList.remove('running');
|
|
347
|
-
log('⏹️ 调度器系统已停止', 'info');
|
|
348
|
-
};
|
|
349
|
-
|
|
350
|
-
log('✨ 系统就绪,等待启动指令...', 'info');
|
|
351
|
-
</script>
|
|
352
|
-
</body>
|
|
353
|
-
|
|
354
|
-
</html>
|
package/examples/node/simple.js
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
// 注意: 在实际项目中,你应该使用 import { Scheduler } from 'hyper-scheduler';
|
|
2
|
-
// 此处为了演示本地构建版本,直接引用 dist 目录下的 ESM 构建产物
|
|
3
|
-
|
|
4
|
-
import { Scheduler } from '../../dist/index.js';
|
|
5
|
-
|
|
6
|
-
// 创建调度器
|
|
7
|
-
const scheduler = new Scheduler({ debug: true });
|
|
8
|
-
|
|
9
|
-
// 辅助函数:格式化时间
|
|
10
|
-
const time = () => new Date().toLocaleTimeString('zh-CN', { hour12: false });
|
|
11
|
-
|
|
12
|
-
console.log('✨ 系统就绪,等待启动指令...');
|
|
13
|
-
|
|
14
|
-
// 主线程心跳 (明确指定 driver: 'main')
|
|
15
|
-
scheduler.createTask({
|
|
16
|
-
id: 'main-heartbeat',
|
|
17
|
-
schedule: '3s',
|
|
18
|
-
options: { driver: 'main' }, // 明确指定为主线程驱动
|
|
19
|
-
handler: () => {
|
|
20
|
-
console.log(`[${time()}] ❤️ [Main] 主线程心跳检测正常`);
|
|
21
|
-
}
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
// Worker 线程心跳 (默认即为 Worker 驱动)
|
|
25
|
-
scheduler.createTask({
|
|
26
|
-
id: 'worker-heartbeat',
|
|
27
|
-
schedule: '5s',
|
|
28
|
-
handler: () => {
|
|
29
|
-
console.log(`[${time()}] 💙 [Worker] 后台线程任务执行中`);
|
|
30
|
-
}
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
// 启动调度器
|
|
34
|
-
scheduler.start();
|
|
35
|
-
|
|
36
|
-
console.log(`[${time()}] 🚀 调度器已启动,按 Ctrl+C 退出`);
|
|
@@ -1,12 +0,0 @@
|
|
|
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 React Demo</title>
|
|
7
|
-
</head>
|
|
8
|
-
<body>
|
|
9
|
-
<div id="root"></div>
|
|
10
|
-
<script type="module" src="/src/main.jsx"></script>
|
|
11
|
-
</body>
|
|
12
|
-
</html>
|