@zhin.js/console 1.0.21 → 1.0.23
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/CHANGELOG.md +20 -0
- package/README.md +4 -4
- package/client/components.json +17 -0
- package/client/index.html +1 -1
- package/client/src/components/PluginConfigForm/BasicFieldRenderers.tsx +89 -180
- package/client/src/components/PluginConfigForm/CollectionFieldRenderers.tsx +97 -200
- package/client/src/components/PluginConfigForm/CompositeFieldRenderers.tsx +31 -70
- package/client/src/components/PluginConfigForm/FieldRenderer.tsx +27 -77
- package/client/src/components/PluginConfigForm/NestedFieldRenderer.tsx +33 -53
- package/client/src/components/PluginConfigForm/index.tsx +71 -173
- package/client/src/components/ui/accordion.tsx +54 -0
- package/client/src/components/ui/alert.tsx +62 -0
- package/client/src/components/ui/avatar.tsx +41 -0
- package/client/src/components/ui/badge.tsx +32 -0
- package/client/src/components/ui/button.tsx +50 -0
- package/client/src/components/ui/card.tsx +50 -0
- package/client/src/components/ui/checkbox.tsx +25 -0
- package/client/src/components/ui/dialog.tsx +87 -0
- package/client/src/components/ui/dropdown-menu.tsx +97 -0
- package/client/src/components/ui/input.tsx +21 -0
- package/client/src/components/ui/scroll-area.tsx +43 -0
- package/client/src/components/ui/select.tsx +127 -0
- package/client/src/components/ui/separator.tsx +23 -0
- package/client/src/components/ui/skeleton.tsx +12 -0
- package/client/src/components/ui/switch.tsx +26 -0
- package/client/src/components/ui/tabs.tsx +52 -0
- package/client/src/components/ui/textarea.tsx +20 -0
- package/client/src/components/ui/tooltip.tsx +27 -0
- package/client/src/layouts/dashboard.tsx +91 -221
- package/client/src/main.tsx +38 -42
- package/client/src/pages/dashboard-bots.tsx +91 -137
- package/client/src/pages/dashboard-home.tsx +133 -204
- package/client/src/pages/dashboard-logs.tsx +125 -196
- package/client/src/pages/dashboard-plugin-detail.tsx +261 -329
- package/client/src/pages/dashboard-plugins.tsx +108 -105
- package/client/src/style.css +156 -865
- package/client/src/theme/index.ts +60 -35
- package/client/tailwind.config.js +78 -69
- package/dist/client.js +1 -1
- package/dist/cva.js +47 -0
- package/dist/index.html +1 -1
- package/dist/index.js +6 -6
- package/dist/react-router.js +7121 -5585
- package/dist/react.js +192 -149
- package/dist/style.css +2 -2
- package/lib/bin.js +2 -2
- package/lib/build.js +2 -2
- package/lib/index.d.ts +0 -3
- package/lib/index.js +160 -205
- package/lib/transform.d.ts +26 -0
- package/lib/transform.js +78 -0
- package/lib/websocket.d.ts +0 -1
- package/package.json +9 -8
- package/dist/radix-ui-themes.js +0 -9305
- package/lib/dev.d.ts +0 -18
- package/lib/dev.js +0 -87
package/lib/bin.js
CHANGED
|
@@ -46,7 +46,7 @@ async function build2(root, config = {}) {
|
|
|
46
46
|
"react/jsx-runtime",
|
|
47
47
|
"react/jsx-dev-runtime",
|
|
48
48
|
"radix-ui",
|
|
49
|
-
"
|
|
49
|
+
"class-variance-authority",
|
|
50
50
|
"lucide-react",
|
|
51
51
|
"@zhin.js/client"
|
|
52
52
|
],
|
|
@@ -57,7 +57,7 @@ async function build2(root, config = {}) {
|
|
|
57
57
|
"react": root + "/react.js",
|
|
58
58
|
"react-dom": root + "/react-dom.js",
|
|
59
59
|
"radix-ui": root + "/radix-ui.js",
|
|
60
|
-
"
|
|
60
|
+
"class-variance-authority": root + "/cva.js"
|
|
61
61
|
}
|
|
62
62
|
},
|
|
63
63
|
output: {
|
package/lib/build.js
CHANGED
|
@@ -46,7 +46,7 @@ async function build2(root, config = {}) {
|
|
|
46
46
|
"react/jsx-runtime",
|
|
47
47
|
"react/jsx-dev-runtime",
|
|
48
48
|
"radix-ui",
|
|
49
|
-
"
|
|
49
|
+
"class-variance-authority",
|
|
50
50
|
"lucide-react",
|
|
51
51
|
"@zhin.js/client"
|
|
52
52
|
],
|
|
@@ -57,7 +57,7 @@ async function build2(root, config = {}) {
|
|
|
57
57
|
"react": root + "/react.js",
|
|
58
58
|
"react-dom": root + "/react-dom.js",
|
|
59
59
|
"radix-ui": root + "/radix-ui.js",
|
|
60
|
-
"
|
|
60
|
+
"class-variance-authority": root + "/cva.js"
|
|
61
61
|
}
|
|
62
62
|
},
|
|
63
63
|
output: {
|
package/lib/index.d.ts
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
import * as _zhin_js_http from '@zhin.js/http';
|
|
2
2
|
import { WebSocketServer } from 'ws';
|
|
3
|
-
import { ViteDevServer } from 'vite';
|
|
4
3
|
|
|
5
4
|
interface ConsoleConfig {
|
|
6
5
|
/** 是否启用控制台插件,默认 true */
|
|
7
6
|
enabled?: boolean;
|
|
8
|
-
/** 是否延迟加载 Vite(开发模式),默认 true */
|
|
9
7
|
/** 端口号(继承自 http 配置) */
|
|
10
8
|
port?: number;
|
|
11
9
|
}
|
|
@@ -14,7 +12,6 @@ type WebEntry = string | {
|
|
|
14
12
|
development: string;
|
|
15
13
|
};
|
|
16
14
|
interface WebServer {
|
|
17
|
-
vite?: ViteDevServer;
|
|
18
15
|
addEntry(entry: WebEntry): () => void;
|
|
19
16
|
entries: Record<string, string>;
|
|
20
17
|
ws: WebSocketServer;
|
package/lib/index.js
CHANGED
|
@@ -1,111 +1,11 @@
|
|
|
1
|
-
import * as path2 from 'path';
|
|
2
|
-
import path2__default from 'path';
|
|
3
|
-
import * as fs2 from 'fs';
|
|
4
|
-
import fs2__default from 'fs';
|
|
5
1
|
import { usePlugin } from '@zhin.js/core';
|
|
6
2
|
import mime from 'mime';
|
|
3
|
+
import * as fs2 from 'fs';
|
|
4
|
+
import * as path2 from 'path';
|
|
7
5
|
import WebSocket from 'ws';
|
|
6
|
+
import { transform } from 'esbuild';
|
|
8
7
|
|
|
9
|
-
|
|
10
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
11
|
-
var __esm = (fn, res) => function __init() {
|
|
12
|
-
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
13
|
-
};
|
|
14
|
-
var __export = (target, all) => {
|
|
15
|
-
for (var name in all)
|
|
16
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
// src/dev.ts
|
|
20
|
-
var dev_exports = {};
|
|
21
|
-
__export(dev_exports, {
|
|
22
|
-
createViteDevServer: () => createViteDevServer
|
|
23
|
-
});
|
|
24
|
-
async function createViteDevServer(options) {
|
|
25
|
-
const { root: root3, base = "/vite/", enableTailwind = true } = options;
|
|
26
|
-
try {
|
|
27
|
-
const [
|
|
28
|
-
{ createServer, searchForWorkspaceRoot },
|
|
29
|
-
{ default: react },
|
|
30
|
-
{ default: tailwindcss }
|
|
31
|
-
] = await Promise.all([
|
|
32
|
-
import('vite'),
|
|
33
|
-
import('@vitejs/plugin-react'),
|
|
34
|
-
import('@tailwindcss/vite')
|
|
35
|
-
]);
|
|
36
|
-
const plugins = [react()];
|
|
37
|
-
if (enableTailwind) {
|
|
38
|
-
plugins.push(tailwindcss());
|
|
39
|
-
}
|
|
40
|
-
const clientPath = path2__default.resolve(process.cwd(), "node_modules/@zhin.js/client/client");
|
|
41
|
-
if (!fs2__default.existsSync(clientPath)) {
|
|
42
|
-
throw new Error("@zhin.js/client not found");
|
|
43
|
-
}
|
|
44
|
-
return await createServer({
|
|
45
|
-
root: root3,
|
|
46
|
-
base,
|
|
47
|
-
plugins: [react(), tailwindcss()],
|
|
48
|
-
server: {
|
|
49
|
-
middlewareMode: true,
|
|
50
|
-
allowedHosts: true,
|
|
51
|
-
fs: {
|
|
52
|
-
strict: false,
|
|
53
|
-
// 添加文件访问过滤,避免访问特殊文件
|
|
54
|
-
allow: [
|
|
55
|
-
// 允许访问的目录
|
|
56
|
-
root3,
|
|
57
|
-
searchForWorkspaceRoot(root3),
|
|
58
|
-
path2__default.resolve(process.cwd(), "node_modules"),
|
|
59
|
-
path2__default.resolve(process.cwd(), "client"),
|
|
60
|
-
path2__default.resolve(process.cwd(), "src")
|
|
61
|
-
],
|
|
62
|
-
// 拒绝访问某些文件模式
|
|
63
|
-
deny: [
|
|
64
|
-
"**/.git/**",
|
|
65
|
-
"**/node_modules/.cache/**",
|
|
66
|
-
"**/*.socket",
|
|
67
|
-
"**/*.pipe",
|
|
68
|
-
"**/Dockerfile*",
|
|
69
|
-
"**/.env*"
|
|
70
|
-
]
|
|
71
|
-
}
|
|
72
|
-
},
|
|
73
|
-
resolve: {
|
|
74
|
-
dedupe: [
|
|
75
|
-
"react",
|
|
76
|
-
"react-dom",
|
|
77
|
-
"clsx",
|
|
78
|
-
"tailwind-merge",
|
|
79
|
-
"@reduxjs/toolkit",
|
|
80
|
-
"react-router",
|
|
81
|
-
"react-redux",
|
|
82
|
-
"redux-persist"
|
|
83
|
-
],
|
|
84
|
-
alias: {
|
|
85
|
-
"@zhin.js/client": path2__default.resolve(process.cwd(), "node_modules/@zhin.js/client/client"),
|
|
86
|
-
"@": path2__default.resolve(root3, "../client/src")
|
|
87
|
-
}
|
|
88
|
-
},
|
|
89
|
-
optimizeDeps: {
|
|
90
|
-
include: ["react", "react-dom"]
|
|
91
|
-
},
|
|
92
|
-
build: {
|
|
93
|
-
rollupOptions: {
|
|
94
|
-
input: root3 + "/index.html"
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
});
|
|
98
|
-
} catch (error) {
|
|
99
|
-
throw new Error(
|
|
100
|
-
`Failed to create Vite dev server. Make sure all development dependencies are installed: vite, @vitejs/plugin-react, @tailwindcss/vite. Run: pnpm install --include=optional
|
|
101
|
-
Original error: ${error.message}`
|
|
102
|
-
);
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
var init_dev = __esm({
|
|
106
|
-
"src/dev.ts"() {
|
|
107
|
-
}
|
|
108
|
-
});
|
|
8
|
+
// src/index.ts
|
|
109
9
|
var { root, logger } = usePlugin();
|
|
110
10
|
function setupWebSocket(webServer) {
|
|
111
11
|
webServer.ws.on("connection", (ws) => {
|
|
@@ -246,20 +146,76 @@ function notifyDataUpdate(webServer) {
|
|
|
246
146
|
timestamp: Date.now()
|
|
247
147
|
});
|
|
248
148
|
}
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
149
|
+
var cache = /* @__PURE__ */ new Map();
|
|
150
|
+
var TRANSFORMABLE_EXTS = [".ts", ".tsx", ".jsx"];
|
|
151
|
+
var RESOLVE_EXTS = [".tsx", ".ts", ".jsx", ".js"];
|
|
152
|
+
function isTransformable(filePath) {
|
|
153
|
+
const ext = path2.extname(filePath).toLowerCase();
|
|
154
|
+
return TRANSFORMABLE_EXTS.includes(ext);
|
|
155
|
+
}
|
|
156
|
+
var IMPORT_RE = /(?:import|export)\s+.*?\s+from\s+['"]([^'"]+)['"]|import\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
|
|
157
|
+
var CSS_IMPORT_RE = /import\s+['"][^'"]+\.css['"]\s*;?\n?/g;
|
|
158
|
+
function isRelative(specifier) {
|
|
159
|
+
return specifier.startsWith("./") || specifier.startsWith("../");
|
|
160
|
+
}
|
|
161
|
+
function hasExtension(specifier) {
|
|
162
|
+
const base = path2.basename(specifier);
|
|
163
|
+
return base.includes(".") && !base.startsWith(".");
|
|
164
|
+
}
|
|
165
|
+
function resolveRelativeImport(specifier, fromFile) {
|
|
166
|
+
if (!isRelative(specifier)) return specifier;
|
|
167
|
+
if (hasExtension(specifier)) return specifier;
|
|
168
|
+
const dir = path2.dirname(fromFile);
|
|
169
|
+
const target = path2.resolve(dir, specifier);
|
|
170
|
+
for (const ext of RESOLVE_EXTS) {
|
|
171
|
+
if (fs2.existsSync(target + ext)) {
|
|
172
|
+
return specifier + ext;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
if (fs2.existsSync(target) && fs2.statSync(target).isDirectory()) {
|
|
176
|
+
for (const ext of RESOLVE_EXTS) {
|
|
177
|
+
if (fs2.existsSync(path2.join(target, `index${ext}`))) {
|
|
178
|
+
return specifier + `/index${ext}`;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
261
181
|
}
|
|
182
|
+
return specifier;
|
|
262
183
|
}
|
|
184
|
+
function rewriteImports(code, fromFile) {
|
|
185
|
+
code = code.replace(CSS_IMPORT_RE, "");
|
|
186
|
+
code = code.replace(IMPORT_RE, (match, fromSpecifier, dynamicSpecifier) => {
|
|
187
|
+
const specifier = fromSpecifier || dynamicSpecifier;
|
|
188
|
+
if (!specifier || !isRelative(specifier)) return match;
|
|
189
|
+
if (hasExtension(specifier)) return match;
|
|
190
|
+
const resolved = resolveRelativeImport(specifier, fromFile);
|
|
191
|
+
if (resolved === specifier) return match;
|
|
192
|
+
return match.replace(specifier, resolved);
|
|
193
|
+
});
|
|
194
|
+
return code;
|
|
195
|
+
}
|
|
196
|
+
async function transformFile(filePath) {
|
|
197
|
+
const stat = fs2.statSync(filePath);
|
|
198
|
+
const cached = cache.get(filePath);
|
|
199
|
+
if (cached && cached.mtime === stat.mtimeMs) {
|
|
200
|
+
return cached.code;
|
|
201
|
+
}
|
|
202
|
+
const source = fs2.readFileSync(filePath, "utf-8");
|
|
203
|
+
const ext = path2.extname(filePath).toLowerCase();
|
|
204
|
+
const loader = ext === ".tsx" ? "tsx" : ext === ".ts" ? "ts" : ext === ".jsx" ? "jsx" : "js";
|
|
205
|
+
const result = await transform(source, {
|
|
206
|
+
loader,
|
|
207
|
+
jsx: "automatic",
|
|
208
|
+
jsxImportSource: "react",
|
|
209
|
+
format: "esm",
|
|
210
|
+
sourcemap: "inline",
|
|
211
|
+
target: "es2022"
|
|
212
|
+
});
|
|
213
|
+
const code = rewriteImports(result.code, filePath);
|
|
214
|
+
cache.set(filePath, { mtime: stat.mtimeMs, code });
|
|
215
|
+
return code;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// src/index.ts
|
|
263
219
|
var { provide, root: root2, useContext, logger: logger2, inject, onDispose } = usePlugin();
|
|
264
220
|
var configService = inject("config");
|
|
265
221
|
var appConfig = configService?.get("zhin.config.yml") || {};
|
|
@@ -278,18 +234,36 @@ if (enabled) {
|
|
|
278
234
|
data: { key, value }
|
|
279
235
|
});
|
|
280
236
|
useContext("router", async (router) => {
|
|
281
|
-
const
|
|
282
|
-
const
|
|
283
|
-
const
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
237
|
+
const entryBases = /* @__PURE__ */ new Map();
|
|
238
|
+
const genToken = () => Math.random().toString(36).slice(2, 8);
|
|
239
|
+
const watchedDirs = /* @__PURE__ */ new Set();
|
|
240
|
+
const watchers = [];
|
|
241
|
+
path2.join(import.meta.dirname, "../client");
|
|
242
|
+
const distDir = path2.join(import.meta.dirname, "../dist");
|
|
243
|
+
const resolveFile = (name) => {
|
|
244
|
+
const distPath = path2.resolve(distDir, name);
|
|
245
|
+
if (fs2.existsSync(distPath)) return distPath;
|
|
246
|
+
return null;
|
|
247
|
+
};
|
|
287
248
|
const webServer = {
|
|
288
249
|
entries: {},
|
|
289
250
|
addEntry(entry) {
|
|
290
251
|
const hash = Date.now().toString(16) + Math.random().toString(16).slice(2, 8);
|
|
291
|
-
const entryFile = typeof entry === "string" ? entry : entry
|
|
292
|
-
|
|
252
|
+
const entryFile = typeof entry === "string" ? entry : entry.production;
|
|
253
|
+
const dir = path2.dirname(entryFile);
|
|
254
|
+
const filename = path2.basename(entryFile);
|
|
255
|
+
let token;
|
|
256
|
+
for (const [t, d] of entryBases) {
|
|
257
|
+
if (d === dir) {
|
|
258
|
+
token = t;
|
|
259
|
+
break;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
if (!token) {
|
|
263
|
+
token = genToken();
|
|
264
|
+
entryBases.set(token, dir);
|
|
265
|
+
}
|
|
266
|
+
this.entries[hash] = `/vite/@ext/${token}/${filename}`;
|
|
293
267
|
if (this.ws) {
|
|
294
268
|
for (const ws of this.ws.clients || []) {
|
|
295
269
|
ws.send(JSON.stringify(createAddMsg("entries", this.entries[hash])));
|
|
@@ -306,64 +280,15 @@ if (enabled) {
|
|
|
306
280
|
},
|
|
307
281
|
ws: router.ws("/server")
|
|
308
282
|
};
|
|
309
|
-
|
|
310
|
-
if (viteStarted || viteStarting || !isDev) return;
|
|
311
|
-
viteStarting = true;
|
|
312
|
-
try {
|
|
313
|
-
logger2.info("\u{1F504} \u68C0\u6D4B\u5230\u63A7\u5236\u53F0\u8BBF\u95EE\uFF0C\u6B63\u5728\u542F\u52A8 Vite \u5F00\u53D1\u670D\u52A1\u5668...");
|
|
314
|
-
devDeps = await loadDevDependencies();
|
|
315
|
-
if (devDeps) {
|
|
316
|
-
webServer.vite = await devDeps.createViteDevServer({
|
|
317
|
-
root: rootDir,
|
|
318
|
-
base,
|
|
319
|
-
enableTailwind: true
|
|
320
|
-
});
|
|
321
|
-
viteStarted = true;
|
|
322
|
-
logger2.info("\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557");
|
|
323
|
-
logger2.info("\u2551 Web \u63A7\u5236\u53F0\u5DF2\u542F\u52A8 (\u6309\u9700\u52A0\u8F7D) \u2551");
|
|
324
|
-
logger2.info("\u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563");
|
|
325
|
-
logger2.info("\u2551 \u5730\u5740: http://localhost:8086/ \u2551");
|
|
326
|
-
logger2.info("\u2551 \u6A21\u5F0F: \u5F00\u53D1\u6A21\u5F0F (Vite HMR) \u2551");
|
|
327
|
-
logger2.info("\u2551 \u5185\u5B58: \u5DF2\u52A0\u8F7D (~23MB) \u2551");
|
|
328
|
-
logger2.info("\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D");
|
|
329
|
-
}
|
|
330
|
-
} catch (error) {
|
|
331
|
-
logger2.error("Vite \u542F\u52A8\u5931\u8D25:", error);
|
|
332
|
-
} finally {
|
|
333
|
-
viteStarting = false;
|
|
334
|
-
}
|
|
335
|
-
};
|
|
336
|
-
if (isDev) {
|
|
337
|
-
await ensureViteStarted();
|
|
338
|
-
router.use(async (ctx, next) => {
|
|
339
|
-
if (ctx.request.originalUrl.startsWith("/api")) return next();
|
|
340
|
-
if (webServer.vite && devDeps) {
|
|
341
|
-
return devDeps.connect(webServer.vite.middlewares)(ctx, next);
|
|
342
|
-
}
|
|
343
|
-
return next();
|
|
344
|
-
});
|
|
345
|
-
} else {
|
|
346
|
-
router.use((ctx, next) => {
|
|
347
|
-
if (ctx.request.originalUrl.startsWith("/api")) return next();
|
|
348
|
-
if (!ctx.path.startsWith("/vite/@fs/")) return next();
|
|
349
|
-
const filename = ctx.path.replace(`/vite/@fs/`, "");
|
|
350
|
-
if (!fs2.existsSync(filename)) return next();
|
|
351
|
-
ctx.type = mime.getType(filename) || path2.extname(filename);
|
|
352
|
-
ctx.body = fs2.createReadStream(filename);
|
|
353
|
-
});
|
|
354
|
-
logger2.info("\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557");
|
|
355
|
-
logger2.info("\u2551 Web \u63A7\u5236\u53F0\u5DF2\u542F\u52A8 \u2551");
|
|
356
|
-
logger2.info("\u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563");
|
|
357
|
-
logger2.info("\u2551 \u5730\u5740: http://localhost:8086/ \u2551");
|
|
358
|
-
logger2.info("\u2551 \u6A21\u5F0F: \u751F\u4EA7\u6A21\u5F0F (\u9759\u6001\u6587\u4EF6) \u2551");
|
|
359
|
-
logger2.info("\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D");
|
|
360
|
-
}
|
|
283
|
+
logger2.info(`Web \u63A7\u5236\u53F0\u5DF2\u542F\u52A8 (${"\u751F\u4EA7\u6A21\u5F0F, \u9759\u6001\u6587\u4EF6 + \u6309\u9700\u8F6C\u8BD1"})`);
|
|
361
284
|
router.all("*all", async (ctx, next) => {
|
|
362
|
-
|
|
285
|
+
if (ctx.path.startsWith("/api/") || ctx.path === "/api") {
|
|
286
|
+
return next();
|
|
287
|
+
}
|
|
363
288
|
const name = ctx.path.slice(1);
|
|
364
|
-
const sendFile = (
|
|
289
|
+
const sendFile = async (filename) => {
|
|
365
290
|
try {
|
|
366
|
-
const stat = fs2.statSync(
|
|
291
|
+
const stat = fs2.statSync(filename);
|
|
367
292
|
if (!stat.isFile()) {
|
|
368
293
|
ctx.status = 404;
|
|
369
294
|
return;
|
|
@@ -372,33 +297,63 @@ if (enabled) {
|
|
|
372
297
|
ctx.status = 404;
|
|
373
298
|
return;
|
|
374
299
|
}
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
300
|
+
if (isTransformable(filename)) {
|
|
301
|
+
try {
|
|
302
|
+
const code = await transformFile(filename);
|
|
303
|
+
ctx.type = "application/javascript";
|
|
304
|
+
ctx.body = code;
|
|
305
|
+
return;
|
|
306
|
+
} catch (e) {
|
|
307
|
+
logger2.warn(`\u8F6C\u8BD1\u5931\u8D25: ${filename}`, e.message);
|
|
308
|
+
ctx.status = 500;
|
|
309
|
+
ctx.body = `/* transform error: ${e.message} */`;
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
ctx.type = path2.extname(filename);
|
|
314
|
+
ctx.type = mime.getType(filename) || ctx.type;
|
|
315
|
+
return ctx.body = fs2.createReadStream(filename);
|
|
378
316
|
};
|
|
379
|
-
if (
|
|
380
|
-
|
|
317
|
+
if (ctx.path.startsWith("/vite/@ext/")) {
|
|
318
|
+
const rest = ctx.path.replace("/vite/@ext/", "");
|
|
319
|
+
const slashIdx = rest.indexOf("/");
|
|
320
|
+
if (slashIdx === -1) {
|
|
321
|
+
ctx.status = 404;
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
const token = rest.slice(0, slashIdx);
|
|
325
|
+
const relPath = rest.slice(slashIdx + 1);
|
|
326
|
+
const baseDir = entryBases.get(token);
|
|
327
|
+
if (!baseDir) {
|
|
328
|
+
ctx.status = 404;
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
const fullPath = path2.resolve(baseDir, relPath);
|
|
332
|
+
const safePfx = baseDir.endsWith(path2.sep) ? baseDir : baseDir + path2.sep;
|
|
333
|
+
if (!fullPath.startsWith(safePfx) && fullPath !== baseDir) {
|
|
334
|
+
ctx.status = 403;
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
if (fs2.existsSync(fullPath)) {
|
|
338
|
+
return sendFile(fullPath);
|
|
339
|
+
}
|
|
340
|
+
ctx.status = 404;
|
|
341
|
+
return;
|
|
381
342
|
}
|
|
382
|
-
const
|
|
383
|
-
if (
|
|
343
|
+
const resolved = resolveFile(name);
|
|
344
|
+
if (resolved) {
|
|
384
345
|
try {
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
return sendFile(filename);
|
|
389
|
-
}
|
|
346
|
+
const fileState = fs2.statSync(resolved);
|
|
347
|
+
if (fileState.isFile() && !fileState.isSocket() && !fileState.isFIFO()) {
|
|
348
|
+
return sendFile(resolved);
|
|
390
349
|
}
|
|
391
350
|
} catch (error) {
|
|
392
|
-
logger2.warn(`\u6587\u4EF6\u8BBF\u95EE\u9519\u8BEF: ${
|
|
351
|
+
logger2.warn(`\u6587\u4EF6\u8BBF\u95EE\u9519\u8BEF: ${resolved}`, error.message);
|
|
393
352
|
}
|
|
394
|
-
} else {
|
|
395
|
-
return ctx.status = 403;
|
|
396
353
|
}
|
|
397
|
-
const indexFile =
|
|
398
|
-
if (
|
|
399
|
-
|
|
400
|
-
ctx.type = "html";
|
|
401
|
-
ctx.body = await webServer.vite?.transformIndexHtml(url, template) || template;
|
|
354
|
+
const indexFile = resolveFile("index.html");
|
|
355
|
+
if (indexFile) return sendFile(indexFile);
|
|
356
|
+
ctx.status = 404;
|
|
402
357
|
});
|
|
403
358
|
webServer.ws;
|
|
404
359
|
const dataUpdateInterval = setInterval(() => {
|
|
@@ -407,18 +362,18 @@ if (enabled) {
|
|
|
407
362
|
setupWebSocket(webServer);
|
|
408
363
|
onDispose(() => {
|
|
409
364
|
clearInterval(dataUpdateInterval);
|
|
365
|
+
for (const w of watchers) w.close();
|
|
366
|
+
watchers.length = 0;
|
|
367
|
+
watchedDirs.clear();
|
|
410
368
|
});
|
|
411
369
|
provide({
|
|
412
370
|
name: "web",
|
|
413
371
|
description: "web\u670D\u52A1",
|
|
414
372
|
value: webServer,
|
|
415
373
|
dispose(server) {
|
|
416
|
-
return Promise
|
|
417
|
-
server.
|
|
418
|
-
|
|
419
|
-
server.ws.close(() => resolve2());
|
|
420
|
-
})
|
|
421
|
-
]);
|
|
374
|
+
return new Promise((resolve3) => {
|
|
375
|
+
server.ws.close(() => resolve3());
|
|
376
|
+
});
|
|
422
377
|
}
|
|
423
378
|
});
|
|
424
379
|
});
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* esbuild 按需转译模块
|
|
3
|
+
*
|
|
4
|
+
* 在生产模式下,对请求的 .ts/.tsx/.jsx 文件进行实时转译:
|
|
5
|
+
* 1. 读取源文件
|
|
6
|
+
* 2. esbuild.transform() 转为 ESM JS(React 17+ automatic JSX)
|
|
7
|
+
* 3. 重写相对 import 路径(补全扩展名,移除 CSS import)
|
|
8
|
+
* 4. 以 mtime 为校验的内存缓存
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* 判断文件是否需要转译
|
|
12
|
+
*/
|
|
13
|
+
declare function isTransformable(filePath: string): boolean;
|
|
14
|
+
/**
|
|
15
|
+
* 转译单个 TS/TSX/JSX 文件为 ESM JavaScript
|
|
16
|
+
*
|
|
17
|
+
* @param filePath 文件绝对路径
|
|
18
|
+
* @returns 转译后的 JS 代码
|
|
19
|
+
*/
|
|
20
|
+
declare function transformFile(filePath: string): Promise<string>;
|
|
21
|
+
/**
|
|
22
|
+
* 清空转译缓存
|
|
23
|
+
*/
|
|
24
|
+
declare function clearTransformCache(): void;
|
|
25
|
+
|
|
26
|
+
export { clearTransformCache, isTransformable, transformFile };
|
package/lib/transform.js
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { transform } from 'esbuild';
|
|
2
|
+
import * as fs from 'fs';
|
|
3
|
+
import * as path from 'path';
|
|
4
|
+
|
|
5
|
+
// src/transform.ts
|
|
6
|
+
var cache = /* @__PURE__ */ new Map();
|
|
7
|
+
var TRANSFORMABLE_EXTS = [".ts", ".tsx", ".jsx"];
|
|
8
|
+
var RESOLVE_EXTS = [".tsx", ".ts", ".jsx", ".js"];
|
|
9
|
+
function isTransformable(filePath) {
|
|
10
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
11
|
+
return TRANSFORMABLE_EXTS.includes(ext);
|
|
12
|
+
}
|
|
13
|
+
var IMPORT_RE = /(?:import|export)\s+.*?\s+from\s+['"]([^'"]+)['"]|import\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
|
|
14
|
+
var CSS_IMPORT_RE = /import\s+['"][^'"]+\.css['"]\s*;?\n?/g;
|
|
15
|
+
function isRelative(specifier) {
|
|
16
|
+
return specifier.startsWith("./") || specifier.startsWith("../");
|
|
17
|
+
}
|
|
18
|
+
function hasExtension(specifier) {
|
|
19
|
+
const base = path.basename(specifier);
|
|
20
|
+
return base.includes(".") && !base.startsWith(".");
|
|
21
|
+
}
|
|
22
|
+
function resolveRelativeImport(specifier, fromFile) {
|
|
23
|
+
if (!isRelative(specifier)) return specifier;
|
|
24
|
+
if (hasExtension(specifier)) return specifier;
|
|
25
|
+
const dir = path.dirname(fromFile);
|
|
26
|
+
const target = path.resolve(dir, specifier);
|
|
27
|
+
for (const ext of RESOLVE_EXTS) {
|
|
28
|
+
if (fs.existsSync(target + ext)) {
|
|
29
|
+
return specifier + ext;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
if (fs.existsSync(target) && fs.statSync(target).isDirectory()) {
|
|
33
|
+
for (const ext of RESOLVE_EXTS) {
|
|
34
|
+
if (fs.existsSync(path.join(target, `index${ext}`))) {
|
|
35
|
+
return specifier + `/index${ext}`;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return specifier;
|
|
40
|
+
}
|
|
41
|
+
function rewriteImports(code, fromFile) {
|
|
42
|
+
code = code.replace(CSS_IMPORT_RE, "");
|
|
43
|
+
code = code.replace(IMPORT_RE, (match, fromSpecifier, dynamicSpecifier) => {
|
|
44
|
+
const specifier = fromSpecifier || dynamicSpecifier;
|
|
45
|
+
if (!specifier || !isRelative(specifier)) return match;
|
|
46
|
+
if (hasExtension(specifier)) return match;
|
|
47
|
+
const resolved = resolveRelativeImport(specifier, fromFile);
|
|
48
|
+
if (resolved === specifier) return match;
|
|
49
|
+
return match.replace(specifier, resolved);
|
|
50
|
+
});
|
|
51
|
+
return code;
|
|
52
|
+
}
|
|
53
|
+
async function transformFile(filePath) {
|
|
54
|
+
const stat = fs.statSync(filePath);
|
|
55
|
+
const cached = cache.get(filePath);
|
|
56
|
+
if (cached && cached.mtime === stat.mtimeMs) {
|
|
57
|
+
return cached.code;
|
|
58
|
+
}
|
|
59
|
+
const source = fs.readFileSync(filePath, "utf-8");
|
|
60
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
61
|
+
const loader = ext === ".tsx" ? "tsx" : ext === ".ts" ? "ts" : ext === ".jsx" ? "jsx" : "js";
|
|
62
|
+
const result = await transform(source, {
|
|
63
|
+
loader,
|
|
64
|
+
jsx: "automatic",
|
|
65
|
+
jsxImportSource: "react",
|
|
66
|
+
format: "esm",
|
|
67
|
+
sourcemap: "inline",
|
|
68
|
+
target: "es2022"
|
|
69
|
+
});
|
|
70
|
+
const code = rewriteImports(result.code, filePath);
|
|
71
|
+
cache.set(filePath, { mtime: stat.mtimeMs, code });
|
|
72
|
+
return code;
|
|
73
|
+
}
|
|
74
|
+
function clearTransformCache() {
|
|
75
|
+
cache.clear();
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export { clearTransformCache, isTransformable, transformFile };
|
package/lib/websocket.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zhin.js/console",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.23",
|
|
4
4
|
"description": "Web console service for Zhin.js with real-time monitoring",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./lib/index.js",
|
|
@@ -44,15 +44,16 @@
|
|
|
44
44
|
"./node.tsconfig.json": "./node.tsconfig.json"
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
|
+
"esbuild": ">=0.21.0",
|
|
47
48
|
"mime": "^4.1.0",
|
|
48
49
|
"ws": "^8.18.3"
|
|
49
50
|
},
|
|
50
51
|
"devDependencies": {
|
|
51
|
-
"@radix-ui/themes": "^3.2.1",
|
|
52
52
|
"@reduxjs/toolkit": "^2.9.0",
|
|
53
53
|
"@tailwindcss/postcss": "^4.1.11",
|
|
54
54
|
"@tailwindcss/vite": "4.1.14",
|
|
55
55
|
"@vitejs/plugin-react": "^4.3.4",
|
|
56
|
+
"class-variance-authority": "^0.7.1",
|
|
56
57
|
"clsx": "^2.1.1",
|
|
57
58
|
"koa-connect": "^2.1.0",
|
|
58
59
|
"lucide-react": "^0.469.0",
|
|
@@ -66,14 +67,14 @@
|
|
|
66
67
|
"tailwindcss": "latest",
|
|
67
68
|
"tsup": "^8.5.1",
|
|
68
69
|
"vite": "^7.0.6",
|
|
69
|
-
"zhin.js": "1.0.
|
|
70
|
+
"zhin.js": "1.0.27"
|
|
70
71
|
},
|
|
71
72
|
"peerDependencies": {
|
|
72
73
|
"@types/ws": "^8.18.1",
|
|
73
|
-
"@zhin.js/
|
|
74
|
-
"@zhin.js/
|
|
75
|
-
"@zhin.js/http": "^1.0.
|
|
76
|
-
"zhin.js": "1.0.
|
|
74
|
+
"@zhin.js/core": "^1.0.27",
|
|
75
|
+
"@zhin.js/client": "^1.0.9",
|
|
76
|
+
"@zhin.js/http": "^1.0.18",
|
|
77
|
+
"zhin.js": "1.0.27"
|
|
77
78
|
},
|
|
78
79
|
"files": [
|
|
79
80
|
"lib",
|
|
@@ -95,7 +96,7 @@
|
|
|
95
96
|
"build:prod": "NODE_ENV=production pnpm build",
|
|
96
97
|
"build:dev": "NODE_ENV=development pnpm build",
|
|
97
98
|
"build:client": "node scripts/client.js",
|
|
98
|
-
"clean": "
|
|
99
|
+
"clean": "rimraf lib dist",
|
|
99
100
|
"build:node": "tsup"
|
|
100
101
|
}
|
|
101
102
|
}
|