@onebots/core 0.5.0 → 1.0.4

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 (66) hide show
  1. package/README.md +3 -3
  2. package/lib/__tests__/config-validator.test.d.ts +4 -0
  3. package/lib/__tests__/config-validator.test.js +152 -0
  4. package/lib/__tests__/di-container.test.d.ts +4 -0
  5. package/lib/__tests__/di-container.test.js +114 -0
  6. package/lib/__tests__/errors.test.d.ts +4 -0
  7. package/lib/__tests__/errors.test.js +111 -0
  8. package/lib/__tests__/integration.test.d.ts +5 -0
  9. package/lib/__tests__/integration.test.js +112 -0
  10. package/lib/__tests__/lifecycle.test.d.ts +4 -0
  11. package/lib/__tests__/lifecycle.test.js +163 -0
  12. package/lib/account.d.ts +4 -1
  13. package/lib/account.js +6 -3
  14. package/lib/adapter.d.ts +67 -1
  15. package/lib/adapter.js +31 -4
  16. package/lib/base-app.d.ts +30 -3
  17. package/lib/base-app.js +295 -142
  18. package/lib/circuit-breaker.d.ts +94 -0
  19. package/lib/circuit-breaker.js +206 -0
  20. package/lib/config-validator.d.ts +51 -0
  21. package/lib/config-validator.js +184 -0
  22. package/lib/connection-pool.d.ts +68 -0
  23. package/lib/connection-pool.js +202 -0
  24. package/lib/db.d.ts +2 -1
  25. package/lib/db.js +11 -2
  26. package/lib/di-container.d.ts +60 -0
  27. package/lib/di-container.js +103 -0
  28. package/lib/errors.d.ts +157 -0
  29. package/lib/errors.js +257 -0
  30. package/lib/index.d.ts +13 -4
  31. package/lib/index.js +17 -3
  32. package/lib/lifecycle.d.ts +75 -0
  33. package/lib/lifecycle.js +175 -0
  34. package/lib/logger.d.ts +76 -0
  35. package/lib/logger.js +156 -0
  36. package/lib/metrics.d.ts +80 -0
  37. package/lib/metrics.js +201 -0
  38. package/lib/middleware/index.d.ts +8 -0
  39. package/lib/middleware/index.js +8 -0
  40. package/lib/middleware/metrics-collector.d.ts +9 -0
  41. package/lib/middleware/metrics-collector.js +64 -0
  42. package/lib/middleware/rate-limit.d.ts +32 -0
  43. package/lib/middleware/rate-limit.js +149 -0
  44. package/lib/middleware/security-audit.d.ts +33 -0
  45. package/lib/middleware/security-audit.js +223 -0
  46. package/lib/middleware/token-manager.d.ts +73 -0
  47. package/lib/middleware/token-manager.js +186 -0
  48. package/lib/middleware/token-validator.d.ts +42 -0
  49. package/lib/middleware/token-validator.js +198 -0
  50. package/lib/protocol.d.ts +2 -3
  51. package/lib/protocol.js +4 -0
  52. package/lib/rate-limiter.d.ts +88 -0
  53. package/lib/rate-limiter.js +196 -0
  54. package/lib/registry.d.ts +27 -0
  55. package/lib/registry.js +40 -5
  56. package/lib/retry.d.ts +87 -0
  57. package/lib/retry.js +205 -0
  58. package/lib/router.d.ts +43 -6
  59. package/lib/router.js +139 -12
  60. package/lib/timestamp.d.ts +42 -0
  61. package/lib/timestamp.js +72 -0
  62. package/lib/types.d.ts +1 -0
  63. package/lib/types.js +2 -1
  64. package/lib/utils.d.ts +2 -1
  65. package/lib/utils.js +11 -19
  66. package/package.json +24 -9
@@ -0,0 +1,198 @@
1
+ /**
2
+ * 访问令牌验证中间件
3
+ * 支持多种令牌格式和验证方式
4
+ */
5
+ import { createLogger } from '../logger.js';
6
+ import { logInvalidToken, logAuthFailure } from './security-audit.js';
7
+ import crypto from 'crypto';
8
+ const logger = createLogger('TokenValidator');
9
+ /**
10
+ * 创建令牌验证中间件
11
+ */
12
+ export function createTokenValidator(config = {}) {
13
+ const { tokenName = 'access_token', fromHeader = true, fromQuery = true, validator, required = true, errorMessage = 'Invalid or missing access token', } = config;
14
+ return async (ctx, next) => {
15
+ // 从多个位置提取令牌
16
+ let token;
17
+ if (fromHeader) {
18
+ // 从 Authorization header 获取
19
+ const authHeader = ctx.get('authorization');
20
+ if (authHeader) {
21
+ // 支持 Bearer token 格式
22
+ const match = authHeader.match(/^Bearer\s+(.+)$/i);
23
+ if (match) {
24
+ token = match[1];
25
+ }
26
+ else {
27
+ token = authHeader;
28
+ }
29
+ }
30
+ }
31
+ if (!token && fromQuery) {
32
+ // 从 query 参数获取
33
+ token = ctx.query[tokenName];
34
+ }
35
+ // 检查令牌是否存在
36
+ if (!token) {
37
+ if (required) {
38
+ logInvalidToken(ctx);
39
+ ctx.status = 401;
40
+ ctx.body = {
41
+ error: 'Unauthorized',
42
+ message: errorMessage,
43
+ };
44
+ return;
45
+ }
46
+ else {
47
+ // 可选令牌,继续执行
48
+ return next();
49
+ }
50
+ }
51
+ // 验证令牌
52
+ let isValid = true;
53
+ if (validator) {
54
+ try {
55
+ isValid = await validator(token, ctx);
56
+ }
57
+ catch (error) {
58
+ logger.error('Token validation error', {
59
+ error: error.message,
60
+ path: ctx.path,
61
+ });
62
+ isValid = false;
63
+ }
64
+ }
65
+ else {
66
+ // 默认验证:检查令牌不为空
67
+ isValid = token.length > 0;
68
+ }
69
+ if (!isValid) {
70
+ logInvalidToken(ctx, token);
71
+ ctx.status = 401;
72
+ ctx.body = {
73
+ error: 'Unauthorized',
74
+ message: errorMessage,
75
+ };
76
+ return;
77
+ }
78
+ // 将令牌存储到 context 中,供后续使用
79
+ ctx.state.token = token;
80
+ await next();
81
+ };
82
+ }
83
+ /**
84
+ * 创建基于配置的令牌验证器
85
+ * 从配置中读取 access_token 列表进行验证
86
+ */
87
+ export function createConfigTokenValidator(expectedTokens) {
88
+ const tokenSet = new Set(expectedTokens.filter(Boolean));
89
+ return createTokenValidator({
90
+ validator: (token) => {
91
+ return tokenSet.has(token);
92
+ },
93
+ });
94
+ }
95
+ /**
96
+ * HMAC 签名验证
97
+ */
98
+ export function createHMACValidator(secret, algorithm = 'sha256') {
99
+ return async (ctx, next) => {
100
+ const signature = ctx.get('x-signature') || ctx.query.signature;
101
+ if (!signature) {
102
+ logAuthFailure(ctx, 'Missing signature');
103
+ ctx.status = 401;
104
+ ctx.body = {
105
+ error: 'Unauthorized',
106
+ message: 'Missing signature',
107
+ };
108
+ return;
109
+ }
110
+ // 获取请求体
111
+ const body = ctx.request.body || ctx.request.rawBody || '';
112
+ const bodyString = typeof body === 'string' ? body : JSON.stringify(body);
113
+ // 计算 HMAC
114
+ const hmac = crypto.createHmac(algorithm, secret);
115
+ hmac.update(bodyString);
116
+ const expectedSignature = hmac.digest('hex');
117
+ // 使用时间安全比较
118
+ const isValid = crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expectedSignature));
119
+ if (!isValid) {
120
+ logAuthFailure(ctx, 'Invalid signature');
121
+ ctx.status = 401;
122
+ ctx.body = {
123
+ error: 'Unauthorized',
124
+ message: 'Invalid signature',
125
+ };
126
+ return;
127
+ }
128
+ await next();
129
+ };
130
+ }
131
+ /**
132
+ * 创建带令牌管理的验证器
133
+ * 支持令牌过期检查和自动刷新
134
+ */
135
+ export function createManagedTokenValidator(tokenManager, config = {}) {
136
+ const baseValidator = createTokenValidator(config);
137
+ return async (ctx, next) => {
138
+ const token = ctx.get('authorization')?.replace(/^Bearer\s+/i, '') ||
139
+ ctx.query[config.tokenName || 'access_token'];
140
+ if (!token) {
141
+ if (config.required !== false) {
142
+ logInvalidToken(ctx);
143
+ ctx.status = 401;
144
+ ctx.body = {
145
+ error: 'Unauthorized',
146
+ message: config.errorMessage || 'Invalid or missing access token',
147
+ };
148
+ return;
149
+ }
150
+ return next();
151
+ }
152
+ // 验证令牌
153
+ const validation = tokenManager.validateToken(token);
154
+ if (!validation.valid) {
155
+ if (validation.expired) {
156
+ logInvalidToken(ctx, token);
157
+ ctx.status = 401;
158
+ ctx.body = {
159
+ error: 'Unauthorized',
160
+ message: 'Token expired',
161
+ code: 'TOKEN_EXPIRED',
162
+ };
163
+ return;
164
+ }
165
+ else {
166
+ logInvalidToken(ctx, token);
167
+ ctx.status = 401;
168
+ ctx.body = {
169
+ error: 'Unauthorized',
170
+ message: config.errorMessage || 'Invalid access token',
171
+ };
172
+ return;
173
+ }
174
+ }
175
+ // 将令牌信息存储到 context
176
+ ctx.state.token = token;
177
+ ctx.state.tokenInfo = validation.info;
178
+ await next();
179
+ };
180
+ }
181
+ /**
182
+ * 组合多个验证器
183
+ */
184
+ export function combineValidators(...validators) {
185
+ return async (ctx, next) => {
186
+ for (const validator of validators) {
187
+ await validator(ctx, async () => {
188
+ // 空函数,用于链式调用
189
+ });
190
+ // 如果验证失败,响应已设置,直接返回
191
+ if (ctx.status === 401) {
192
+ return;
193
+ }
194
+ }
195
+ // 所有验证通过,继续执行
196
+ await next();
197
+ };
198
+ }
package/lib/protocol.d.ts CHANGED
@@ -3,7 +3,6 @@ import { Account } from "./account.js";
3
3
  import { Adapter } from "./adapter.js";
4
4
  import { Dict } from "./types.js";
5
5
  import { Router } from "./router.js";
6
- import { BaseApp } from "./base-app.js";
7
6
  /**
8
7
  * Base Protocol class
9
8
  * Represents a communication protocol (e.g., OneBot, Milky, Satori)
@@ -14,7 +13,7 @@ export declare abstract class Protocol<V extends string = string, C = any> exten
14
13
  config: Protocol.FullConfig<C>;
15
14
  abstract readonly name: string;
16
15
  abstract readonly version: V;
17
- get app(): BaseApp;
16
+ get app(): import("./base-app.js").BaseApp;
18
17
  get router(): Router;
19
18
  get logger(): import("log4js").Logger;
20
19
  constructor(adapter: Adapter, account: Account, config: Protocol.FullConfig<C>);
@@ -95,6 +94,6 @@ export declare namespace Protocol {
95
94
  */
96
95
  export type Factory<T extends Protocol = Protocol> = Creator<T> | Construct<T>;
97
96
  export function isClassFactory<T extends Protocol = Protocol>(factory: Factory<T>): factory is Construct<T>;
98
- export function createFilter(filters: Filters): (event: Dict) => any;
97
+ export function createFilter(filters?: Filters): any;
99
98
  export {};
100
99
  }
package/lib/protocol.js CHANGED
@@ -41,6 +41,10 @@ export class Protocol extends EventEmitter {
41
41
  }
42
42
  Protocol.isClassFactory = isClassFactory;
43
43
  function createFilter(filters) {
44
+ // 如果没有 filters,返回始终为 true 的过滤器
45
+ if (!filters || Object.keys(filters).length === 0) {
46
+ return () => true;
47
+ }
44
48
  const isLogicKey = (key) => {
45
49
  return [
46
50
  "$and",
@@ -0,0 +1,88 @@
1
+ /**
2
+ * API 速率限制器
3
+ * 防止触发平台的速率限制
4
+ */
5
+ export interface RateLimiterOptions {
6
+ /** 时间窗口内允许的最大请求数 */
7
+ maxRequests: number;
8
+ /** 时间窗口大小(毫秒) */
9
+ windowMs: number;
10
+ /** 请求之间的最小间隔(毫秒) */
11
+ minInterval?: number;
12
+ /** 超出限制时是否排队等待 */
13
+ queueExcess?: boolean;
14
+ /** 队列最大长度 */
15
+ maxQueueSize?: number;
16
+ }
17
+ /**
18
+ * 令牌桶速率限制器
19
+ */
20
+ export declare class RateLimiter {
21
+ private tokens;
22
+ private lastRefill;
23
+ private queue;
24
+ private processing;
25
+ private lastRequest;
26
+ private readonly maxRequests;
27
+ private readonly windowMs;
28
+ private readonly minInterval;
29
+ private readonly queueExcess;
30
+ private readonly maxQueueSize;
31
+ constructor(options: RateLimiterOptions);
32
+ /**
33
+ * 补充令牌
34
+ */
35
+ private refillTokens;
36
+ /**
37
+ * 尝试获取令牌
38
+ */
39
+ private tryAcquire;
40
+ /**
41
+ * 计算需要等待的时间
42
+ */
43
+ private getWaitTime;
44
+ /**
45
+ * 执行受限函数
46
+ */
47
+ execute<T>(fn: () => Promise<T>): Promise<T>;
48
+ /**
49
+ * 处理队列
50
+ */
51
+ private processQueue;
52
+ /**
53
+ * 获取当前状态
54
+ */
55
+ getStatus(): {
56
+ availableTokens: number;
57
+ queueLength: number;
58
+ isProcessing: boolean;
59
+ };
60
+ /**
61
+ * 清空队列
62
+ */
63
+ clearQueue(): void;
64
+ private sleep;
65
+ }
66
+ /**
67
+ * 速率限制错误
68
+ */
69
+ export declare class RateLimitError extends Error {
70
+ constructor(message: string);
71
+ }
72
+ /**
73
+ * 平台速率限制预设
74
+ */
75
+ export declare const RateLimitPresets: {
76
+ /** Discord - 50 请求/秒 */
77
+ discord: RateLimiterOptions;
78
+ /** Telegram - 30 消息/秒(群组 20/分钟) */
79
+ telegram: RateLimiterOptions;
80
+ /** QQ - 5 消息/秒 */
81
+ qq: RateLimiterOptions;
82
+ /** 通用 - 10 请求/秒 */
83
+ default: RateLimiterOptions;
84
+ };
85
+ /**
86
+ * 创建带速率限制的函数包装器
87
+ */
88
+ export declare function withRateLimit<T extends (...args: any[]) => Promise<any>>(fn: T, limiter: RateLimiter): T;
@@ -0,0 +1,196 @@
1
+ /**
2
+ * API 速率限制器
3
+ * 防止触发平台的速率限制
4
+ */
5
+ /**
6
+ * 令牌桶速率限制器
7
+ */
8
+ export class RateLimiter {
9
+ tokens;
10
+ lastRefill;
11
+ queue = [];
12
+ processing = false;
13
+ lastRequest = 0;
14
+ maxRequests;
15
+ windowMs;
16
+ minInterval;
17
+ queueExcess;
18
+ maxQueueSize;
19
+ constructor(options) {
20
+ this.maxRequests = options.maxRequests;
21
+ this.windowMs = options.windowMs;
22
+ this.minInterval = options.minInterval ?? 0;
23
+ this.queueExcess = options.queueExcess ?? true;
24
+ this.maxQueueSize = options.maxQueueSize ?? 100;
25
+ this.tokens = this.maxRequests;
26
+ this.lastRefill = Date.now();
27
+ }
28
+ /**
29
+ * 补充令牌
30
+ */
31
+ refillTokens() {
32
+ const now = Date.now();
33
+ const elapsed = now - this.lastRefill;
34
+ const tokensToAdd = (elapsed / this.windowMs) * this.maxRequests;
35
+ this.tokens = Math.min(this.maxRequests, this.tokens + tokensToAdd);
36
+ this.lastRefill = now;
37
+ }
38
+ /**
39
+ * 尝试获取令牌
40
+ */
41
+ tryAcquire() {
42
+ this.refillTokens();
43
+ if (this.tokens >= 1) {
44
+ this.tokens -= 1;
45
+ return true;
46
+ }
47
+ return false;
48
+ }
49
+ /**
50
+ * 计算需要等待的时间
51
+ */
52
+ getWaitTime() {
53
+ this.refillTokens();
54
+ if (this.tokens >= 1) {
55
+ return 0;
56
+ }
57
+ // 计算获得一个令牌需要的时间
58
+ const tokensNeeded = 1 - this.tokens;
59
+ return (tokensNeeded / this.maxRequests) * this.windowMs;
60
+ }
61
+ /**
62
+ * 执行受限函数
63
+ */
64
+ async execute(fn) {
65
+ // 检查最小间隔
66
+ const now = Date.now();
67
+ const timeSinceLastRequest = now - this.lastRequest;
68
+ if (timeSinceLastRequest < this.minInterval) {
69
+ await this.sleep(this.minInterval - timeSinceLastRequest);
70
+ }
71
+ // 尝试获取令牌
72
+ if (this.tryAcquire()) {
73
+ this.lastRequest = Date.now();
74
+ return fn();
75
+ }
76
+ // 如果不排队,直接抛出错误
77
+ if (!this.queueExcess) {
78
+ throw new RateLimitError('Rate limit exceeded');
79
+ }
80
+ // 检查队列大小
81
+ if (this.queue.length >= this.maxQueueSize) {
82
+ throw new RateLimitError('Rate limit queue is full');
83
+ }
84
+ // 加入队列
85
+ return new Promise((resolve, reject) => {
86
+ this.queue.push({ execute: fn, resolve, reject });
87
+ this.processQueue();
88
+ });
89
+ }
90
+ /**
91
+ * 处理队列
92
+ */
93
+ async processQueue() {
94
+ if (this.processing || this.queue.length === 0) {
95
+ return;
96
+ }
97
+ this.processing = true;
98
+ while (this.queue.length > 0) {
99
+ const waitTime = this.getWaitTime();
100
+ if (waitTime > 0) {
101
+ await this.sleep(waitTime);
102
+ }
103
+ // 检查最小间隔
104
+ const timeSinceLastRequest = Date.now() - this.lastRequest;
105
+ if (timeSinceLastRequest < this.minInterval) {
106
+ await this.sleep(this.minInterval - timeSinceLastRequest);
107
+ }
108
+ if (this.tryAcquire()) {
109
+ const request = this.queue.shift();
110
+ if (request) {
111
+ this.lastRequest = Date.now();
112
+ try {
113
+ const result = await request.execute();
114
+ request.resolve(result);
115
+ }
116
+ catch (error) {
117
+ request.reject(error instanceof Error ? error : new Error(String(error)));
118
+ }
119
+ }
120
+ }
121
+ }
122
+ this.processing = false;
123
+ }
124
+ /**
125
+ * 获取当前状态
126
+ */
127
+ getStatus() {
128
+ this.refillTokens();
129
+ return {
130
+ availableTokens: Math.floor(this.tokens),
131
+ queueLength: this.queue.length,
132
+ isProcessing: this.processing,
133
+ };
134
+ }
135
+ /**
136
+ * 清空队列
137
+ */
138
+ clearQueue() {
139
+ const error = new RateLimitError('Queue cleared');
140
+ this.queue.forEach(request => request.reject(error));
141
+ this.queue = [];
142
+ }
143
+ sleep(ms) {
144
+ return new Promise(resolve => setTimeout(resolve, ms));
145
+ }
146
+ }
147
+ /**
148
+ * 速率限制错误
149
+ */
150
+ export class RateLimitError extends Error {
151
+ constructor(message) {
152
+ super(message);
153
+ this.name = 'RateLimitError';
154
+ }
155
+ }
156
+ /**
157
+ * 平台速率限制预设
158
+ */
159
+ export const RateLimitPresets = {
160
+ /** Discord - 50 请求/秒 */
161
+ discord: {
162
+ maxRequests: 50,
163
+ windowMs: 1000,
164
+ minInterval: 50,
165
+ queueExcess: true,
166
+ },
167
+ /** Telegram - 30 消息/秒(群组 20/分钟) */
168
+ telegram: {
169
+ maxRequests: 30,
170
+ windowMs: 1000,
171
+ minInterval: 50,
172
+ queueExcess: true,
173
+ },
174
+ /** QQ - 5 消息/秒 */
175
+ qq: {
176
+ maxRequests: 5,
177
+ windowMs: 1000,
178
+ minInterval: 200,
179
+ queueExcess: true,
180
+ },
181
+ /** 通用 - 10 请求/秒 */
182
+ default: {
183
+ maxRequests: 10,
184
+ windowMs: 1000,
185
+ minInterval: 100,
186
+ queueExcess: true,
187
+ },
188
+ };
189
+ /**
190
+ * 创建带速率限制的函数包装器
191
+ */
192
+ export function withRateLimit(fn, limiter) {
193
+ return ((...args) => {
194
+ return limiter.execute(() => fn(...args));
195
+ });
196
+ }
package/lib/registry.d.ts CHANGED
@@ -2,6 +2,7 @@ import { Protocol } from "./protocol.js";
2
2
  import { Adapter } from "./adapter.js";
3
3
  import { BaseApp } from "./base-app.js";
4
4
  import { Account } from "./account.js";
5
+ import type { Schema } from "./config-validator.js";
5
6
  /**
6
7
  * Protocol Registry
7
8
  * Manages registration and retrieval of protocol implementations
@@ -9,6 +10,7 @@ import { Account } from "./account.js";
9
10
  export declare class ProtocolRegistry {
10
11
  private static protocols;
11
12
  private static metadata;
13
+ private static schemas;
12
14
  /**
13
15
  * Register a protocol implementation
14
16
  * @param name Protocol name (e.g., 'onebot', 'milky', 'satori')
@@ -17,6 +19,18 @@ export declare class ProtocolRegistry {
17
19
  * @param metadata Optional protocol metadata
18
20
  */
19
21
  static register(name: string, version: string, factory: Protocol.Factory, metadata?: Partial<Protocol.Metadata>): void;
22
+ /**
23
+ * Register a protocol config schema (key format: name.version)
24
+ */
25
+ static registerSchema(key: string, schema: Schema): void;
26
+ /**
27
+ * Get a protocol config schema by key
28
+ */
29
+ static getSchema(key: string): Schema | undefined;
30
+ /**
31
+ * Get all protocol config schemas
32
+ */
33
+ static getAllSchemas(): Record<string, Schema>;
20
34
  /**
21
35
  * Get a protocol factory
22
36
  * @param name Protocol name
@@ -63,6 +77,7 @@ export declare class ProtocolRegistry {
63
77
  export declare class AdapterRegistry {
64
78
  private static adapters;
65
79
  private static metadata;
80
+ private static schemas;
66
81
  /**
67
82
  * Register an adapter implementation
68
83
  * @param name Adapter name/platform (e.g., 'wechat', 'dingtalk', 'qq')
@@ -70,6 +85,18 @@ export declare class AdapterRegistry {
70
85
  * @param metadata Optional adapter metadata
71
86
  */
72
87
  static register(name: string, factory: Adapter.Factory, metadata?: Partial<Adapter.Metadata>): void;
88
+ /**
89
+ * Register an adapter config schema
90
+ */
91
+ static registerSchema(name: string, schema: Schema): void;
92
+ /**
93
+ * Get an adapter config schema
94
+ */
95
+ static getSchema(name: string): Schema | undefined;
96
+ /**
97
+ * Get all adapter config schemas
98
+ */
99
+ static getAllSchemas(): Record<string, Schema>;
73
100
  /**
74
101
  * Get an adapter factory
75
102
  * @param name Adapter name/platform
package/lib/registry.js CHANGED
@@ -7,6 +7,7 @@ import { Adapter } from "./adapter.js";
7
7
  export class ProtocolRegistry {
8
8
  static protocols = new Map();
9
9
  static metadata = new Map();
10
+ static schemas = new Map();
10
11
  /**
11
12
  * Register a protocol implementation
12
13
  * @param name Protocol name (e.g., 'onebot', 'milky', 'satori')
@@ -36,6 +37,24 @@ export class ProtocolRegistry {
36
37
  }
37
38
  }
38
39
  }
40
+ /**
41
+ * Register a protocol config schema (key format: name.version)
42
+ */
43
+ static registerSchema(key, schema) {
44
+ this.schemas.set(key, schema);
45
+ }
46
+ /**
47
+ * Get a protocol config schema by key
48
+ */
49
+ static getSchema(key) {
50
+ return this.schemas.get(key);
51
+ }
52
+ /**
53
+ * Get all protocol config schemas
54
+ */
55
+ static getAllSchemas() {
56
+ return Object.fromEntries(this.schemas.entries());
57
+ }
39
58
  /**
40
59
  * Get a protocol factory
41
60
  * @param name Protocol name
@@ -83,11 +102,6 @@ export class ProtocolRegistry {
83
102
  */
84
103
  static create(name, version, adapter, account, config) {
85
104
  const factory = this.get(name, version);
86
- console.log({
87
- name,
88
- version,
89
- account: account.account_id
90
- });
91
105
  if (!factory) {
92
106
  throw new Error(`Protocol ${name}/${version} not registered`);
93
107
  }
@@ -127,6 +141,7 @@ export class ProtocolRegistry {
127
141
  static clear() {
128
142
  this.protocols.clear();
129
143
  this.metadata.clear();
144
+ this.schemas.clear();
130
145
  }
131
146
  }
132
147
  /**
@@ -136,6 +151,7 @@ export class ProtocolRegistry {
136
151
  export class AdapterRegistry {
137
152
  static adapters = new Map();
138
153
  static metadata = new Map();
154
+ static schemas = new Map();
139
155
  /**
140
156
  * Register an adapter implementation
141
157
  * @param name Adapter name/platform (e.g., 'wechat', 'dingtalk', 'qq')
@@ -156,6 +172,24 @@ export class AdapterRegistry {
156
172
  });
157
173
  }
158
174
  }
175
+ /**
176
+ * Register an adapter config schema
177
+ */
178
+ static registerSchema(name, schema) {
179
+ this.schemas.set(name, schema);
180
+ }
181
+ /**
182
+ * Get an adapter config schema
183
+ */
184
+ static getSchema(name) {
185
+ return this.schemas.get(name);
186
+ }
187
+ /**
188
+ * Get all adapter config schemas
189
+ */
190
+ static getAllSchemas() {
191
+ return Object.fromEntries(this.schemas.entries());
192
+ }
159
193
  /**
160
194
  * Get an adapter factory
161
195
  * @param name Adapter name/platform
@@ -213,5 +247,6 @@ export class AdapterRegistry {
213
247
  static clear() {
214
248
  this.adapters.clear();
215
249
  this.metadata.clear();
250
+ this.schemas.clear();
216
251
  }
217
252
  }