@zhin.js/core 1.0.0 → 1.0.1

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 (121) hide show
  1. package/CHANGELOG.md +9 -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 +107 -0
  19. package/lib/component.d.ts.map +1 -0
  20. package/lib/component.js +273 -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/message.d.ts +44 -0
  38. package/lib/message.d.ts.map +1 -0
  39. package/lib/message.js +11 -0
  40. package/lib/message.js.map +1 -0
  41. package/lib/plugin.d.ts +50 -0
  42. package/lib/plugin.d.ts.map +1 -0
  43. package/lib/plugin.js +170 -0
  44. package/lib/plugin.js.map +1 -0
  45. package/lib/prompt.d.ts +116 -0
  46. package/lib/prompt.d.ts.map +1 -0
  47. package/lib/prompt.js +240 -0
  48. package/lib/prompt.js.map +1 -0
  49. package/lib/schema.d.ts +83 -0
  50. package/lib/schema.d.ts.map +1 -0
  51. package/lib/schema.js +245 -0
  52. package/lib/schema.js.map +1 -0
  53. package/{dist → lib}/types-generator.d.ts.map +1 -1
  54. package/{dist → lib}/types-generator.js +6 -3
  55. package/lib/types-generator.js.map +1 -0
  56. package/lib/types.d.ts +119 -0
  57. package/lib/types.d.ts.map +1 -0
  58. package/lib/utils.d.ts +52 -0
  59. package/lib/utils.d.ts.map +1 -0
  60. package/lib/utils.js +338 -0
  61. package/lib/utils.js.map +1 -0
  62. package/package.json +15 -9
  63. package/src/adapter.ts +25 -9
  64. package/src/app.ts +363 -258
  65. package/src/bot.ts +29 -8
  66. package/src/command.ts +50 -0
  67. package/src/component.ts +318 -0
  68. package/src/config.ts +9 -12
  69. package/src/cron.ts +176 -0
  70. package/src/errors.ts +365 -0
  71. package/src/index.ts +16 -13
  72. package/src/message.ts +44 -0
  73. package/src/plugin.ts +148 -66
  74. package/src/prompt.ts +290 -0
  75. package/src/schema.ts +273 -0
  76. package/src/types-generator.ts +7 -3
  77. package/src/types.ts +77 -30
  78. package/src/utils.ts +312 -0
  79. package/tests/adapter.test.ts +36 -22
  80. package/tests/app.test.ts +30 -0
  81. package/tests/command.test.ts +545 -0
  82. package/tests/component.test.ts +656 -0
  83. package/tests/config.test.ts +1 -1
  84. package/tests/errors.test.ts +311 -0
  85. package/tests/message.test.ts +402 -0
  86. package/tests/plugin.test.ts +275 -143
  87. package/tests/utils.test.ts +80 -0
  88. package/tsconfig.json +3 -4
  89. package/dist/adapter.d.ts +0 -22
  90. package/dist/adapter.d.ts.map +0 -1
  91. package/dist/adapter.js.map +0 -1
  92. package/dist/app.d.ts +0 -69
  93. package/dist/app.d.ts.map +0 -1
  94. package/dist/app.js.map +0 -1
  95. package/dist/bot.d.ts +0 -9
  96. package/dist/bot.d.ts.map +0 -1
  97. package/dist/config.js.map +0 -1
  98. package/dist/index.d.ts +0 -9
  99. package/dist/index.d.ts.map +0 -1
  100. package/dist/index.js +0 -12
  101. package/dist/index.js.map +0 -1
  102. package/dist/logger.d.ts +0 -3
  103. package/dist/logger.d.ts.map +0 -1
  104. package/dist/logger.js +0 -3
  105. package/dist/logger.js.map +0 -1
  106. package/dist/plugin.d.ts +0 -41
  107. package/dist/plugin.d.ts.map +0 -1
  108. package/dist/plugin.js +0 -95
  109. package/dist/plugin.js.map +0 -1
  110. package/dist/types-generator.js.map +0 -1
  111. package/dist/types.d.ts +0 -69
  112. package/dist/types.d.ts.map +0 -1
  113. package/src/logger.ts +0 -3
  114. package/tests/logger.test.ts +0 -170
  115. package/tsconfig.tsbuildinfo +0 -1
  116. /package/{dist → lib}/bot.js +0 -0
  117. /package/{dist → lib}/bot.js.map +0 -0
  118. /package/{dist → lib}/config.d.ts +0 -0
  119. /package/{dist → lib}/types-generator.d.ts +0 -0
  120. /package/{dist → lib}/types.js +0 -0
  121. /package/{dist → lib}/types.js.map +0 -0
package/src/prompt.ts ADDED
@@ -0,0 +1,290 @@
1
+ import {AdapterMessage, Dict, RegisteredAdapter} from './types.js';
2
+ import { MessageMiddleware,Plugin } from './plugin.js';
3
+ import { Message } from './message.js';
4
+ import { Schema } from './schema.js';
5
+
6
+ /**
7
+ * Prompt类:用于实现机器人与用户的交互式提问与输入收集。
8
+ * 支持文本、数字、确认、列表、选项、Schema等多种输入类型,自动处理超时、默认值、格式化等。
9
+ * 典型用法:await new Prompt(plugin, event).text('请输入内容')
10
+ * @template P 适配器类型
11
+ */
12
+ export class Prompt<P extends RegisteredAdapter> {
13
+ /**
14
+ * 构造函数
15
+ * @param plugin 所属插件实例
16
+ * @param event 当前消息事件
17
+ */
18
+ constructor(private plugin:Plugin,private event: Message<AdapterMessage<P>>) {}
19
+ /**
20
+ * 获取当前会话唯一标识(适配器-机器人-频道-用户)
21
+ */
22
+ private getChannelAddress<P2 extends RegisteredAdapter>(event: Message<AdapterMessage<P2>>) {
23
+ return `${event.$adapter}-${event.$bot}-${event.$channel.type}:${event.$channel.id}-${event.$sender.id}`;
24
+ }
25
+ /**
26
+ * 通用提问方法,支持自定义格式化、超时、默认值等
27
+ * @param config 提问配置
28
+ */
29
+ private prompt<T = any>(config: Prompt.Config<T>) {
30
+ return new Promise<T>((resolve, reject) => {
31
+ this.event.$reply(config.tips);
32
+ this.middleware(
33
+ input => {
34
+ if (input instanceof Error) {
35
+ this.event.$reply(input.message);
36
+ if (config.defaultValue) resolve(config.defaultValue);
37
+ else reject(input);
38
+ return;
39
+ }
40
+ resolve(config.format(input));
41
+ },
42
+ config.timeout,
43
+ config.timeoutText,
44
+ );
45
+ });
46
+ }
47
+ /**
48
+ * 注册一次性消息中间件,等待用户输入或超时
49
+ * @param callback 输入回调
50
+ * @param timeout 超时时间(默认3分钟)
51
+ * @param timeoutText 超时提示
52
+ */
53
+ middleware(callback: (input: string | Error) => any, timeout: number = 3 * 60 * 1000, timeoutText = '输入超时') {
54
+ const middleware: MessageMiddleware<P> = (event, next) => {
55
+ if (this.getChannelAddress<P>(event) !== this.getChannelAddress<P>(this.event)) return next();
56
+ callback(event.$raw);
57
+ dispose();
58
+ clearTimeout(timer);
59
+ };
60
+ const dispose = this.plugin.addMiddleware(middleware);
61
+ const timer = setTimeout(() => {
62
+ dispose();
63
+ callback(new Error(timeoutText));
64
+ }, timeout);
65
+ }
66
+ /**
67
+ * 文本输入
68
+ */
69
+ async text(tips: string, timeout?: number, defaultValue = '', timeoutText?: string): Promise<string> {
70
+ return this.prompt<string>({
71
+ tips,
72
+ defaultValue,
73
+ timeoutText,
74
+ timeout,
75
+ format: (input: string) => input,
76
+ });
77
+ }
78
+ /**
79
+ * 任意输入
80
+ */
81
+ async any(tips: string, timeout?: number, defaultValue = '', timeoutText?: string) {
82
+ return this.prompt<string>({
83
+ tips,
84
+ defaultValue,
85
+ timeoutText,
86
+ timeout,
87
+ format: (input: string) => input,
88
+ });
89
+ }
90
+ /**
91
+ * 数字输入
92
+ */
93
+ async number(tips: string, timeout?: number, defaultValue = 0, timeoutText?: string): Promise<number> {
94
+ return this.prompt<number>({
95
+ tips,
96
+ defaultValue,
97
+ timeoutText,
98
+ timeout,
99
+ format: (input: string) => +input,
100
+ });
101
+ }
102
+ /**
103
+ * 确认输入(如 yes/no)
104
+ */
105
+ async confirm(
106
+ tips: string,
107
+ condition: string = 'yes',
108
+ timeout?: number,
109
+ defaultValue = false,
110
+ timeoutText?: string,
111
+ ): Promise<boolean> {
112
+ return this.prompt<boolean>({
113
+ tips: `${tips}\n输入“${condition}”以确认`,
114
+ defaultValue,
115
+ timeout,
116
+ timeoutText,
117
+ format: (input: string) => input === condition,
118
+ });
119
+ }
120
+ /**
121
+ * 列表输入,支持多值分隔
122
+ */
123
+ async list<T extends Prompt.SingleType = 'text'>(
124
+ tips: string,
125
+ config: Prompt.ListConfig<T> = { type: 'text' as T },
126
+ timeoutText?: string,
127
+ ): Promise<Prompt.Result<T>[]> {
128
+ const separator = config.separator || ',';
129
+ return this.prompt<Prompt.Result<T>[]>({
130
+ tips: `${tips}\n值之间使用“${separator}”分隔`,
131
+ defaultValue: config.defaultValue || [],
132
+ timeout: config.timeout,
133
+ timeoutText,
134
+ format: (input: string) =>
135
+ input.split(separator).map(v => {
136
+ switch (config.type) {
137
+ case 'boolean':
138
+ return Boolean(v);
139
+ case 'number':
140
+ return +v;
141
+ case 'text':
142
+ return v;
143
+ }
144
+ }) as Prompt.Result<T>[],
145
+ });
146
+ }
147
+ /**
148
+ * 返回常量值(用于Schema)
149
+ */
150
+ async const<T = any>(value: T): Promise<T> {
151
+ return value;
152
+ }
153
+ /**
154
+ * 选项选择,支持单选/多选
155
+ */
156
+ async pick<T extends Prompt.SingleType, M extends boolean = false>(
157
+ tips: string,
158
+ config: Prompt.PickConfig<T, M>,
159
+ timeoutText?: string,
160
+ ): Promise<Prompt.PickResult<T, M>> {
161
+ const moreTextArr = config.options.map((o, idx) => {
162
+ return `${idx + 1}.${o.label}`;
163
+ });
164
+ const separator = config.separator || ',';
165
+ if (config.multiple) moreTextArr.push(`多选请用“${separator}”分隔`);
166
+ return this.prompt<Prompt.PickResult<T, M>>({
167
+ tips: `${tips}\n${moreTextArr.join('\n')}`,
168
+ defaultValue: config.defaultValue,
169
+ timeout: config.timeout,
170
+ timeoutText,
171
+ format: (input: string) => {
172
+ if (!config.multiple)
173
+ return config.options.find((o, idx) => {
174
+ return idx + 1 === +input;
175
+ })?.value as Prompt.PickResult<T, M>;
176
+ const pickIdx = input.split(separator).map(Number);
177
+ return config.options
178
+ .filter((o, idx) => {
179
+ return pickIdx.includes(idx + 1);
180
+ })
181
+ .map(o => o.value) as Prompt.PickResult<T, M>;
182
+ },
183
+ });
184
+ }
185
+ /**
186
+ * 基于Schema的选项选择
187
+ */
188
+ async pickValueWithSchema<T extends Schema>(schema: T): Promise<Schema.Types<T>> {
189
+ return this.pick(schema.meta.description, {
190
+ type: '' as any,
191
+ options: schema.meta.options!.map(o => ({
192
+ label: o.label,
193
+ value: o.value,
194
+ })),
195
+ multiple: schema.meta.multiple,
196
+ defaultValue: schema.meta.default,
197
+ });
198
+ }
199
+ /**
200
+ * 批量Schema输入
201
+ */
202
+ async getValueWithSchemas<T extends Record<string, Schema>>(schemas: T): Promise<Schema.RecordTypes<T>> {
203
+ const result: Dict = {};
204
+ for (const key of Object.keys(schemas)) {
205
+ const schema = schemas[key];
206
+ result[key] = await this.getValueWithSchema(schema);
207
+ }
208
+ return result as Schema.RecordTypes<T>;
209
+ }
210
+ /**
211
+ * 单个Schema输入,自动分发到不同类型
212
+ */
213
+ async getValueWithSchema<T extends Schema>(schema: T): Promise<Schema.Types<T>> {
214
+ if (schema.meta.options) return this.pickValueWithSchema(schema);
215
+ switch (schema.meta.type) {
216
+ case 'number':
217
+ return (await this.number(schema.meta.description)) as Schema.Types<T>;
218
+ case 'string':
219
+ return (await this.text(schema.meta.description)) as Schema.Types<T>;
220
+ case 'boolean':
221
+ return (await this.confirm(schema.meta.description)) as Schema.Types<T>;
222
+ case 'object':
223
+ if (schema.meta.description) await this.event.$reply(schema.meta.description);
224
+ return (await this.getValueWithSchemas(schema.options.object!)) as Schema.Types<T>;
225
+ case 'date':
226
+ return await this.prompt({
227
+ tips: schema.meta.description,
228
+ defaultValue: schema.meta.default || new Date(),
229
+ format: (input: string) => new Date(input) as Schema.Types<T>,
230
+ });
231
+ case 'regexp':
232
+ return await this.prompt({
233
+ tips: schema.meta.description,
234
+ defaultValue: schema.meta.default || '',
235
+ format: (input: string) => new RegExp(input) as Schema.Types<T>,
236
+ });
237
+ case 'const':
238
+ return await this.const(schema.meta.default!);
239
+ case 'list':
240
+ const inner = schema.options.inner!;
241
+ if (!['string', 'boolean', 'number'].includes(inner.meta.type))
242
+ throw new Error(`unsupported inner type :${inner.meta.type}`);
243
+ return (await this.list(schema.meta.description, {
244
+ type: inner.meta.type === 'string' ? 'text' : (inner.meta.type as Prompt.SingleType),
245
+ defaultValue: schema.meta.default,
246
+ })) as Schema.Types<T>;
247
+ case 'dict':
248
+ default:
249
+ throw new Error(`Unsupported schema input type: ${schema.meta.type}`);
250
+ }
251
+ }
252
+ }
253
+ /**
254
+ * Prompt命名空间:类型辅助定义
255
+ */
256
+ export namespace Prompt {
257
+ interface SingleMap {
258
+ text: string;
259
+ number: number;
260
+ boolean: boolean;
261
+ }
262
+ export interface ListConfig<T extends SingleType> {
263
+ type: T;
264
+ defaultValue?: SingleMap[T][];
265
+ separator?: string;
266
+ timeout?: number;
267
+ }
268
+ export interface PickConfig<T extends SingleType = SingleType, M extends boolean = false> {
269
+ type: T;
270
+ defaultValue?: PickResult<T, M>;
271
+ separator?: string;
272
+ timeout?: number;
273
+ options: PickOption<T>[];
274
+ multiple?: M;
275
+ }
276
+ export type PickOption<T extends SingleType = 'text'> = {
277
+ label: string;
278
+ value: SingleMap[T];
279
+ };
280
+ export type PickResult<T extends SingleType, M extends boolean> = M extends true ? Result<T>[] : Result<T>;
281
+ export type SingleType = keyof SingleMap;
282
+ export type Result<T extends SingleType> = SingleMap[T];
283
+ export type Config<R = any> = {
284
+ tips: string;
285
+ defaultValue?: R;
286
+ timeout?: number;
287
+ timeoutText?: string;
288
+ format: (input: string) => R;
289
+ };
290
+ }
package/src/schema.ts ADDED
@@ -0,0 +1,273 @@
1
+ import { isEmpty } from './utils.js';
2
+
3
+ export class Schema<S = any, T = S> {
4
+ public [Symbol.toStringTag] = 'Schema';
5
+ constructor(
6
+ public meta: Schema.Meta<S, T>,
7
+ public options: Schema.Options = {},
8
+ ) {
9
+ const _this = this;
10
+ const schema = function (value?: S) {
11
+ const formatter = Schema.resolve(_this.meta.type);
12
+ if (!formatter) throw new Error(`type ${_this.meta.type} not found`);
13
+ return formatter.call(_this, value);
14
+ } as Schema<S, T>;
15
+ return new Proxy(schema, {
16
+ get(target, p: string | symbol, receiver: any): any {
17
+ return Reflect.get(_this, p, receiver);
18
+ },
19
+ set(target, p: string | symbol, value: any, receiver: any): boolean {
20
+ return Reflect.set(_this, p, value, receiver);
21
+ },
22
+ });
23
+ }
24
+ static fromJSON<S, T>(json: Schema.JSON<S, T>) {
25
+ const { object, inner, list, ...meta } = json;
26
+ const options: Schema.Options = {};
27
+ if (object)
28
+ options.object = Object.fromEntries(Object.entries(object).map(([key, value]) => [key, Schema.fromJSON(value)]));
29
+ if (inner) options.inner = Schema.fromJSON(inner);
30
+ return new Schema<S, T>(meta, options);
31
+ }
32
+ toJSON(): Record<string, any> {
33
+ return Object.fromEntries(
34
+ Object.entries({
35
+ ...this.meta,
36
+ default: typeof this.meta.default === 'function' ? this.meta.default() : this.meta.default,
37
+ inner: this.options.inner?.toJSON(),
38
+ dict: this.options.object
39
+ ? Object.fromEntries(Object.entries(this.options.object || {}).map(([key, value]) => [key, value.toJSON()]))
40
+ : undefined,
41
+ }).filter(([key, value]) => typeof value !== 'undefined'),
42
+ );
43
+ }
44
+ [Symbol.unscopables]() {
45
+ return {
46
+ options: true,
47
+ meta: true,
48
+ };
49
+ }
50
+ /** 设置是否必填 */
51
+ required(): this {
52
+ this.meta.required = true;
53
+ return this;
54
+ }
55
+ /** 是否隐藏 */
56
+ hidden(): this {
57
+ this.meta.hidden = true;
58
+ return this;
59
+ }
60
+ /** 设置描述 */
61
+ description(description: string): this {
62
+ this.meta.description = description;
63
+ return this;
64
+ }
65
+ /** 设置默认值 */
66
+ default(defaultValue: T): this {
67
+ this.meta.default = defaultValue;
68
+ return this;
69
+ }
70
+ /** 设置选项列表 */
71
+ option(list: (T | Schema.Option<T>)[]): this {
72
+ this.meta.options = Schema.formatOptionList(list);
73
+ return this;
74
+ }
75
+ /** 设置是否允许多选 */
76
+ multiple(): this {
77
+ if (this.meta.type !== 'list') throw new Error('multiple only support list type');
78
+ this.meta.multiple = true;
79
+ return this;
80
+ }
81
+ /** 声明一个数字类型 */
82
+ static number(description: string): Schema<number> {
83
+ return new Schema<number>({ type: 'number', description });
84
+ }
85
+ /** 声明一个字符串类型 */
86
+ static string(description: string): Schema<string> {
87
+ return new Schema<string>({ type: 'string', description });
88
+ }
89
+ /** 声明一个布尔类型 */
90
+ static boolean(description: string): Schema<boolean> {
91
+ return new Schema<boolean>({ type: 'boolean', description });
92
+ }
93
+ /** 声明一个正则类型 */
94
+ static regexp(description: string) {
95
+ return new Schema<RegExp | string, RegExp>({ type: 'regexp', description });
96
+ }
97
+ /** 声明一个日期类型 */
98
+ static date(description: string) {
99
+ return new Schema<Date | number, Date>({ type: 'date', description });
100
+ }
101
+ /** 声明一个字典类型 */
102
+ static dict<X extends Schema>(input: X, description: string) {
103
+ return new Schema<Record<string, Schema.Types<X>>>({ type: 'dict', description }, { inner: input });
104
+ }
105
+ static object<X extends Record<string, Schema>>(input: X, description: string = '') {
106
+ return new Schema<Schema.RecordTypes<X>>({ type: 'object', description }, { object: input });
107
+ }
108
+ /** 声明一个列表类型 */
109
+ static list<X extends Schema>(inner: X, description: string) {
110
+ return new Schema<Schema.Types<X>[]>({ type: 'list', description }, { inner });
111
+ }
112
+ /** 声明一个常量 */
113
+ static const<X extends string | number | boolean>(value: X, description: string) {
114
+ return new Schema<X>({ type: 'const', default: value as any, description });
115
+ }
116
+ static resolve<T extends string>(type: T): Schema.Formatter {
117
+ return Schema.formatters.get(type)!;
118
+ }
119
+ static extend<T extends string>(type: T, formatter: Schema.Formatter) {
120
+ Schema.formatters.set(type, formatter);
121
+ }
122
+ }
123
+ export interface Schema<S = any> {
124
+ (value?: S): S;
125
+ }
126
+ export namespace Schema {
127
+ export const formatters: Map<string, Formatter> = new Map<string, Formatter>();
128
+ export type Formatter<S = any, T = S> = (this: Schema, value: S) => T;
129
+ export type JSON<S = any, T = S> = Meta<S, T> & {
130
+ object?: Record<string, JSON>;
131
+ inner?: JSON;
132
+ list?: JSON[];
133
+ };
134
+ export interface Meta<S = any, T = S> {
135
+ hidden?: boolean;
136
+ type: string;
137
+ default?: T;
138
+ required?: boolean;
139
+ options?: Option<T>[];
140
+ multiple?: boolean;
141
+ description: string;
142
+ component?: string;
143
+ min?: number;
144
+ max?: number;
145
+ step?: number;
146
+ }
147
+ export interface Options {
148
+ object?: Record<string, Schema>;
149
+ inner?: Schema;
150
+ }
151
+ export type Types<T> = T extends Schema<infer S> ? S : never;
152
+ export type RecordTypes<T> = T extends Record<string, Schema>
153
+ ? {
154
+ [K in keyof T]?: Types<T[K]>;
155
+ }
156
+ : unknown;
157
+ export function checkDefault<T>(schema: Schema, value: T, fallback: T = value) {
158
+ if (isEmpty(value)) {
159
+ value = schema.meta.default || fallback;
160
+ }
161
+ const validateType = (schema: Schema, value: any) => {
162
+ switch (schema.meta.type) {
163
+ case 'string':
164
+ if (!['string', 'undefined'].includes(typeof value)) throw new TypeError(`value is not a string`);
165
+ break;
166
+ case 'number':
167
+ if (!['number', 'undefined'].includes(typeof value)) throw new TypeError(`value is not a number`);
168
+ break;
169
+ case 'boolean':
170
+ if (!['boolean', 'undefined'].includes(typeof value)) throw new TypeError(`value is not a boolean`);
171
+ break;
172
+ case 'regexp':
173
+ if (!['string', 'undefined'].includes(typeof value) && !(value instanceof RegExp))
174
+ throw new TypeError(`value is not a RegExp|string`);
175
+ break;
176
+ case 'date':
177
+ if (!['number', 'undefined'].includes(typeof value) && !(value instanceof Date))
178
+ throw new TypeError(`value is not a Date|number`);
179
+ if (value instanceof Date && isNaN(value.getTime())) throw new TypeError(`value is not a valid Date`);
180
+ break;
181
+ case 'dict':
182
+ if (!['object', 'undefined', 'null'].includes(typeof value)) throw new TypeError(`value is not a object`);
183
+ break;
184
+ case 'object':
185
+ if (!['object', 'undefined', 'null'].includes(typeof value)) throw new TypeError(`value is not a object`);
186
+ break;
187
+ case 'list':
188
+ if (typeof value !== 'undefined' && !Array.isArray(value)) throw new TypeError(`value is not a list`);
189
+ break;
190
+ case 'const':
191
+ if (typeof value !== 'undefined' && value !== schema.meta.default) throw new TypeError(`value is not const`);
192
+ break;
193
+ default:
194
+ throw new TypeError(`value is not a valid type`);
195
+ }
196
+ };
197
+ if (schema.meta.required && typeof value === 'undefined') throw new Error(`value is required`);
198
+ validateType(schema, value);
199
+ return value;
200
+ }
201
+ export type Option<T = any> = {
202
+ label: string;
203
+ value: T;
204
+ };
205
+ export function formatOptionList<T extends (any | Schema.Option)[]>(list: T): Schema.Option[] {
206
+ return list.map(item => {
207
+ if (typeof item === 'string') {
208
+ return {
209
+ label: `${item}`,
210
+ value: item,
211
+ } as Schema.Option;
212
+ }
213
+ return item as unknown as Schema.Option;
214
+ });
215
+ }
216
+ }
217
+ Schema.extend('number', function (this: Schema, value: any) {
218
+ value = Schema.checkDefault(this, value);
219
+ return value;
220
+ });
221
+ Schema.extend('string', function (this: Schema, value: any) {
222
+ value = Schema.checkDefault(this, value);
223
+ return value;
224
+ });
225
+ Schema.extend('boolean', function (this: Schema, value: any) {
226
+ return Schema.checkDefault(this, value);
227
+ });
228
+ Schema.extend('dict', function (this: Schema, value: any) {
229
+ value = Schema.checkDefault(this, value, {});
230
+ return Object.fromEntries(
231
+ Object.entries(value).map(([key, schema]) => {
232
+ return [key, this.options.inner!(schema)];
233
+ }),
234
+ );
235
+ });
236
+
237
+ Schema.extend('object', function (this: Schema, value: any) {
238
+ const getDefault = (schema: Schema) => {
239
+ const result = Object.create(null);
240
+ for (const key in schema.options.object) {
241
+ result[key] = getDefault(schema.options.object[key]);
242
+ }
243
+ return result;
244
+ };
245
+ value = Schema.checkDefault(this, value, getDefault(this));
246
+ return Object.fromEntries(
247
+ Object.entries(value).map(([key, schema]) => {
248
+ return [key, this.options.object![key](schema)];
249
+ }),
250
+ );
251
+ });
252
+ Schema.extend('list', function (this: Schema, value: any) {
253
+ value = Schema.checkDefault(this, value, []);
254
+ return value.map((item: any) => this.options.inner!(item));
255
+ });
256
+ Schema.extend('regexp', function (this: Schema, value: any) {
257
+ value = Schema.checkDefault(this, value);
258
+ if (typeof value === 'string') {
259
+ return new RegExp(value);
260
+ }
261
+ return value;
262
+ });
263
+ Schema.extend('date', function (this: Schema, value: any) {
264
+ value = Schema.checkDefault(this, value);
265
+ return new Date(value);
266
+ });
267
+ Schema.extend('const', function (this: Schema, value: any) {
268
+ value = Schema.checkDefault(this, value);
269
+ if (value !== this.meta.default) {
270
+ throw new Error('const value not match');
271
+ }
272
+ return value;
273
+ });
@@ -1,6 +1,8 @@
1
1
  import fs from 'node:fs';
2
2
  import path from 'node:path';
3
- import { logger } from './logger.js';
3
+ import { getLogger } from '@zhin.js/logger';
4
+
5
+ const logger = getLogger('TypesGenerator');
4
6
 
5
7
  /**
6
8
  * 更新 tsconfig.json 的类型声明
@@ -48,7 +50,7 @@ export async function generateEnvTypes(cwd: string): Promise<void> {
48
50
  try {
49
51
  tsconfig = JSON.parse(fs.readFileSync(tsconfigPath, 'utf-8'));
50
52
  } catch (err) {
51
- console.error(err)
53
+ // console.error 已替换为注释
52
54
  logger.warn('⚠️ Failed to parse tsconfig.json, creating new one');
53
55
  }
54
56
  }
@@ -69,6 +71,8 @@ export async function generateEnvTypes(cwd: string): Promise<void> {
69
71
  fs.writeFileSync(tsconfigPath, JSON.stringify(tsconfig, null, 2), 'utf-8');
70
72
  logger.info('✅ Updated TypeScript types configuration');
71
73
  } catch (error) {
72
- logger.warn('⚠️ Failed to update TypeScript types:', error);
74
+ logger.warn('⚠️ Failed to update TypeScript types', {
75
+ error: error instanceof Error ? error.message : String(error)
76
+ });
73
77
  }
74
78
  }