@zhin.js/core 1.0.0 → 1.0.2

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 (133) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/LICENSE +21 -0
  3. package/README.md +295 -74
  4. package/lib/adapter.d.ts +39 -0
  5. package/lib/adapter.d.ts.map +1 -0
  6. package/{dist → lib}/adapter.js +20 -2
  7. package/lib/adapter.js.map +1 -0
  8. package/lib/app.d.ts +115 -0
  9. package/lib/app.d.ts.map +1 -0
  10. package/{dist → lib}/app.js +148 -78
  11. package/lib/app.js.map +1 -0
  12. package/lib/bot.d.ts +31 -0
  13. package/lib/bot.d.ts.map +1 -0
  14. package/lib/command.d.ts +32 -0
  15. package/lib/command.d.ts.map +1 -0
  16. package/lib/command.js +46 -0
  17. package/lib/command.js.map +1 -0
  18. package/lib/component.d.ts +27 -0
  19. package/lib/component.d.ts.map +1 -0
  20. package/lib/component.js +469 -0
  21. package/lib/component.js.map +1 -0
  22. package/{dist → lib}/config.d.ts.map +1 -1
  23. package/{dist → lib}/config.js +6 -9
  24. package/lib/config.js.map +1 -0
  25. package/lib/cron.d.ts +81 -0
  26. package/lib/cron.d.ts.map +1 -0
  27. package/lib/cron.js +159 -0
  28. package/lib/cron.js.map +1 -0
  29. package/lib/errors.d.ts +165 -0
  30. package/lib/errors.d.ts.map +1 -0
  31. package/lib/errors.js +306 -0
  32. package/lib/errors.js.map +1 -0
  33. package/lib/index.d.ts +15 -0
  34. package/lib/index.d.ts.map +1 -0
  35. package/lib/index.js +17 -0
  36. package/lib/index.js.map +1 -0
  37. package/lib/jsx-runtime.d.ts +12 -0
  38. package/lib/jsx-runtime.d.ts.map +1 -0
  39. package/lib/jsx-runtime.js +11 -0
  40. package/lib/jsx-runtime.js.map +1 -0
  41. package/lib/jsx.d.ts +32 -0
  42. package/lib/jsx.d.ts.map +1 -0
  43. package/lib/jsx.js +57 -0
  44. package/lib/jsx.js.map +1 -0
  45. package/lib/message.d.ts +47 -0
  46. package/lib/message.d.ts.map +1 -0
  47. package/lib/message.js +11 -0
  48. package/lib/message.js.map +1 -0
  49. package/lib/plugin.d.ts +50 -0
  50. package/lib/plugin.d.ts.map +1 -0
  51. package/lib/plugin.js +170 -0
  52. package/lib/plugin.js.map +1 -0
  53. package/lib/prompt.d.ts +116 -0
  54. package/lib/prompt.d.ts.map +1 -0
  55. package/lib/prompt.js +240 -0
  56. package/lib/prompt.js.map +1 -0
  57. package/lib/schema.d.ts +83 -0
  58. package/lib/schema.d.ts.map +1 -0
  59. package/lib/schema.js +245 -0
  60. package/lib/schema.js.map +1 -0
  61. package/{dist → lib}/types-generator.d.ts.map +1 -1
  62. package/{dist → lib}/types-generator.js +6 -3
  63. package/lib/types-generator.js.map +1 -0
  64. package/lib/types.d.ts +121 -0
  65. package/lib/types.d.ts.map +1 -0
  66. package/lib/utils.d.ts +52 -0
  67. package/lib/utils.d.ts.map +1 -0
  68. package/lib/utils.js +340 -0
  69. package/lib/utils.js.map +1 -0
  70. package/package.json +23 -9
  71. package/src/adapter.ts +25 -9
  72. package/src/app.ts +363 -258
  73. package/src/bot.ts +29 -8
  74. package/src/command.ts +50 -0
  75. package/src/component.ts +561 -0
  76. package/src/config.ts +9 -12
  77. package/src/cron.ts +176 -0
  78. package/src/errors.ts +365 -0
  79. package/src/index.ts +16 -13
  80. package/src/jsx-runtime.ts +12 -0
  81. package/src/jsx.d.ts +52 -0
  82. package/src/jsx.ts +92 -0
  83. package/src/message.ts +47 -0
  84. package/src/plugin.ts +148 -66
  85. package/src/prompt.ts +290 -0
  86. package/src/schema.ts +273 -0
  87. package/src/types-generator.ts +7 -3
  88. package/src/types.ts +80 -31
  89. package/src/utils.ts +313 -0
  90. package/tests/adapter.test.ts +36 -22
  91. package/tests/app.test.ts +30 -0
  92. package/tests/command.test.ts +545 -0
  93. package/tests/component-new.test.ts +348 -0
  94. package/tests/config.test.ts +1 -1
  95. package/tests/errors.test.ts +311 -0
  96. package/tests/expression-evaluation.test.ts +258 -0
  97. package/tests/message.test.ts +402 -0
  98. package/tests/plugin.test.ts +284 -143
  99. package/tests/utils.test.ts +80 -0
  100. package/tsconfig.json +3 -4
  101. package/dist/adapter.d.ts +0 -22
  102. package/dist/adapter.d.ts.map +0 -1
  103. package/dist/adapter.js.map +0 -1
  104. package/dist/app.d.ts +0 -69
  105. package/dist/app.d.ts.map +0 -1
  106. package/dist/app.js.map +0 -1
  107. package/dist/bot.d.ts +0 -9
  108. package/dist/bot.d.ts.map +0 -1
  109. package/dist/config.js.map +0 -1
  110. package/dist/index.d.ts +0 -9
  111. package/dist/index.d.ts.map +0 -1
  112. package/dist/index.js +0 -12
  113. package/dist/index.js.map +0 -1
  114. package/dist/logger.d.ts +0 -3
  115. package/dist/logger.d.ts.map +0 -1
  116. package/dist/logger.js +0 -3
  117. package/dist/logger.js.map +0 -1
  118. package/dist/plugin.d.ts +0 -41
  119. package/dist/plugin.d.ts.map +0 -1
  120. package/dist/plugin.js +0 -95
  121. package/dist/plugin.js.map +0 -1
  122. package/dist/types-generator.js.map +0 -1
  123. package/dist/types.d.ts +0 -69
  124. package/dist/types.d.ts.map +0 -1
  125. package/src/logger.ts +0 -3
  126. package/tests/logger.test.ts +0 -170
  127. package/tsconfig.tsbuildinfo +0 -1
  128. /package/{dist → lib}/bot.js +0 -0
  129. /package/{dist → lib}/bot.js.map +0 -0
  130. /package/{dist → lib}/config.d.ts +0 -0
  131. /package/{dist → lib}/types-generator.d.ts +0 -0
  132. /package/{dist → lib}/types.js +0 -0
  133. /package/{dist → lib}/types.js.map +0 -0
package/src/bot.ts CHANGED
@@ -1,9 +1,30 @@
1
- import type {BotConfig, SendOptions} from "./types";
2
-
3
- export interface Bot<T extends BotConfig=BotConfig> {
4
- config: T;
5
- connected?: boolean;
6
- connect():Promise<void>
7
- disconnect():Promise<void>
8
- sendMessage(options: SendOptions): Promise<void>
1
+ import type {RegisteredAdapters, SendOptions,AdapterConfig} from "./types.js";
2
+ import {Message} from "./message.js";
3
+ /**
4
+ * Bot接口:所有平台机器人需实现的统一接口。
5
+ * 负责消息格式化、连接、断开、消息发送等。
6
+ * @template M 消息类型
7
+ * @template T 配置类型
8
+ */
9
+ export interface Bot<M extends object={},T extends BotConfig=BotConfig> {
10
+ /** 机器人配置 */
11
+ $config: T;
12
+ /** 是否已连接 */
13
+ $connected?: boolean;
14
+ /** 格式化平台消息为标准Message结构 */
15
+ $formatMessage(message:M):Message<M>
16
+ /** 连接机器人 */
17
+ $connect():Promise<void>
18
+ /** 断开机器人 */
19
+ $disconnect():Promise<void>
20
+ /** 发送消息 */
21
+ $sendMessage(options: SendOptions): Promise<void>
22
+ }
23
+ /**
24
+ * Bot配置类型,所有平台机器人通用
25
+ */
26
+ export interface BotConfig{
27
+ context:string
28
+ name:string
29
+ [key:string]:any
9
30
  }
package/src/command.ts ADDED
@@ -0,0 +1,50 @@
1
+ import {MatchResult, SegmentMatcher} from "segment-matcher";
2
+ import {AdapterMessage, RegisteredAdapters, SendContent} from "./types.js";
3
+ import type {Message} from "./message.js";
4
+ import {MaybePromise} from "@zhin.js/types";
5
+
6
+ /**
7
+ * MessageCommand类:命令系统核心,基于segment-matcher实现。
8
+ * 支持多平台命令注册、作用域限制、参数解析、异步处理等。
9
+ */
10
+ export class MessageCommand<T extends keyof RegisteredAdapters=keyof RegisteredAdapters> extends SegmentMatcher{
11
+ #callbacks:MessageCommand.Callback<T>[]=[];
12
+ #checkers:MessageCommand.Checker<T>[]=[]
13
+ /**
14
+ * 限定命令作用域(适配器名)
15
+ * @param scopes 适配器名列表
16
+ */
17
+ scope<R extends T>(...scopes:R[]):MessageCommand<R>{
18
+ this.#checkers.push((m)=>(scopes as string[]).includes(m.$adapter))
19
+ return this as MessageCommand<R>
20
+ }
21
+ /**
22
+ * 注册命令回调
23
+ * @param callback 命令处理函数
24
+ */
25
+ action(callback:MessageCommand.Callback<T>){
26
+ this.#callbacks.push(callback)
27
+ return this as MessageCommand<T>;
28
+ }
29
+ /**
30
+ * 处理消息,自动匹配命令并执行回调
31
+ * @param message 消息对象
32
+ * @returns 命令返回内容或undefined
33
+ */
34
+ async handle(message:Message<AdapterMessage<T>>):Promise<SendContent|undefined>{
35
+ for(const check of this.#checkers){
36
+ const result=await check(message)
37
+ if(!result) return;
38
+ }
39
+ const matched=this.match(message.$content);
40
+ if(!matched) return
41
+ for(const handler of this.#callbacks){
42
+ const result=await handler(message,matched)
43
+ if(result) return result
44
+ }
45
+ }
46
+ }
47
+ export namespace MessageCommand{
48
+ export type Callback<T extends keyof RegisteredAdapters>=(message:Message<AdapterMessage<T>>,result:MatchResult)=>MaybePromise<SendContent|void>;
49
+ export type Checker<T extends keyof RegisteredAdapters>=(message:Message<AdapterMessage<T>>)=>MaybePromise<boolean>
50
+ }
@@ -0,0 +1,561 @@
1
+ import { getValueWithRuntime, compiler, segment } from './utils.js';
2
+ import { Dict, SendContent, SendOptions, MessageElement } from './types.js';
3
+ import { Message } from "./message.js";
4
+
5
+ // 组件匹配符号
6
+ export const CapWithChild = Symbol('CapWithChild');
7
+ export const CapWithClose = Symbol('CapWithClose');
8
+
9
+ // 函数式组件类型定义
10
+ export type Component<P = any> = {
11
+ (props: P, context: ComponentContext): Promise<SendContent>;
12
+ name: string;
13
+ }
14
+
15
+
16
+ // 组件上下文接口 - 通过闭包严格控制可访问的信息
17
+ export interface ComponentContext {
18
+ // 基础渲染能力
19
+ render: (template: string, context?: Partial<ComponentContext>) => Promise<SendContent>;
20
+
21
+ // 数据访问(只读)
22
+ props: Readonly<Dict>;
23
+
24
+ // 父组件上下文(只读)
25
+ parent?: Readonly<ComponentContext>;
26
+
27
+ // 根模板(只读)
28
+ root: string;
29
+
30
+ // 子组件内容(React 概念)
31
+ children?: string;
32
+ getValue: (template: string) => any;
33
+ compile: (template: string) => string;
34
+ }
35
+
36
+ // 组件定义函数 - 简化版,只支持函数式组件
37
+ export function defineComponent<P = any>(
38
+ component: Component<P>,
39
+ name: string = component.name
40
+ ): Component<P> {
41
+ if (name) {
42
+ // 创建一个新的函数来避免修改只读属性
43
+ const namedComponent = component as Component<P>;
44
+ Object.defineProperty(namedComponent, 'name', {
45
+ value: name,
46
+ writable: false,
47
+ enumerable: false,
48
+ configurable: true
49
+ });
50
+ return namedComponent;
51
+ }
52
+ return component;
53
+ }
54
+
55
+ // 组件匹配函数
56
+ export function matchComponent<P = any>(comp: Component<P>, template: string): string {
57
+ // 使用更复杂的正则表达式来正确处理大括号内的内容
58
+ const selfClosingRegex = new RegExp(`<${comp.name}((?:[^>]|{[^}]*})*)?/>`);
59
+ const closingRegex = new RegExp(`<${comp.name}((?:[^>]|{[^}]*})*)?>([^<]*?)</${comp.name}>`);
60
+
61
+ let match = template.match(selfClosingRegex);
62
+ if (!match) {
63
+ match = template.match(closingRegex);
64
+ }
65
+
66
+ return match ? match[0] : '';
67
+ }
68
+
69
+ // 属性解析函数 - 支持 children
70
+ export function getProps<P = any>(comp: Component<P>, template: string, context?: ComponentContext): P {
71
+ // 1. 首先匹配组件标签,支持自闭合和闭合标签
72
+ const selfClosingRegex = new RegExp(`<${comp.name}((?:[^>]|{[^}]*})*)?/>`);
73
+ const closingRegex = new RegExp(`<${comp.name}((?:[^>]|{[^}]*})*)?>([^<]*?)</${comp.name}>`);
74
+
75
+ let match = template.match(selfClosingRegex);
76
+ let isSelfClosing = true;
77
+ let children = '';
78
+
79
+ if (!match) {
80
+ match = template.match(closingRegex);
81
+ isSelfClosing = false;
82
+ if (match) {
83
+ children = match[2] || '';
84
+ }
85
+ }
86
+
87
+ if (!match) {
88
+ return {} as P;
89
+ }
90
+
91
+ const attributesString = match[1] || '';
92
+
93
+ // 2. 解析属性,支持多种格式
94
+ const props: Record<string, any> = {};
95
+
96
+ // 如果有属性字符串,解析属性
97
+ if (attributesString.trim()) {
98
+ // 使用手动解析来处理复杂的嵌套结构
99
+ let i = 0;
100
+ while (i < attributesString.length) {
101
+ // 跳过空白字符
102
+ while (i < attributesString.length && /\s/.test(attributesString[i])) {
103
+ i++;
104
+ }
105
+
106
+ if (i >= attributesString.length) break;
107
+
108
+ // 解析属性名
109
+ let key = '';
110
+ while (i < attributesString.length && /[a-zA-Z0-9_$\-]/.test(attributesString[i])) {
111
+ key += attributesString[i];
112
+ i++;
113
+ }
114
+
115
+ if (!key) {
116
+ i++;
117
+ continue;
118
+ }
119
+
120
+ // 跳过空白字符
121
+ while (i < attributesString.length && /\s/.test(attributesString[i])) {
122
+ i++;
123
+ }
124
+
125
+ // 检查是否有等号
126
+ if (i < attributesString.length && attributesString[i] === '=') {
127
+ i++; // 跳过等号
128
+
129
+ // 跳过空白字符
130
+ while (i < attributesString.length && /\s/.test(attributesString[i])) {
131
+ i++;
132
+ }
133
+
134
+ if (i >= attributesString.length) {
135
+ props[key] = true;
136
+ break;
137
+ }
138
+
139
+ // 解析属性值
140
+ const value = parseAttributeValue(attributesString, i, context);
141
+ props[key] = value.value;
142
+ i = value.nextIndex;
143
+ } else {
144
+ // 没有等号,是布尔属性
145
+ props[key] = true;
146
+ }
147
+ }
148
+ }
149
+
150
+ // 3. 处理 children(如果不是自闭合标签)
151
+ if (!isSelfClosing && children.trim()) {
152
+ props.children = children;
153
+ }
154
+
155
+ // 4. 处理 kebab-case 到 camelCase 的转换
156
+ const camelCaseProps: Record<string, any> = {};
157
+ for (const [key, value] of Object.entries(props)) {
158
+ const camelKey = key.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());
159
+ camelCaseProps[camelKey] = value;
160
+ }
161
+
162
+ return camelCaseProps as P;
163
+ }
164
+
165
+ /**
166
+ * 解析属性值
167
+ */
168
+ function parseAttributeValue(str: string, startIndex: number, context?: ComponentContext): { value: any; nextIndex: number } {
169
+ let i = startIndex;
170
+
171
+ // 处理引号包围的字符串
172
+ if (str[i] === '"' || str[i] === "'") {
173
+ const quote = str[i];
174
+ i++; // 跳过开始引号
175
+ let value = '';
176
+
177
+ while (i < str.length && str[i] !== quote) {
178
+ if (str[i] === '\\' && i + 1 < str.length) {
179
+ // 处理转义字符
180
+ i++;
181
+ value += str[i];
182
+ } else {
183
+ value += str[i];
184
+ }
185
+ i++;
186
+ }
187
+
188
+ if (i < str.length) {
189
+ i++; // 跳过结束引号
190
+ }
191
+
192
+ // 检查引号内是否包含表达式(大括号)
193
+ if (value.includes('{') && value.includes('}')) {
194
+ // 包含表达式,进行求值
195
+ const evaluatedValue = evaluateQuotedExpression(value, context);
196
+ return { value: evaluatedValue, nextIndex: i };
197
+ }
198
+
199
+ return { value, nextIndex: i };
200
+ }
201
+
202
+ // 处理大括号包围的表达式
203
+ if (str[i] === '{') {
204
+ let braceCount = 0;
205
+ let value = '';
206
+ i++; // 跳过开始大括号
207
+
208
+ while (i < str.length) {
209
+ if (str[i] === '{') {
210
+ braceCount++;
211
+ } else if (str[i] === '}') {
212
+ if (braceCount === 0) {
213
+ i++; // 跳过结束大括号
214
+ break;
215
+ }
216
+ braceCount--;
217
+ }
218
+ value += str[i];
219
+ i++;
220
+ }
221
+
222
+ return { value: parseExpressionValue(value, context), nextIndex: i };
223
+ }
224
+
225
+ // 处理无引号的值
226
+ let value = '';
227
+ while (i < str.length && !/\s/.test(str[i]) && str[i] !== '>') {
228
+ value += str[i];
229
+ i++;
230
+ }
231
+
232
+ return { value: parseUnquotedValue(value, context), nextIndex: i };
233
+ }
234
+
235
+ /**
236
+ * 求值引号内的表达式
237
+ */
238
+ function evaluateQuotedExpression(quotedValue: string, context?: ComponentContext): string {
239
+ if (!context) return quotedValue;
240
+
241
+ // 处理引号内的表达式,将 {expr} 格式转换为 ${expr} 格式
242
+ let result = quotedValue;
243
+ const expressionRegex = /\{([^}]+)\}/g;
244
+ let match;
245
+
246
+ while ((match = expressionRegex.exec(quotedValue)) !== null) {
247
+ const expr = match[1];
248
+ try {
249
+ const value = context.getValue(expr);
250
+ if (value !== undefined && value !== expr) {
251
+ const stringValue = typeof value === 'string' ? value : JSON.stringify(value);
252
+ result = result.replace(match[0], stringValue);
253
+ }
254
+ } catch (error) {
255
+ // 如果求值失败,保持原始表达式
256
+ }
257
+ }
258
+
259
+ return result;
260
+ }
261
+
262
+ /**
263
+ * 解析表达式值
264
+ */
265
+ function parseExpressionValue(expr: string, context?: ComponentContext): any {
266
+ expr = expr.trim();
267
+
268
+ // 处理字符串字面量
269
+ if ((expr.startsWith('"') && expr.endsWith('"')) ||
270
+ (expr.startsWith("'") && expr.endsWith("'"))) {
271
+ return expr.slice(1, -1);
272
+ }
273
+
274
+ // 处理数字
275
+ if (/^-?\d+(\.\d+)?$/.test(expr)) {
276
+ return parseFloat(expr);
277
+ }
278
+
279
+ // 处理布尔值
280
+ if (expr === 'true') return true;
281
+ if (expr === 'false') return false;
282
+ if (expr === 'null') return null;
283
+ if (expr === 'undefined') return undefined;
284
+
285
+ // 处理数组
286
+ if (expr.startsWith('[') && expr.endsWith(']')) {
287
+ try {
288
+ return JSON.parse(expr);
289
+ } catch {
290
+ // 如果JSON解析失败,尝试手动解析简单数组
291
+ const items = expr.slice(1, -1).split(',').map(item =>
292
+ parseExpressionValue(item.trim(), context)
293
+ );
294
+ return items;
295
+ }
296
+ }
297
+
298
+ // 处理对象 - 改进的嵌套大括号处理
299
+ if (expr.startsWith('{') && expr.endsWith('}')) {
300
+ try {
301
+ return JSON.parse(expr);
302
+ } catch {
303
+ // 如果JSON解析失败,尝试手动解析简单对象
304
+ try {
305
+ return parseSimpleObject(expr);
306
+ } catch {
307
+ // 如果都失败,返回原始字符串
308
+ return expr;
309
+ }
310
+ }
311
+ }
312
+
313
+ // 处理表达式 - 在沙盒中执行
314
+ if (context) {
315
+ try {
316
+ const result = context.getValue(expr);
317
+ // 如果结果是 undefined,说明表达式被阻止或求值失败,返回原始表达式
318
+ if (result === undefined) {
319
+ return expr;
320
+ }
321
+ return result;
322
+ } catch (error) {
323
+ // 如果执行失败,返回原始表达式
324
+ return expr;
325
+ }
326
+ }
327
+
328
+ // 如果没有上下文,返回原始表达式
329
+ return expr;
330
+ }
331
+
332
+ /**
333
+ * 解析简单对象(处理嵌套大括号和方括号)
334
+ */
335
+ function parseSimpleObject(objStr: string): any {
336
+ const result: any = {};
337
+ let i = 1; // 跳过开始的 {
338
+ let depth = 0;
339
+ let bracketDepth = 0;
340
+ let key = '';
341
+ let value = '';
342
+ let inKey = true;
343
+ let inString = false;
344
+ let stringChar = '';
345
+
346
+ while (i < objStr.length - 1) { // 跳过结束的 }
347
+ const char = objStr[i];
348
+
349
+ if (!inString) {
350
+ if (char === '{') {
351
+ depth++;
352
+ value += char;
353
+ } else if (char === '}') {
354
+ depth--;
355
+ value += char;
356
+ } else if (char === '[') {
357
+ bracketDepth++;
358
+ value += char;
359
+ } else if (char === ']') {
360
+ bracketDepth--;
361
+ value += char;
362
+ } else if (char === ':' && depth === 0 && bracketDepth === 0) {
363
+ inKey = false;
364
+ i++;
365
+ continue;
366
+ } else if (char === ',' && depth === 0 && bracketDepth === 0) {
367
+ // 处理键值对
368
+ if (key.trim() && value.trim()) {
369
+ const parsedValue = parseExpressionValue(value.trim());
370
+ result[key.trim()] = parsedValue;
371
+ }
372
+ key = '';
373
+ value = '';
374
+ inKey = true;
375
+ i++;
376
+ continue;
377
+ } else if (char === '"' || char === "'") {
378
+ inString = true;
379
+ stringChar = char;
380
+ if (inKey) {
381
+ key += char;
382
+ } else {
383
+ value += char;
384
+ }
385
+ } else {
386
+ if (inKey) {
387
+ key += char;
388
+ } else {
389
+ value += char;
390
+ }
391
+ }
392
+ } else {
393
+ if (char === stringChar) {
394
+ inString = false;
395
+ }
396
+ if (inKey) {
397
+ key += char;
398
+ } else {
399
+ value += char;
400
+ }
401
+ }
402
+ i++;
403
+ }
404
+
405
+ // 处理最后一个键值对
406
+ if (key.trim() && value.trim()) {
407
+ const parsedValue = parseExpressionValue(value.trim());
408
+ result[key.trim()] = parsedValue;
409
+ }
410
+
411
+ return result;
412
+ }
413
+
414
+ /**
415
+ * 解析无引号的值
416
+ */
417
+ function parseUnquotedValue(value: string, context?: ComponentContext): any {
418
+ // 检查是否是大括号表达式
419
+ if (value.startsWith('{') && value.endsWith('}')) {
420
+ const expr = value.slice(1, -1); // 移除大括号
421
+ return parseExpressionValue(expr, context);
422
+ }
423
+
424
+ // 处理布尔值
425
+ if (value === 'true') return true;
426
+ if (value === 'false') return false;
427
+
428
+ // 处理数字
429
+ if (/^-?\d+(\.\d+)?$/.test(value)) {
430
+ return parseFloat(value);
431
+ }
432
+
433
+ // 处理 null/undefined
434
+ if (value === 'null') return null;
435
+ if (value === 'undefined') return undefined;
436
+
437
+ // 其他情况作为字符串处理
438
+ return value;
439
+ }
440
+
441
+ // 创建组件上下文的工厂函数
442
+ export function createComponentContext(
443
+ props: Dict = {},
444
+ parent?: ComponentContext,
445
+ root: string = ''
446
+ ): ComponentContext {
447
+ return {
448
+ // 基础渲染能力
449
+ render: async (template: string, context?: Partial<ComponentContext>) => {
450
+ // 这里需要实现渲染逻辑
451
+ return template;
452
+ },
453
+
454
+ // 数据访问(只读)
455
+ props: Object.freeze({ ...props }),
456
+
457
+ // 父组件上下文(只读)
458
+ parent: parent ? Object.freeze(parent) : undefined,
459
+
460
+ // 根模板(只读)
461
+ root,
462
+
463
+
464
+ // 子组件内容(React 概念)
465
+ children: undefined,
466
+ getValue: (template: string) => getValueWithRuntime(template, props),
467
+ compile: (template: string) => compiler(template, props),
468
+ };
469
+ }
470
+ export async function renderComponent<P = any>(component: Component<P>, template: string, context: ComponentContext): Promise<SendContent> {
471
+ const props = getProps(component, template, context);
472
+ return component(props, context);
473
+ }
474
+ // 渲染函数 - 支持新的组件系统
475
+ export async function renderComponents(
476
+ componentMap: Map<string, Component>,
477
+ options: SendOptions,
478
+ customContext?: ComponentContext
479
+ ): Promise<SendOptions> {
480
+ if (!componentMap.size) return options;
481
+
482
+ const components = [...Array.from(componentMap.values()), Fetch, Fragment];
483
+
484
+ // 创建根上下文
485
+ const rootContext = customContext || createComponentContext(
486
+ options,
487
+ undefined,
488
+ typeof options.content === 'string' ? options.content : segment.toString(options.content as MessageElement)
489
+ );
490
+
491
+ // 实现渲染逻辑
492
+ const renderWithContext = async (template: string, context: ComponentContext): Promise<SendContent> => {
493
+ let result = template;
494
+ let hasChanges = true;
495
+ let iterations = 0;
496
+ const maxIterations = 10; // 防止无限循环
497
+
498
+ // 编译模板
499
+ result = context.compile(result);
500
+
501
+ // 递归处理所有组件,直到没有更多组件需要渲染
502
+ while (hasChanges && iterations < maxIterations) {
503
+ hasChanges = false;
504
+ iterations++;
505
+
506
+ for (const comp of components) {
507
+ const match = matchComponent(comp, result);
508
+ if (match) {
509
+ // 创建组件特定的上下文
510
+ const componentContext = createComponentContext(
511
+ context.props,
512
+ context,
513
+ result
514
+ );
515
+
516
+ const rendered = await renderComponent(comp, match, componentContext);
517
+ const renderedString = typeof rendered === 'string' ? rendered : segment.toString(rendered as MessageElement);
518
+ result = result.replace(match, renderedString);
519
+ hasChanges = true;
520
+ break; // 处理一个组件后重新开始循环
521
+ }
522
+ }
523
+ }
524
+
525
+ return result;
526
+ };
527
+
528
+ // 更新根上下文的渲染函数
529
+ rootContext.render = async (template: string, context?: Partial<ComponentContext>) => {
530
+ return await renderWithContext(template, rootContext);
531
+ };
532
+
533
+ // 渲染模板
534
+ const output = await renderWithContext(rootContext.root, rootContext);
535
+ const content = typeof output === 'string' ? segment.from(output) : output as MessageElement[];
536
+
537
+ return {
538
+ ...options,
539
+ content
540
+ };
541
+ }
542
+
543
+ // 内置组件
544
+ export const Fragment = defineComponent(async (props: { children?: SendContent }, context: ComponentContext) => {
545
+ let children = props.children || '';
546
+ if (Array.isArray(children)) {
547
+ return children.join('');
548
+ }
549
+ if (typeof children === 'string') {
550
+ try{
551
+ const parsed = JSON.parse(segment.unescape(children));
552
+ return context.render(parsed || '', context);
553
+ }catch{
554
+ return context.render(children || '', context);
555
+ }
556
+ }
557
+ return String(children);
558
+ }, 'Fragment');
559
+ export const Fetch = defineComponent(async ({ url }) => {
560
+ return await fetch(url).then((r) => r.text());
561
+ }, 'fetch');
package/src/config.ts CHANGED
@@ -4,7 +4,7 @@ import { pathToFileURL } from 'node:url';
4
4
  import { parse as parseYaml, stringify as stringifyYaml } from 'yaml';
5
5
  import { parse as parseToml } from 'toml';
6
6
  import { config as loadDotenv } from 'dotenv';
7
- import type { AppConfig,DefineConfig } from './types.js';
7
+ import type {AppConfig, DefineConfig} from './types.js';
8
8
 
9
9
  export interface ConfigOptions {
10
10
  configPath?: string;
@@ -49,7 +49,7 @@ function replaceEnvVars(str: string): string {
49
49
  } else if (defaultValue !== undefined) {
50
50
  return defaultValue;
51
51
  } else {
52
- console.warn(`环境变量 ${envName} 未定义,保持原值`);
52
+ // console.warn 已替换为注释
53
53
  return match;
54
54
  }
55
55
  });
@@ -131,7 +131,7 @@ function findConfigFile(cwd: string = process.cwd()): string | null {
131
131
  'zhin.config.yml',
132
132
  'zhin.config.json',
133
133
  'zhin.config.toml',
134
- 'zhin.config.js',
134
+ 'zhin.config.ts',
135
135
  'zhin.config.ts',
136
136
  // 然后查找 config.* 格式
137
137
  'config.yaml',
@@ -262,15 +262,12 @@ export function saveConfig(config: AppConfig, filePath: string): void {
262
262
  */
263
263
  export function createDefaultConfig(format: ConfigFormat = 'yaml'): AppConfig {
264
264
  return {
265
- bots: [
266
- {
267
- name: 'onebot11',
268
- context: 'onebot11',
269
- url: '${ONEBOT_URL:-ws://localhost:8080}',
270
- access_token: '${ONEBOT_ACCESS_TOKEN:-}'
271
- }
272
- ],
265
+ bots: [{
266
+ name: 'onebot11',
267
+ context: 'onebot11',
268
+ url: '${ONEBOT_URL:-ws://localhost:8080}'
269
+ }],
273
270
  plugin_dirs: ['./src/plugins', 'node_modules'],
274
- plugins: []
271
+ plugins: [],
275
272
  };
276
273
  }