zhin.js 1.0.16 → 1.0.18

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 CHANGED
@@ -1,5 +1,22 @@
1
1
  # zhin.js
2
2
 
3
+ ## 1.0.18
4
+
5
+ ### Patch Changes
6
+
7
+ - d16a69c: fix: test trust publish
8
+ - Updated dependencies [d16a69c]
9
+ - @zhin.js/logger@0.1.2
10
+ - @zhin.js/schema@1.0.2
11
+ - @zhin.js/core@1.0.18
12
+
13
+ ## 1.0.17
14
+
15
+ ### Patch Changes
16
+
17
+ - Updated dependencies [3bc5d56]
18
+ - @zhin.js/core@1.0.17
19
+
3
20
  ## 1.0.16
4
21
 
5
22
  ### Patch Changes
package/lib/index.d.ts CHANGED
@@ -1,6 +1,21 @@
1
1
  export * from '@zhin.js/core';
2
- import { AppConfig, App } from '@zhin.js/core';
3
2
  export { default as logger } from '@zhin.js/logger';
4
- export declare function createApp(config?: Partial<AppConfig>): Promise<App>;
5
- export declare function createApp(config_file?: string): Promise<App>;
3
+ declare module "zhin.js" {
4
+ namespace Plugin {
5
+ interface Contexts {
6
+ }
7
+ interface Extensions {
8
+ /**
9
+ * 定义数据库模型
10
+ * @param name 模型名称
11
+ * @param definition 模型定义
12
+ */
13
+ defineModel<K extends keyof Models>(name: K, definition: import('@zhin.js/core').Definition<Models[K]>): void;
14
+ }
15
+ }
16
+ interface RegisteredAdapters {
17
+ }
18
+ interface Models {
19
+ }
20
+ }
6
21
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAKA,cAAc,eAAe,CAAA;AAE7B,OAAO,EAAE,SAAS,EAAE,GAAG,EAAU,MAAM,eAAe,CAAA;AAKtD,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,iBAAiB,CAAA;AAEnD,wBAAsB,SAAS,CAAC,MAAM,CAAC,EAAC,OAAO,CAAC,SAAS,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC,CAAA;AACzE,wBAAsB,SAAS,CAAC,WAAW,CAAC,EAAC,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAKA,cAAc,eAAe,CAAA;AAE7B,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,iBAAiB,CAAA;AAOnD,OAAO,QAAQ,SAAS,CAAC;IAEvB,UAAU,MAAM,CAAC;QAEf,UAAU,QAAQ;SAAG;QAErB,UAAU,UAAU;YAClB;;;;eAIG;YACH,WAAW,CAAC,CAAC,SAAS,MAAM,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE,UAAU,EAAE,OAAO,eAAe,EAAE,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;SAC/G;KACF;IAED,UAAU,kBAAkB;KAAG;IAE/B,UAAU,MAAM;KAAG;CACpB"}
package/lib/index.js CHANGED
@@ -3,23 +3,6 @@
3
3
  // ================================================================================================
4
4
  // 导出核心框架
5
5
  export * from '@zhin.js/core';
6
- import { App, Config } from '@zhin.js/core';
7
- import path from 'path';
8
- import fs from 'fs';
9
6
  // 重新导出 logger(作为独立的工具)
10
7
  export { default as logger } from '@zhin.js/logger';
11
- export async function createApp(config_param) {
12
- const envFiles = ['.env', `.env.${process.env.NODE_ENV}`].filter(f => fs.existsSync(path.join(process.cwd(), f)));
13
- if (config_param === undefined) {
14
- config_param = Config.supportedExtensions.map(ext => `zhin.config${ext}`).find(f => fs.existsSync(path.join(process.cwd(), f)));
15
- }
16
- if (config_param === undefined)
17
- throw new Error('No configuration file found and no configuration provided.');
18
- const app = new App(config_param);
19
- app.watching(envFiles, () => {
20
- console.log('Environment file changed, exiting to reload...');
21
- process.exit(51);
22
- });
23
- return app;
24
- }
25
8
  //# sourceMappingURL=index.js.map
package/lib/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,mGAAmG;AACnG,uBAAuB;AACvB,mGAAmG;AAEnG,SAAS;AACT,cAAc,eAAe,CAAA;AAE7B,OAAO,EAAa,GAAG,EAAE,MAAM,EAAE,MAAM,eAAe,CAAA;AACtD,OAAO,IAAI,MAAM,MAAM,CAAA;AACvB,OAAO,EAAE,MAAM,IAAI,CAAA;AAEnB,uBAAuB;AACvB,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,iBAAiB,CAAA;AAInD,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,YAAuC;IACnE,MAAM,QAAQ,GAAC,CAAC,MAAM,EAAC,QAAQ,OAAO,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAA,EAAE,CAAA,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAC,CAAC,CAAC,CAAC,CAAC,CAAA;IAC3G,IAAG,YAAY,KAAG,SAAS,EAAC,CAAC;QACzB,YAAY,GAAC,MAAM,CAAC,mBAAmB,CAAC,GAAG,CAAC,GAAG,CAAA,EAAE,CAAA,cAAc,GAAG,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAA,EAAE,CAAA,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAC,CAAC,CAAC,CAAC,CAAC,CAAA;IAC5H,CAAC;IACD,IAAG,YAAY,KAAG,SAAS;QAAE,MAAM,IAAI,KAAK,CAAC,4DAA4D,CAAC,CAAA;IAC1G,MAAM,GAAG,GAAE,IAAI,GAAG,CAAC,YAAsB,CAAC,CAAC;IAC3C,GAAG,CAAC,QAAQ,CAAC,QAAQ,EAAC,GAAE,EAAE;QACtB,OAAO,CAAC,GAAG,CAAC,gDAAgD,CAAC,CAAA;QAC7D,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;IACpB,CAAC,CAAC,CAAA;IACF,OAAO,GAAG,CAAA;AACd,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,mGAAmG;AACnG,uBAAuB;AACvB,mGAAmG;AAEnG,SAAS;AACT,cAAc,eAAe,CAAA;AAC7B,uBAAuB;AACvB,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,iBAAiB,CAAA"}
@@ -0,0 +1,37 @@
1
+ import { LogTransport } from '@zhin.js/logger';
2
+ import { Plugin } from '@zhin.js/core';
3
+ /**
4
+ * 数据库日志传输器
5
+ * 将日志存储到数据库,并自动清理旧日志
6
+ */
7
+ export declare class DatabaseLogTransport implements LogTransport {
8
+ private plugin;
9
+ private stripAnsiRegex;
10
+ private cleanupTimer?;
11
+ private maxDays;
12
+ private maxRecords;
13
+ private cleanupInterval;
14
+ constructor(plugin: Plugin);
15
+ /**
16
+ * 启动定时清理任务
17
+ */
18
+ private startCleanup;
19
+ /**
20
+ * 清理旧日志
21
+ */
22
+ private cleanupOldLogs;
23
+ /**
24
+ * 停止清理任务
25
+ */
26
+ stopCleanup(): void;
27
+ /**
28
+ * 移除 ANSI 颜色代码
29
+ */
30
+ private stripAnsi;
31
+ write(message: string): void;
32
+ /**
33
+ * 保存日志到数据库
34
+ */
35
+ private saveToDatabase;
36
+ }
37
+ //# sourceMappingURL=log-transport.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"log-transport.d.ts","sourceRoot":"","sources":["../src/log-transport.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAC9C,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAA;AAGtC;;;GAGG;AACH,qBAAa,oBAAqB,YAAW,YAAY;IACvD,OAAO,CAAC,MAAM,CAAQ;IACtB,OAAO,CAAC,cAAc,CAAoB;IAC1C,OAAO,CAAC,YAAY,CAAC,CAAgB;IACrC,OAAO,CAAC,OAAO,CAAQ;IACvB,OAAO,CAAC,UAAU,CAAQ;IAC1B,OAAO,CAAC,eAAe,CAAQ;gBAEnB,MAAM,EAAE,MAAM;IAiB1B;;OAEG;IACH,OAAO,CAAC,YAAY;IAcpB;;OAEG;YACW,cAAc;IAgD5B;;OAEG;IACI,WAAW,IAAI,IAAI;IAO1B;;OAEG;IACH,OAAO,CAAC,SAAS;IAIjB,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAqB5B;;OAEG;YACW,cAAc;CAuB7B"}
@@ -0,0 +1,140 @@
1
+ /**
2
+ * 数据库日志传输器
3
+ * 将日志存储到数据库,并自动清理旧日志
4
+ */
5
+ export class DatabaseLogTransport {
6
+ plugin;
7
+ stripAnsiRegex = /\x1b\[[0-9;]*m/g;
8
+ cleanupTimer;
9
+ maxDays;
10
+ maxRecords;
11
+ cleanupInterval;
12
+ constructor(plugin) {
13
+ this.plugin = plugin;
14
+ const configService = plugin.inject('config');
15
+ const appConfig = configService.get('zhin.config.yml');
16
+ // 从配置读取日志清理策略
17
+ const logConfig = appConfig.log || {};
18
+ this.maxDays = logConfig.maxDays || 7; // 默认保留 7 天
19
+ this.maxRecords = logConfig.maxRecords || 10000; // 默认最多 10000 条
20
+ this.cleanupInterval = logConfig.cleanupInterval || 24; // 默认 24 小时清理一次
21
+ // 启动定时清理
22
+ this.startCleanup();
23
+ // 插件销毁时停止清理任务
24
+ plugin.onDispose(() => this.stopCleanup());
25
+ }
26
+ /**
27
+ * 启动定时清理任务
28
+ */
29
+ startCleanup() {
30
+ // 立即执行一次清理
31
+ this.cleanupOldLogs().catch(err => {
32
+ this.plugin.logger.error('[DatabaseLogTransport] Initial cleanup failed:', err.message);
33
+ });
34
+ // 设置定时任务
35
+ this.cleanupTimer = setInterval(() => {
36
+ this.cleanupOldLogs().catch(err => {
37
+ this.plugin.logger.error('[DatabaseLogTransport] Scheduled cleanup failed:', err.message);
38
+ });
39
+ }, this.cleanupInterval * 60 * 60 * 1000); // 转换为毫秒
40
+ }
41
+ /**
42
+ * 清理旧日志
43
+ */
44
+ async cleanupOldLogs() {
45
+ const db = this.plugin.inject('database');
46
+ if (!db) {
47
+ return;
48
+ }
49
+ try {
50
+ const LogModel = db.models.get('SystemLog');
51
+ if (!LogModel) {
52
+ return;
53
+ }
54
+ // 1. 按时间清理:删除超过 maxDays 天的日志
55
+ const cutoffDate = new Date();
56
+ cutoffDate.setDate(cutoffDate.getDate() - this.maxDays);
57
+ const deletedData = await LogModel
58
+ .delete({ timestamp: { $lt: cutoffDate } });
59
+ const deletedCount = typeof deletedData === 'number' ? deletedData : deletedData.length;
60
+ // 2. 按数量清理:如果日志总数超过 maxRecords,删除最旧的
61
+ const total = await LogModel.select();
62
+ const totalCount = total.length;
63
+ if (totalCount > this.maxRecords) {
64
+ const excessCount = totalCount - this.maxRecords;
65
+ // 查找最旧的 excessCount 条日志的 ID
66
+ const oldestLogs = await LogModel.select('id', 'timestamp').orderBy('timestamp', 'ASC').limit(excessCount);
67
+ const idsToDelete = oldestLogs.map((log) => log.id);
68
+ if (idsToDelete.length > 0) {
69
+ await LogModel
70
+ .delete({ id: { $in: idsToDelete } });
71
+ }
72
+ }
73
+ this.plugin.logger.info(`[DatabaseLogTransport] Log cleanup completed. ` +
74
+ `Deleted ${deletedData || 0} logs older than ${this.maxDays} days. ` +
75
+ `Current total: ${Math.max(0, totalCount - (deletedCount || 0))} logs.`);
76
+ }
77
+ catch (error) {
78
+ // 静默处理错误
79
+ this.plugin.logger.debug('[DatabaseLogTransport] Cleanup error:', error.message, error.stack);
80
+ }
81
+ }
82
+ /**
83
+ * 停止清理任务
84
+ */
85
+ stopCleanup() {
86
+ if (this.cleanupTimer) {
87
+ clearInterval(this.cleanupTimer);
88
+ this.cleanupTimer = undefined;
89
+ }
90
+ }
91
+ /**
92
+ * 移除 ANSI 颜色代码
93
+ */
94
+ stripAnsi(str) {
95
+ return str.replace(this.stripAnsiRegex, '');
96
+ }
97
+ write(message) {
98
+ // 移除 ANSI 颜色代码
99
+ const cleanMessage = this.stripAnsi(message);
100
+ // 解析日志消息
101
+ // 格式: [09-08 04:07:55.852] [INFO] [MyApp]: message
102
+ const logRegex = /\[[\d-]+ [\d:.]+\] \[(\w+)\] \[([^\]]+)\]: ([\s\S]+)/;
103
+ const match = cleanMessage.match(logRegex);
104
+ if (match) {
105
+ const [, level, name, msg] = match;
106
+ const source = name.split(':')[0]; // 取第一部分作为 source
107
+ // 异步存储到数据库,不阻塞日志输出
108
+ this.saveToDatabase(level.toLowerCase(), name, msg.trim(), source).catch(err => {
109
+ // 避免日志存储失败导致应用崩溃
110
+ console.error('[DatabaseLogTransport] Failed to save log:', err.message);
111
+ });
112
+ }
113
+ }
114
+ /**
115
+ * 保存日志到数据库
116
+ */
117
+ async saveToDatabase(level, name, message, source) {
118
+ const databaseService = this.plugin.inject('database');
119
+ if (!databaseService) {
120
+ return; // 没有数据库则跳过
121
+ }
122
+ try {
123
+ const LogModel = databaseService.models.get('SystemLog');
124
+ if (!LogModel) {
125
+ return; // 模型不存在则跳过
126
+ }
127
+ await LogModel.insert({
128
+ level,
129
+ name,
130
+ message,
131
+ source,
132
+ timestamp: new Date()
133
+ });
134
+ }
135
+ catch (error) {
136
+ // 静默处理错误,避免干扰主流程
137
+ }
138
+ }
139
+ }
140
+ //# sourceMappingURL=log-transport.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"log-transport.js","sourceRoot":"","sources":["../src/log-transport.ts"],"names":[],"mappings":"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"}
package/lib/setup.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=setup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../src/setup.ts"],"names":[],"mappings":""}
package/lib/setup.js ADDED
@@ -0,0 +1,116 @@
1
+ import { setLevel, LogLevel } from '@zhin.js/logger';
2
+ import { usePlugin, resolveEntry, ConfigService, PermissionService, createCommandService, createComponentService, createCronService, defineDatabaseService, ProcessAdapter, } from '@zhin.js/core';
3
+ import { DatabaseLogTransport } from './log-transport.js';
4
+ import * as path from 'path';
5
+ const plugin = usePlugin();
6
+ const { provide, start, stop, logger, children, import: importPlugin } = plugin;
7
+ // 1. 先加载配置,用户在 zhin.config 中决定启用哪些服务
8
+ const configService = new ConfigService();
9
+ await configService.load('zhin.config.yml', {
10
+ log_level: LogLevel.INFO,
11
+ database: {
12
+ dialect: "sqlite",
13
+ filename: "./data/test.db"
14
+ },
15
+ plugin_dirs: [path.relative(process.cwd(), import.meta.dirname), 'node_modules/@zhin.js', './plugins',],
16
+ plugins: [],
17
+ services: ['process', 'config', 'command', 'component', 'permission', 'cron'],
18
+ });
19
+ const appConfig = configService.get('zhin.config.yml');
20
+ const enabledServices = new Set(appConfig.services || ['process', 'config', 'command', 'component', 'permission', 'cron']);
21
+ // 注册配置服务(必须)
22
+ provide({
23
+ name: 'config',
24
+ description: '配置服务',
25
+ value: configService,
26
+ });
27
+ // 2. 可选服务按配置注册
28
+ if (enabledServices.has('process')) {
29
+ provide({
30
+ name: 'process',
31
+ description: '命令行适配器',
32
+ mounted: async (plugin) => {
33
+ const adapter = new ProcessAdapter(plugin);
34
+ await adapter.start();
35
+ return adapter;
36
+ },
37
+ dispose: async (adapter) => {
38
+ await adapter.stop();
39
+ }
40
+ });
41
+ }
42
+ if (enabledServices.has('command')) {
43
+ provide(createCommandService());
44
+ }
45
+ if (enabledServices.has('component')) {
46
+ provide(createComponentService());
47
+ }
48
+ if (enabledServices.has('permission')) {
49
+ provide({
50
+ name: 'permission',
51
+ description: '权限管理',
52
+ value: new PermissionService(),
53
+ });
54
+ }
55
+ if (enabledServices.has('cron')) {
56
+ provide(createCronService());
57
+ }
58
+ // 3. 配置生效:日志、数据库、插件加载
59
+ setLevel(appConfig.log_level || LogLevel.INFO);
60
+ if (appConfig.database) {
61
+ provide(defineDatabaseService(appConfig.database));
62
+ const logTransport = new DatabaseLogTransport(plugin);
63
+ // 直接访问 logger 实例的 transports 数组,或使用非递归方式
64
+ // 检查是否已存在 DatabaseLogTransport(避免重复添加)
65
+ if (!logger['transports'].some((t) => t instanceof DatabaseLogTransport)) {
66
+ logger['transports'].push(logTransport);
67
+ }
68
+ }
69
+ // 4. 启动核心上下文(确保扩展方法可用,比如 addCommand)
70
+ await start();
71
+ // 5. 加载插件(父插件已启动,会自动 start 子插件)
72
+ // 先去重插件列表,避免重复加载
73
+ const pluginNames = new Set(appConfig.plugins || []);
74
+ logger.debug(`Plugin list: ${Array.from(pluginNames).join(', ')}`);
75
+ for (const pluginName of pluginNames) {
76
+ const dir = appConfig.plugin_dirs?.find((dir) => resolveEntry(path.join(dir, pluginName)));
77
+ if (dir) {
78
+ const pluginPath = path.join(process.cwd(), dir, pluginName);
79
+ logger.debug(`Importing plugin: ${pluginName} from ${pluginPath}`);
80
+ await importPlugin(pluginPath);
81
+ }
82
+ }
83
+ logger.debug(`${children.length} plugins loaded`);
84
+ // 6. 优雅关闭(使用 once 防止重复注册)
85
+ const handleSIGTERM = () => {
86
+ logger.info('Received SIGTERM, shutting down gracefully...');
87
+ stop();
88
+ process.exit(0);
89
+ };
90
+ const handleSIGINT = () => {
91
+ logger.info('Received SIGINT, shutting down gracefully...');
92
+ stop();
93
+ process.exit(0);
94
+ };
95
+ // 7. 异常处理
96
+ const handleUncaughtException = (error) => {
97
+ logger.error('Uncaught exception:', error);
98
+ stop();
99
+ process.exit(1);
100
+ };
101
+ const handleUnhandledRejection = (reason) => {
102
+ logger.error('Unhandled rejection:', reason);
103
+ stop();
104
+ process.exit(1);
105
+ };
106
+ // 移除可能存在的旧监听器(防止热重载时累积)
107
+ process.removeListener('SIGTERM', handleSIGTERM);
108
+ process.removeListener('SIGINT', handleSIGINT);
109
+ process.removeListener('uncaughtException', handleUncaughtException);
110
+ process.removeListener('unhandledRejection', handleUnhandledRejection);
111
+ // 注册新监听器
112
+ process.once('SIGTERM', handleSIGTERM);
113
+ process.once('SIGINT', handleSIGINT);
114
+ process.on('uncaughtException', handleUncaughtException);
115
+ process.on('unhandledRejection', handleUnhandledRejection);
116
+ //# sourceMappingURL=setup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setup.js","sourceRoot":"","sources":["../src/setup.ts"],"names":[],"mappings":"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"}
package/lib/types.d.ts ADDED
@@ -0,0 +1,33 @@
1
+ import { Bot, DatabaseConfig, Databases } from "@zhin.js/core";
2
+ import { LogLevel } from "@zhin.js/logger";
3
+ /**
4
+ * App配置类型,涵盖机器人、数据库、插件、调试等
5
+ */
6
+ export interface AppConfig<T extends keyof Databases = keyof Databases> {
7
+ bots?: Bot.Config[];
8
+ log_level: LogLevel;
9
+ /** 数据库配置列表 */
10
+ database?: DatabaseConfig<T>;
11
+ /** 插件目录列表,默认为 ['./plugins', 'node_modules'] */
12
+ plugin_dirs?: string[];
13
+ /** 需要加载的插件列表 */
14
+ plugins?: string[];
15
+ /** 启用的内置服务列表,例如 ['process','config','command','component','permission','cron'] */
16
+ services?: ('process' | 'config' | 'command' | 'component' | 'permission' | 'cron')[];
17
+ /** 禁用的依赖列表 */
18
+ disable_dependencies?: string[];
19
+ /** 是否启用调试模式 */
20
+ debug?: boolean;
21
+ /** 日志配置 */
22
+ log?: {
23
+ /** 最大日志保留天数,默认 7 天 */
24
+ maxDays?: number;
25
+ /** 最大日志条数,默认 10000 条 */
26
+ maxRecords?: number;
27
+ /** 自动清理间隔(小时),默认 24 小时 */
28
+ cleanupInterval?: number;
29
+ };
30
+ /** 插件配置(键为插件名,值为配置对象) */
31
+ [key: string]: any;
32
+ }
33
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC/D,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAE3C;;GAEG;AACH,MAAM,WAAW,SAAS,CAAC,CAAC,SAAS,MAAM,SAAS,GAAG,MAAM,SAAS;IACpE,IAAI,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC;IACpB,SAAS,EAAE,QAAQ,CAAC;IACpB,cAAc;IACd,QAAQ,CAAC,EAAE,cAAc,CAAC,CAAC,CAAC,CAAC;IAC7B,+CAA+C;IAC/C,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,gBAAgB;IAChB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,kFAAkF;IAClF,QAAQ,CAAC,EAAE,CAAC,SAAS,GAAG,QAAQ,GAAG,SAAS,GAAG,WAAW,GAAG,YAAY,GAAG,MAAM,CAAC,EAAE,CAAC;IACtF,cAAc;IACd,oBAAoB,CAAC,EAAE,MAAM,EAAE,CAAC;IAChC,eAAe;IACf,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,WAAW;IACX,GAAG,CAAC,EAAE;QACJ,sBAAsB;QACtB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,wBAAwB;QACxB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,0BAA0B;QAC1B,eAAe,CAAC,EAAE,MAAM,CAAC;KAC1B,CAAC;IACF,yBAAyB;IACzB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACpB"}
package/lib/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zhin.js",
3
- "version": "1.0.16",
3
+ "version": "1.0.18",
4
4
  "description": "开箱即用的机器人框架 - 包含进程适配器、HTTP服务、Web控制台和SQLite数据库",
5
5
  "type": "module",
6
6
  "main": "./lib/index.js",
@@ -8,27 +8,45 @@
8
8
  "exports": {
9
9
  ".": {
10
10
  "types": "./lib/index.d.ts",
11
+ "development": "./src/index.ts",
11
12
  "import": "./lib/index.js"
12
13
  },
13
14
  "./jsx": {
14
15
  "types": "./lib/jsx.d.ts",
16
+ "development": "./src/jsx.ts",
15
17
  "import": "./lib/jsx.js"
16
18
  },
17
19
  "./jsx-runtime": {
18
20
  "types": "./lib/jsx-runtime.d.ts",
21
+ "development": "./src/jsx-runtime.ts",
19
22
  "import": "./lib/jsx-runtime.js"
20
23
  },
24
+ "./setup": {
25
+ "types": "./lib/setup.d.ts",
26
+ "development": "./src/setup.ts",
27
+ "import": "./lib/setup.js"
28
+ },
21
29
  "./jsx-dev-runtime": {
22
30
  "types": "./lib/jsx-dev-runtime.d.ts",
31
+ "development": "./src/jsx-dev-runtime.ts",
23
32
  "import": "./lib/jsx-dev-runtime.js"
24
33
  }
25
34
  },
26
35
  "dependencies": {
27
- "@zhin.js/core": "1.0.16",
28
- "@zhin.js/logger": "0.1.1"
36
+ "yaml": "^2.3.4",
37
+ "dotenv": "^16.3.1",
38
+ "@zhin.js/core": "1.0.18",
39
+ "@zhin.js/schema": "1.0.2",
40
+ "@zhin.js/logger": "0.1.2"
29
41
  },
30
42
  "devDependencies": {
31
- "typescript": "^5.3.0"
43
+ "typescript": "^5.3.0",
44
+ "@types/node": "^24.3.0"
45
+ },
46
+ "repository": {
47
+ "type": "git",
48
+ "url": "git+https://github.com/zhinjs/zhin.git",
49
+ "directory": "packages/zhin"
32
50
  },
33
51
  "scripts": {
34
52
  "build": "tsc",
package/src/index.ts CHANGED
@@ -4,26 +4,31 @@
4
4
 
5
5
  // 导出核心框架
6
6
  export * from '@zhin.js/core'
7
- import logger from '@zhin.js/logger'
8
- import { AppConfig, App, Config } from '@zhin.js/core'
9
- import path from 'path'
10
- import fs from 'fs'
11
-
12
7
  // 重新导出 logger(作为独立的工具)
13
8
  export { default as logger } from '@zhin.js/logger'
14
9
 
15
- export async function createApp(config?:Partial<AppConfig>): Promise<App>
16
- export async function createApp(config_file?:string): Promise<App>
17
- export async function createApp(config_param?:string|Partial<AppConfig>): Promise<App> {
18
- const envFiles=['.env',`.env.${process.env.NODE_ENV}`].filter(f=>fs.existsSync(path.join(process.cwd(),f)))
19
- if(config_param===undefined){
20
- config_param=Config.supportedExtensions.map(ext=>`zhin.config${ext}`).find(f=>fs.existsSync(path.join(process.cwd(),f)))
10
+ // ================================================================================================
11
+ // 模块声明 - 允许插件通过 declare module "zhin.js" 扩展类型
12
+ // ================================================================================================
13
+ // 重新声明可扩展接口,使得 declare module "zhin.js" 可以正确合并类型
14
+ // 这些接口会与 @zhin.js/core 中的原始定义合并
15
+ declare module "zhin.js" {
16
+ // 重新导出 Plugin 命名空间中的可扩展接口
17
+ namespace Plugin {
18
+ // 可扩展的 Context 注册表
19
+ interface Contexts {}
20
+ // 扩展方法
21
+ interface Extensions {
22
+ /**
23
+ * 定义数据库模型
24
+ * @param name 模型名称
25
+ * @param definition 模型定义
26
+ */
27
+ defineModel<K extends keyof Models>(name: K, definition: import('@zhin.js/core').Definition<Models[K]>): void;
21
28
  }
22
- if(config_param===undefined) throw new Error('No configuration file found and no configuration provided.')
23
- const app= new App(config_param as string);
24
- app.watching(envFiles,()=>{
25
- console.log('Environment file changed, exiting to reload...')
26
- process.exit(51)
27
- })
28
- return app
29
- }
29
+ }
30
+ // 可扩展的适配器注册表
31
+ interface RegisteredAdapters {}
32
+ // 可扩展的数据模型注册表
33
+ interface Models {}
34
+ }
@@ -0,0 +1,167 @@
1
+ import { LogTransport } from '@zhin.js/logger'
2
+ import { Plugin } from '@zhin.js/core'
3
+ import { AppConfig } from './types.js'
4
+
5
+ /**
6
+ * 数据库日志传输器
7
+ * 将日志存储到数据库,并自动清理旧日志
8
+ */
9
+ export class DatabaseLogTransport implements LogTransport {
10
+ private plugin: Plugin
11
+ private stripAnsiRegex = /\x1b\[[0-9;]*m/g
12
+ private cleanupTimer?: NodeJS.Timeout
13
+ private maxDays: number
14
+ private maxRecords: number
15
+ private cleanupInterval: number
16
+
17
+ constructor(plugin: Plugin) {
18
+ this.plugin = plugin
19
+ const configService = plugin.inject('config')!
20
+ const appConfig = configService.get<AppConfig>('zhin.config.yml')
21
+ // 从配置读取日志清理策略
22
+ const logConfig = appConfig.log || {}
23
+ this.maxDays = logConfig.maxDays || 7 // 默认保留 7 天
24
+ this.maxRecords = logConfig.maxRecords || 10000 // 默认最多 10000 条
25
+ this.cleanupInterval = logConfig.cleanupInterval || 24 // 默认 24 小时清理一次
26
+
27
+ // 启动定时清理
28
+ this.startCleanup()
29
+
30
+ // 插件销毁时停止清理任务
31
+ plugin.onDispose(() => this.stopCleanup())
32
+ }
33
+
34
+ /**
35
+ * 启动定时清理任务
36
+ */
37
+ private startCleanup(): void {
38
+ // 立即执行一次清理
39
+ this.cleanupOldLogs().catch(err => {
40
+ this.plugin.logger.error('[DatabaseLogTransport] Initial cleanup failed:', err.message)
41
+ })
42
+
43
+ // 设置定时任务
44
+ this.cleanupTimer = setInterval(() => {
45
+ this.cleanupOldLogs().catch(err => {
46
+ this.plugin.logger.error('[DatabaseLogTransport] Scheduled cleanup failed:', err.message)
47
+ })
48
+ }, this.cleanupInterval * 60 * 60 * 1000) // 转换为毫秒
49
+ }
50
+
51
+ /**
52
+ * 清理旧日志
53
+ */
54
+ private async cleanupOldLogs(): Promise<void> {
55
+ const db=this.plugin.inject('database')
56
+ if (!db) {
57
+ return
58
+ }
59
+
60
+ try {
61
+ const LogModel = db.models.get('SystemLog')
62
+ if (!LogModel) {
63
+ return
64
+ }
65
+
66
+ // 1. 按时间清理:删除超过 maxDays 天的日志
67
+ const cutoffDate = new Date()
68
+ cutoffDate.setDate(cutoffDate.getDate() - this.maxDays)
69
+
70
+ const deletedData = await LogModel
71
+ .delete({ timestamp: { $lt: cutoffDate } })
72
+ const deletedCount = typeof deletedData === 'number' ? deletedData : deletedData.length
73
+ // 2. 按数量清理:如果日志总数超过 maxRecords,删除最旧的
74
+ const total = await LogModel.select()
75
+ const totalCount = total.length
76
+
77
+ if (totalCount > this.maxRecords) {
78
+ const excessCount = totalCount - this.maxRecords
79
+
80
+ // 查找最旧的 excessCount 条日志的 ID
81
+ const oldestLogs: any[] = await LogModel.select('id','timestamp').orderBy('timestamp', 'ASC').limit(excessCount)
82
+
83
+ const idsToDelete = oldestLogs.map((log: any) => log.id)
84
+
85
+ if (idsToDelete.length > 0) {
86
+ await LogModel
87
+ .delete({ id: { $in: idsToDelete } })
88
+ }
89
+ }
90
+
91
+ this.plugin.logger.info(
92
+ `[DatabaseLogTransport] Log cleanup completed. ` +
93
+ `Deleted ${deletedData || 0} logs older than ${this.maxDays} days. ` +
94
+ `Current total: ${Math.max(0, totalCount - (deletedCount || 0))} logs.`
95
+ )
96
+ } catch (error) {
97
+ // 静默处理错误
98
+ this.plugin.logger.debug('[DatabaseLogTransport] Cleanup error:', (error as Error).message,(error as Error).stack)
99
+ }
100
+ }
101
+
102
+ /**
103
+ * 停止清理任务
104
+ */
105
+ public stopCleanup(): void {
106
+ if (this.cleanupTimer) {
107
+ clearInterval(this.cleanupTimer)
108
+ this.cleanupTimer = undefined
109
+ }
110
+ }
111
+
112
+ /**
113
+ * 移除 ANSI 颜色代码
114
+ */
115
+ private stripAnsi(str: string): string {
116
+ return str.replace(this.stripAnsiRegex, '')
117
+ }
118
+
119
+ write(message: string): void {
120
+ // 移除 ANSI 颜色代码
121
+ const cleanMessage = this.stripAnsi(message)
122
+
123
+ // 解析日志消息
124
+ // 格式: [09-08 04:07:55.852] [INFO] [MyApp]: message
125
+ const logRegex = /\[[\d-]+ [\d:.]+\] \[(\w+)\] \[([^\]]+)\]: ([\s\S]+)/
126
+ const match = cleanMessage.match(logRegex)
127
+
128
+ if (match) {
129
+ const [, level, name, msg] = match
130
+ const source = name.split(':')[0] // 取第一部分作为 source
131
+
132
+ // 异步存储到数据库,不阻塞日志输出
133
+ this.saveToDatabase(level.toLowerCase(), name, msg.trim(), source).catch(err => {
134
+ // 避免日志存储失败导致应用崩溃
135
+ console.error('[DatabaseLogTransport] Failed to save log:', err.message)
136
+ })
137
+ }
138
+ }
139
+
140
+ /**
141
+ * 保存日志到数据库
142
+ */
143
+ private async saveToDatabase(level: string, name: string, message: string, source: string): Promise<void> {
144
+ const databaseService=this.plugin.inject('database')
145
+ if (!databaseService) {
146
+ return // 没有数据库则跳过
147
+ }
148
+
149
+ try {
150
+ const LogModel = databaseService.models.get('SystemLog')
151
+ if (!LogModel) {
152
+ return // 模型不存在则跳过
153
+ }
154
+
155
+ await LogModel.insert({
156
+ level,
157
+ name,
158
+ message,
159
+ source,
160
+ timestamp: new Date()
161
+ })
162
+ } catch (error) {
163
+ // 静默处理错误,避免干扰主流程
164
+ }
165
+ }
166
+ }
167
+
package/src/setup.ts ADDED
@@ -0,0 +1,145 @@
1
+
2
+ import { setLevel, LogLevel } from '@zhin.js/logger';
3
+ import {
4
+ usePlugin, resolveEntry,
5
+ ConfigService, PermissionService,
6
+ createCommandService, createComponentService, createCronService,
7
+ defineDatabaseService,
8
+ ProcessAdapter,
9
+ } from '@zhin.js/core';
10
+ import { AppConfig } from './types.js';
11
+ import { DatabaseLogTransport } from './log-transport.js';
12
+ import * as path from 'path';
13
+
14
+ const plugin = usePlugin();
15
+ const {
16
+ provide,
17
+ start, stop,
18
+ logger, children,
19
+ import: importPlugin } = plugin;
20
+
21
+ // 1. 先加载配置,用户在 zhin.config 中决定启用哪些服务
22
+ const configService = new ConfigService();
23
+ await configService.load('zhin.config.yml', {
24
+ log_level: LogLevel.INFO,
25
+ database: {
26
+ dialect: "sqlite",
27
+ filename: "./data/test.db"
28
+ },
29
+ plugin_dirs: [path.relative(process.cwd(), import.meta.dirname), 'node_modules/@zhin.js', './plugins',],
30
+ plugins: [],
31
+ services: ['process', 'config', 'command', 'component', 'permission', 'cron'],
32
+ });
33
+ const appConfig = configService.get<AppConfig>('zhin.config.yml');
34
+ const enabledServices = new Set(appConfig.services || ['process', 'config', 'command', 'component', 'permission', 'cron']);
35
+
36
+ // 注册配置服务(必须)
37
+ provide({
38
+ name: 'config',
39
+ description: '配置服务',
40
+ value: configService,
41
+ });
42
+
43
+ // 2. 可选服务按配置注册
44
+ if (enabledServices.has('process')) {
45
+ provide({
46
+ name: 'process',
47
+ description: '命令行适配器',
48
+ mounted: async (plugin) => {
49
+ const adapter = new ProcessAdapter(plugin);
50
+ await adapter.start();
51
+ return adapter;
52
+ },
53
+ dispose: async (adapter) => {
54
+ await adapter.stop();
55
+ }
56
+ });
57
+ }
58
+
59
+ if (enabledServices.has('command')) {
60
+ provide(createCommandService());
61
+ }
62
+
63
+ if (enabledServices.has('component')) {
64
+ provide(createComponentService());
65
+ }
66
+
67
+ if (enabledServices.has('permission')) {
68
+ provide({
69
+ name: 'permission',
70
+ description: '权限管理',
71
+ value: new PermissionService(),
72
+ });
73
+ }
74
+
75
+ if (enabledServices.has('cron')) {
76
+ provide(createCronService());
77
+ }
78
+
79
+ // 3. 配置生效:日志、数据库、插件加载
80
+ setLevel(appConfig.log_level || LogLevel.INFO);
81
+
82
+ if (appConfig.database) {
83
+ provide(defineDatabaseService(appConfig.database));
84
+ const logTransport = new DatabaseLogTransport(plugin);
85
+ // 直接访问 logger 实例的 transports 数组,或使用非递归方式
86
+ // 检查是否已存在 DatabaseLogTransport(避免重复添加)
87
+ if (!logger['transports'].some((t: any) => t instanceof DatabaseLogTransport)) {
88
+ logger['transports'].push(logTransport);
89
+ }
90
+ }
91
+
92
+ // 4. 启动核心上下文(确保扩展方法可用,比如 addCommand)
93
+ await start();
94
+
95
+ // 5. 加载插件(父插件已启动,会自动 start 子插件)
96
+ // 先去重插件列表,避免重复加载
97
+ const pluginNames = new Set(appConfig.plugins || []);
98
+ logger.debug(`Plugin list: ${Array.from(pluginNames).join(', ')}`);
99
+ for (const pluginName of pluginNames) {
100
+ const dir = appConfig.plugin_dirs?.find((dir: string) => resolveEntry(path.join(dir, pluginName)));
101
+ if (dir) {
102
+ const pluginPath = path.join(process.cwd(), dir, pluginName);
103
+ logger.debug(`Importing plugin: ${pluginName} from ${pluginPath}`);
104
+ await importPlugin(pluginPath);
105
+ }
106
+ }
107
+ logger.debug(`${children.length} plugins loaded`);
108
+
109
+ // 6. 优雅关闭(使用 once 防止重复注册)
110
+ const handleSIGTERM = () => {
111
+ logger.info('Received SIGTERM, shutting down gracefully...');
112
+ stop();
113
+ process.exit(0);
114
+ };
115
+
116
+ const handleSIGINT = () => {
117
+ logger.info('Received SIGINT, shutting down gracefully...');
118
+ stop();
119
+ process.exit(0);
120
+ };
121
+
122
+ // 7. 异常处理
123
+ const handleUncaughtException = (error: Error) => {
124
+ logger.error('Uncaught exception:', error);
125
+ stop();
126
+ process.exit(1);
127
+ };
128
+
129
+ const handleUnhandledRejection = (reason: unknown) => {
130
+ logger.error('Unhandled rejection:', reason);
131
+ stop();
132
+ process.exit(1);
133
+ };
134
+
135
+ // 移除可能存在的旧监听器(防止热重载时累积)
136
+ process.removeListener('SIGTERM', handleSIGTERM);
137
+ process.removeListener('SIGINT', handleSIGINT);
138
+ process.removeListener('uncaughtException', handleUncaughtException);
139
+ process.removeListener('unhandledRejection', handleUnhandledRejection);
140
+
141
+ // 注册新监听器
142
+ process.once('SIGTERM', handleSIGTERM);
143
+ process.once('SIGINT', handleSIGINT);
144
+ process.on('uncaughtException', handleUncaughtException);
145
+ process.on('unhandledRejection', handleUnhandledRejection);
package/src/types.ts ADDED
@@ -0,0 +1,34 @@
1
+ import { Bot, DatabaseConfig, Databases } from "@zhin.js/core";
2
+ import { LogLevel } from "@zhin.js/logger";
3
+
4
+ /**
5
+ * App配置类型,涵盖机器人、数据库、插件、调试等
6
+ */
7
+ export interface AppConfig<T extends keyof Databases = keyof Databases> {
8
+ bots?: Bot.Config[];
9
+ log_level: LogLevel;
10
+ /** 数据库配置列表 */
11
+ database?: DatabaseConfig<T>;
12
+ /** 插件目录列表,默认为 ['./plugins', 'node_modules'] */
13
+ plugin_dirs?: string[];
14
+ /** 需要加载的插件列表 */
15
+ plugins?: string[];
16
+ /** 启用的内置服务列表,例如 ['process','config','command','component','permission','cron'] */
17
+ services?: ('process' | 'config' | 'command' | 'component' | 'permission' | 'cron')[];
18
+ /** 禁用的依赖列表 */
19
+ disable_dependencies?: string[];
20
+ /** 是否启用调试模式 */
21
+ debug?: boolean;
22
+ /** 日志配置 */
23
+ log?: {
24
+ /** 最大日志保留天数,默认 7 天 */
25
+ maxDays?: number;
26
+ /** 最大日志条数,默认 10000 条 */
27
+ maxRecords?: number;
28
+ /** 自动清理间隔(小时),默认 24 小时 */
29
+ cleanupInterval?: number;
30
+ };
31
+ /** 插件配置(键为插件名,值为配置对象) */
32
+ [key: string]: any;
33
+ }
34
+