@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/utils.ts ADDED
@@ -0,0 +1,313 @@
1
+ import {Dict, MessageElement, MessageSegment, SendContent} from "./types";
2
+
3
+ export function getValueWithRuntime(template: string, ctx: Dict) {
4
+ const result = evaluate(template, ctx);
5
+ if (result === `return(${template})`) return undefined;
6
+ return result;
7
+ }
8
+ export const evaluate = <S, T = any>(exp: string, context: S) => {
9
+ const result = execute<S, T>(`return(${exp})`, context);
10
+ // 如果结果是原始表达式,说明访问被阻止,返回 undefined
11
+ if (result === `return(${exp})`) return undefined;
12
+ return result;
13
+ };
14
+ const evalCache: Record<string, Function> = Object.create(null);
15
+ export const execute = <S, T = any>(exp: string, context: S):T => {
16
+ const fn = evalCache[exp] || (evalCache[exp] = toFunction(exp));
17
+ context={
18
+ ...context,
19
+ process:undefined,
20
+ global:undefined,
21
+ Buffer:undefined,
22
+ crypto:undefined
23
+ }
24
+ try {
25
+ return fn.apply(context, [context]);
26
+ } catch {
27
+ return exp as T;
28
+ }
29
+ };
30
+
31
+ const toFunction = (exp: string): Function => {
32
+ try {
33
+ return new Function(`$data`, `with($data){${exp}}`);
34
+ } catch {
35
+ return () => {};
36
+ }
37
+ };
38
+ export function compiler(template: string, ctx: Dict) {
39
+ const matched = [...template.matchAll(/\${([^}]*?)}/g)];
40
+ for (const item of matched) {
41
+ const tpl = item[1];
42
+ let value = getValueWithRuntime(tpl, ctx);
43
+ if (value === tpl) continue;
44
+ if (typeof value !== 'string') value = JSON.stringify(value, null, 2);
45
+ template = template.replace(`\${${item[1]}}`, value);
46
+ }
47
+ return template;
48
+ }
49
+ export function segment<T extends object>(type:string,data:T){
50
+ return {
51
+ type,
52
+ data
53
+ }
54
+ }
55
+ export namespace segment{
56
+ export function escape<T>(text: T): T {
57
+ if (typeof text !== 'string') return text;
58
+ return text
59
+ .replace(/&/g, '&amp;')
60
+ .replace(/</g, '&lt;')
61
+ .replace(/>/g, '&gt;')
62
+ .replace(/"/g, '&quot;')
63
+ .replace(/'/g, '&#39;') as T;
64
+ }
65
+ export function unescape<T>(text: T): T {
66
+ if (typeof text !== 'string') return text;
67
+ return text
68
+ .replace(/&lt;/g, '<')
69
+ .replace(/&gt;/g, '>')
70
+ .replace(/&quot;/g, '"')
71
+ .replace(/&#39;/g, "'")
72
+ .replace(/&amp;/g, '&') as T;
73
+ }
74
+ export function text(text:string){
75
+ return segment('text',{text});
76
+ }
77
+ export function face(id:string,text?:string){
78
+ return segment('face',{id,text});
79
+ }
80
+ export function from(content: SendContent): SendContent {
81
+ if (!Array.isArray(content)) content=[content];
82
+ const toString=(template:string|MessageElement)=>{
83
+ if(typeof template!=='string') return [template]
84
+ template=unescape(template);
85
+ const result: MessageElement[] = [];
86
+ const closingReg = /<(\S+)(\s[^>]+)?\/>/;
87
+ const twinningReg = /<(\S+)(\s[^>]+)?>([\s\S]*?)<\/\1>/;
88
+ while (template.length) {
89
+ const [_, type, attrStr = '', child = ''] = template.match(twinningReg) || template.match(closingReg) || [];
90
+ if (!type) break;
91
+ const isClosing = closingReg.test(template);
92
+ const matched = isClosing ? `<${type}${attrStr}/>` : `<${type}${attrStr}>${child}</${type}>`;
93
+ const index = template.indexOf(matched);
94
+ const prevText = template.slice(0, index);
95
+ if (prevText)
96
+ result.push({
97
+ type: 'text',
98
+ data: {
99
+ text: unescape(prevText),
100
+ },
101
+ });
102
+ template = template.slice(index + matched.length);
103
+ const attrArr = [...attrStr.matchAll(/\s([^=]+)(?=(?=="([^"]+)")|(?=='([^']+)'))/g)];
104
+ const data = Object.fromEntries(
105
+ attrArr.map(([source, key, v1, v2]) => {
106
+ const value = v1 || v2;
107
+ try {
108
+ return [key, JSON.parse(unescape(value))];
109
+ } catch {
110
+ return [key, unescape(value)];
111
+ }
112
+ }),
113
+ );
114
+ if (child) {
115
+ data.message = toString(child).map(({ type, data }) => ({ type, ...data }));
116
+ }
117
+ result.push({
118
+ type: type,
119
+ data,
120
+ });
121
+ }
122
+ if (template.length) {
123
+ result.push({
124
+ type: 'text',
125
+ data: {
126
+ text: unescape(template),
127
+ },
128
+ });
129
+ }
130
+ return result;
131
+ }
132
+ return content.reduce((result,item)=>{
133
+ result.push(...toString(item))
134
+ return result;
135
+ },[] as MessageElement[])
136
+ }
137
+ export function raw(content:SendContent){
138
+ if(!Array.isArray(content)) content=[content]
139
+ return content.map(item=>{
140
+ if(typeof item==='string') return item
141
+ const {type,data}=item
142
+ if(type==='text') return data.text
143
+ return data.text?`{${type}}(${data.text})`:`{${type}}`;
144
+ }).join('')
145
+ }
146
+ export function toString(content:SendContent){
147
+ if(!Array.isArray(content)) content=[content]
148
+ return content.map(item=>{
149
+ if(typeof item==='string') return item
150
+ let {type,data}=item
151
+ if(typeof type==='function') type=type.name
152
+ if(type==='text') return data.text
153
+ return `<${type} ${Object.keys(data).map(key=>`${key}='${escape(JSON.stringify(data[key]))}'`).join(' ')}/>`
154
+ }).join('')
155
+ }
156
+ }
157
+
158
+ export function remove<T>(list: T[], fn: (item: T) => boolean): void;
159
+ export function remove<T>(list: T[], item: T): void;
160
+ export function remove<T>(list: T[], arg: T | ((item: T) => boolean)) {
161
+ const index =
162
+ typeof arg === 'function' && !list.every(item => typeof item === 'function')
163
+ ? list.findIndex(arg as (item: T) => boolean)
164
+ : list.indexOf(arg as T);
165
+ if (index !== -1) list.splice(index, 1);
166
+ }
167
+ export function isEmpty<T>(item:T){
168
+ if(Array.isArray(item)) return item.length===0
169
+ if(typeof item==='object'){
170
+ if(!item) return true
171
+ return Reflect.ownKeys(item).length===0
172
+ }
173
+ return false
174
+ }
175
+
176
+ export namespace Time {
177
+ export const millisecond = 1;
178
+ export const second = 1000;
179
+ export const minute = second * 60;
180
+ export const hour = minute * 60;
181
+ export const day = hour * 24;
182
+ export const week = day * 7;
183
+
184
+ let timezoneOffset = new Date().getTimezoneOffset();
185
+
186
+ export function setTimezoneOffset(offset: number) {
187
+ timezoneOffset = offset;
188
+ }
189
+
190
+ export function getTimezoneOffset() {
191
+ return timezoneOffset;
192
+ }
193
+
194
+ export function getDateNumber(date: number | Date = new Date(), offset?: number) {
195
+ if (typeof date === 'number') date = new Date(date);
196
+ if (offset === undefined) offset = timezoneOffset;
197
+ return Math.floor((date.valueOf() / minute - offset) / 1440);
198
+ }
199
+
200
+ export function fromDateNumber(value: number, offset?: number) {
201
+ const date = new Date(value * day);
202
+ if (offset === undefined) offset = timezoneOffset;
203
+ return new Date(+date + offset * minute);
204
+ }
205
+
206
+ const numeric = /\d+(?:\.\d+)?/.source;
207
+ const timeRegExp = new RegExp(
208
+ `^${['w(?:eek(?:s)?)?', 'd(?:ay(?:s)?)?', 'h(?:our(?:s)?)?', 'm(?:in(?:ute)?(?:s)?)?', 's(?:ec(?:ond)?(?:s)?)?']
209
+ .map(unit => `(${numeric}${unit})?`)
210
+ .join('')}$`,
211
+ );
212
+
213
+ export function parseTime(source: string) {
214
+ const capture = timeRegExp.exec(source);
215
+ if (!capture) return 0;
216
+ return (
217
+ (parseFloat(capture[1]) * week || 0) +
218
+ (parseFloat(capture[2]) * day || 0) +
219
+ (parseFloat(capture[3]) * hour || 0) +
220
+ (parseFloat(capture[4]) * minute || 0) +
221
+ (parseFloat(capture[5]) * second || 0)
222
+ );
223
+ }
224
+
225
+ export function parseDate(date: string) {
226
+ const parsed = parseTime(date);
227
+ if (parsed) {
228
+ date = (Date.now() + parsed) as any;
229
+ } else if (/^\d{1,2}(:\d{1,2}){1,2}$/.test(date)) {
230
+ date = `${new Date().toLocaleDateString()}-${date}`;
231
+ } else if (/^\d{1,2}-\d{1,2}-\d{1,2}(:\d{1,2}){1,2}$/.test(date)) {
232
+ date = `${new Date().getFullYear()}-${date}`;
233
+ }
234
+ return date ? new Date(date) : new Date();
235
+ }
236
+
237
+ export function formatTimeShort(ms: number) {
238
+ const abs = Math.abs(ms);
239
+ if (abs >= day - hour / 2) {
240
+ return Math.round(ms / day) + 'd';
241
+ } else if (abs >= hour - minute / 2) {
242
+ return Math.round(ms / hour) + 'h';
243
+ } else if (abs >= minute - second / 2) {
244
+ return Math.round(ms / minute) + 'm';
245
+ } else if (abs >= second) {
246
+ return Math.round(ms / second) + 's';
247
+ }
248
+ return ms + 'ms';
249
+ }
250
+
251
+ export function formatTime(ms: number) {
252
+ let result: string;
253
+ if (ms >= day - hour / 2) {
254
+ ms += hour / 2;
255
+ result = Math.floor(ms / day) + ' 天';
256
+ if (ms % day > hour) {
257
+ result += ` ${Math.floor((ms % day) / hour)} 小时`;
258
+ }
259
+ } else if (ms >= hour - minute / 2) {
260
+ ms += minute / 2;
261
+ result = Math.floor(ms / hour) + ' 小时';
262
+ if (ms % hour > minute) {
263
+ result += ` ${Math.floor((ms % hour) / minute)} 分钟`;
264
+ }
265
+ } else if (ms >= minute - second / 2) {
266
+ ms += second / 2;
267
+ result = Math.floor(ms / minute) + ' 分钟';
268
+ if (ms % minute > second) {
269
+ result += ` ${Math.floor((ms % minute) / second)} 秒`;
270
+ }
271
+ } else {
272
+ result = Math.round(ms / second) + ' 秒';
273
+ }
274
+ return result;
275
+ }
276
+
277
+ const dayMap = ['日', '一', '二', '三', '四', '五', '六'];
278
+
279
+ function toDigits(source: number, length = 2) {
280
+ return source.toString().padStart(length, '0');
281
+ }
282
+
283
+ export function template(template: string, time = new Date()) {
284
+ return template
285
+ .replace('yyyy', time.getFullYear().toString())
286
+ .replace('yy', time.getFullYear().toString().slice(2))
287
+ .replace('MM', toDigits(time.getMonth() + 1))
288
+ .replace('dd', toDigits(time.getDate()))
289
+ .replace('hh', toDigits(time.getHours()))
290
+ .replace('mm', toDigits(time.getMinutes()))
291
+ .replace('ss', toDigits(time.getSeconds()))
292
+ .replace('SSS', toDigits(time.getMilliseconds(), 3));
293
+ }
294
+
295
+ function toHourMinute(time: Date) {
296
+ return `${toDigits(time.getHours())}:${toDigits(time.getMinutes())}`;
297
+ }
298
+
299
+ export function formatTimeInterval(time: Date, interval?: number) {
300
+ if (!interval) {
301
+ return template('yyyy-MM-dd hh:mm:ss', time);
302
+ } else if (interval === day) {
303
+ return `每天 ${toHourMinute(time)}`;
304
+ } else if (interval === week) {
305
+ return `每周${dayMap[time.getDay()]} ${toHourMinute(time)}`;
306
+ } else {
307
+ return `${template('yyyy-MM-dd hh:mm:ss', time)} 起每隔 ${formatTime(interval)}`;
308
+ }
309
+ }
310
+ }
311
+ export function sleep(ms: number) {
312
+ return new Promise(resolve => setTimeout(resolve, ms));
313
+ }
@@ -8,20 +8,27 @@ import type { BotConfig } from '../src/types'
8
8
  describe('适配器类测试', () => {
9
9
  // 创建测试用的Bot类
10
10
  class TestBot implements Bot {
11
- connected = false
11
+ $connected = false
12
+ $config: BotConfig
12
13
 
13
- constructor(public plugin: Plugin, public config: BotConfig) {}
14
+ constructor(public plugin: Plugin, config: BotConfig) {
15
+ this.$config = config
16
+ }
17
+
18
+ async $connect(): Promise<void> {
19
+ this.$connected = true
20
+ }
14
21
 
15
- async connect(): Promise<void> {
16
- this.connected = true
22
+ async $disconnect(): Promise<void> {
23
+ this.$connected = false
17
24
  }
18
25
 
19
- async disconnect(): Promise<void> {
20
- this.connected = false
26
+ async $sendMessage(): Promise<void> {
27
+ if (!this.$connected) throw new Error('机器人未连接')
21
28
  }
22
29
 
23
- async sendMessage(): Promise<void> {
24
- if (!this.connected) throw new Error('机器人未连接')
30
+ $formatMessage(message: any): any {
31
+ return message
25
32
  }
26
33
  }
27
34
 
@@ -68,8 +75,8 @@ describe('适配器类测试', () => {
68
75
  expect(adapter.bots.size).toBe(2)
69
76
  expect(adapter.bots.get('test-bot-1')).toBeDefined()
70
77
  expect(adapter.bots.get('test-bot-2')).toBeDefined()
71
- expect(adapter.bots.get('test-bot-1')?.connected).toBe(true)
72
- expect(adapter.bots.get('test-bot-2')?.connected).toBe(true)
78
+ expect(adapter.bots.get('test-bot-1')?.$connected).toBe(true)
79
+ expect(adapter.bots.get('test-bot-2')?.$connected).toBe(true)
73
80
 
74
81
  expect(loggerSpy).toHaveBeenCalledWith('bot test-bot-1 of adapter test-adapter connected')
75
82
  expect(loggerSpy).toHaveBeenCalledWith('bot test-bot-2 of adapter test-adapter connected')
@@ -106,7 +113,7 @@ describe('适配器类测试', () => {
106
113
  describe('错误处理测试', () => {
107
114
  it('应该处理机器人连接失败', async () => {
108
115
  class FailingBot extends TestBot {
109
- async connect(): Promise<void> {
116
+ async $connect(): Promise<void> {
110
117
  throw new Error('连接失败')
111
118
  }
112
119
  }
@@ -123,7 +130,7 @@ describe('适配器类测试', () => {
123
130
 
124
131
  it('应该处理机器人断开连接失败', async () => {
125
132
  class FailingBot extends TestBot {
126
- async disconnect(): Promise<void> {
133
+ async $disconnect(): Promise<void> {
127
134
  throw new Error('断开连接失败')
128
135
  }
129
136
  }
@@ -148,20 +155,27 @@ describe('适配器类测试', () => {
148
155
  }
149
156
 
150
157
  class ExtendedBot implements Bot<ExtendedBotConfig> {
151
- connected = false
158
+ $connected = false
159
+ $config: ExtendedBotConfig
152
160
 
153
- constructor(public plugin: Plugin, public config: ExtendedBotConfig) {}
161
+ constructor(public plugin: Plugin, config: ExtendedBotConfig) {
162
+ this.$config = config
163
+ }
164
+
165
+ async $connect(): Promise<void> {
166
+ this.$connected = true
167
+ }
154
168
 
155
- async connect(): Promise<void> {
156
- this.connected = true
169
+ async $disconnect(): Promise<void> {
170
+ this.$connected = false
157
171
  }
158
172
 
159
- async disconnect(): Promise<void> {
160
- this.connected = false
173
+ async $sendMessage(): Promise<void> {
174
+ if (!this.$connected) throw new Error('机器人未连接')
161
175
  }
162
176
 
163
- async sendMessage(): Promise<void> {
164
- if (!this.connected) throw new Error('机器人未连接')
177
+ $formatMessage(message: any): any {
178
+ return message
165
179
  }
166
180
  }
167
181
 
@@ -180,8 +194,8 @@ describe('适配器类测试', () => {
180
194
 
181
195
  const bot = extendedAdapter.bots.get('extended-bot')
182
196
  expect(bot).toBeDefined()
183
- expect(bot?.config.token).toBe('test-token')
184
- expect(bot?.config.platform).toBe('test-platform')
197
+ expect(bot?.$config.token).toBe('test-token')
198
+ expect(bot?.$config.platform).toBe('test-platform')
185
199
  })
186
200
  })
187
201
  })
package/tests/app.test.ts CHANGED
@@ -137,6 +137,36 @@ describe('App类测试', () => {
137
137
  it('当上下文不存在时应该抛出错误', () => {
138
138
  expect(() => app.getContext('non-existent')).toThrow("can't find Context of non-existent")
139
139
  })
140
+
141
+ it('应该正确设置和获取上下文描述', async () => {
142
+ // 创建测试插件
143
+ const plugin = app.createDependency('test-plugin-desc', 'test-plugin-desc.ts')
144
+
145
+ // 注册带描述的上下文
146
+ const context = {
147
+ name: 'test-context',
148
+ description: '这是一个测试上下文,用于验证描述字段功能',
149
+ mounted: () => ({ testValue: 'test' }),
150
+ dispose: () => {}
151
+ }
152
+ plugin.register(context)
153
+
154
+ // 等待插件挂载
155
+ await plugin.mounted()
156
+
157
+ // 使用上下文来验证功能
158
+ plugin.useContext('test-context', () => {
159
+ // 验证上下文可以正常获取(先测试基本功能)
160
+ const retrievedContext = app.getContext('test-context')
161
+ expect(retrievedContext).toEqual({ testValue: 'test' })
162
+
163
+ // 验证上下文列表包含描述信息
164
+ const contextList = app.contextList
165
+ const testContext = contextList.find(ctx => ctx.name === 'test-context')
166
+ expect(testContext).toBeDefined()
167
+ expect(testContext?.description).toBe('这是一个测试上下文,用于验证描述字段功能')
168
+ })
169
+ })
140
170
  })
141
171
 
142
172
  describe('消息处理测试', () => {