Skip to content

Commit 58d6f01

Browse files
committed
feat: add command handler decorator system
1 parent 9d3ed95 commit 58d6f01

2 files changed

Lines changed: 569 additions & 0 deletions

File tree

Lines changed: 264 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,264 @@
1+
/**
2+
* Command Handler Decorators
3+
* 提供可复用的横切关注点(验证、错误处理、模式检查等)
4+
*/
5+
6+
import type { CommandHandler, CommandResult } from '../../runtime'
7+
import { ModeError, ValidationError } from '../../errors'
8+
9+
/**
10+
* 验证结果
11+
*/
12+
export interface ValidationResult {
13+
valid: boolean
14+
error?: string
15+
}
16+
17+
/**
18+
* 验证器函数类型
19+
*/
20+
export type Validator<T> = (payload: T) => ValidationResult
21+
22+
/**
23+
* 外部依赖(从 command-handlers.ts 导入)
24+
*/
25+
export interface CommandDependencies {
26+
process: import('../../process').FrpProcessManager
27+
nodeManager?: import('../../node').NodeManager
28+
rpcServer?: import('../../rpc').RpcServer
29+
mode: 'client' | 'server'
30+
}
31+
32+
/**
33+
* 仅限 Server 模式
34+
*/
35+
export function withServerModeOnly<T>(
36+
handler: CommandHandler<T>,
37+
deps: CommandDependencies
38+
): CommandHandler<T> {
39+
return async (command, ctx) => {
40+
if (deps.mode !== 'server') {
41+
throw new ModeError('This operation is only available in server mode')
42+
}
43+
return handler(command, ctx)
44+
}
45+
}
46+
47+
/**
48+
* 仅限 Client 模式
49+
*/
50+
export function withClientModeOnly<T>(
51+
handler: CommandHandler<T>,
52+
deps: CommandDependencies
53+
): CommandHandler<T> {
54+
return async (command, ctx) => {
55+
if (deps.mode !== 'client') {
56+
throw new ModeError('This operation is only available in client mode')
57+
}
58+
return handler(command, ctx)
59+
}
60+
}
61+
62+
/**
63+
* 验证装饰器
64+
*/
65+
export function withValidation<T>(
66+
validator: Validator<T>
67+
): (handler: CommandHandler<T>, deps: CommandDependencies) => CommandHandler<T> {
68+
return (handler, _deps) => async (command, ctx) => {
69+
const result = validator(command.payload as T)
70+
if (!result.valid) {
71+
throw new ValidationError(result.error || 'Validation failed')
72+
}
73+
return handler(command, ctx)
74+
}
75+
}
76+
77+
/**
78+
* 错误处理装饰器
79+
*/
80+
export function withErrorHandling<T>(
81+
handler: CommandHandler<T>,
82+
_deps: CommandDependencies
83+
): CommandHandler<T> {
84+
return async (command, ctx) => {
85+
try {
86+
return await handler(command, ctx)
87+
}
88+
catch (error) {
89+
return handleError(error)
90+
}
91+
}
92+
}
93+
94+
/**
95+
* 需要进程运行中
96+
*/
97+
export function withProcessRunning<T>(
98+
handler: CommandHandler<T>,
99+
deps: CommandDependencies
100+
): CommandHandler<T> {
101+
return async (command, ctx) => {
102+
if (!deps.process.isRunning()) {
103+
throw new ModeError('FRP process is not running')
104+
}
105+
return handler(command, ctx)
106+
}
107+
}
108+
109+
/**
110+
* 需要进程已停止
111+
*/
112+
export function withProcessStopped<T>(
113+
handler: CommandHandler<T>,
114+
deps: CommandDependencies
115+
): CommandHandler<T> {
116+
return async (command, ctx) => {
117+
if (deps.process.isRunning()) {
118+
throw new ModeError('FRP process is already running')
119+
}
120+
return handler(command, ctx)
121+
}
122+
}
123+
124+
/**
125+
* 需要 RPC Server(仅 Server 模式)
126+
*/
127+
export function withRpcServer<T>(
128+
handler: CommandHandler<T>,
129+
deps: CommandDependencies
130+
): CommandHandler<T> {
131+
return async (command, ctx) => {
132+
if (!deps.rpcServer) {
133+
throw new ModeError('RPC server not available')
134+
}
135+
return handler(command, ctx)
136+
}
137+
}
138+
139+
/**
140+
* 需要节点管理器(仅 Server 模式)
141+
*/
142+
export function withNodeManager<T>(
143+
handler: CommandHandler<T>,
144+
deps: CommandDependencies
145+
): CommandHandler<T> {
146+
return async (command, ctx) => {
147+
if (!deps.nodeManager) {
148+
throw new ModeError('Node manager not available')
149+
}
150+
return handler(command, ctx)
151+
}
152+
}
153+
154+
/**
155+
* 装饰器组合
156+
*/
157+
export function compose<T>(
158+
...decorators: ((handler: CommandHandler<T>, deps: CommandDependencies) => CommandHandler<T>)[]
159+
): (handler: CommandHandler<T>, deps: CommandDependencies) => CommandHandler<T> {
160+
return (handler, deps) =>
161+
decorators.reduceRight(
162+
(h, decorator) => decorator(h, deps),
163+
handler
164+
)
165+
}
166+
167+
/**
168+
* 错误处理辅助函数
169+
*/
170+
function handleError(error: unknown): CommandResult {
171+
if (error instanceof ValidationError) {
172+
return {
173+
status: 'failed',
174+
error: {
175+
code: error.code as any,
176+
message: error.message
177+
}
178+
}
179+
}
180+
181+
if (error instanceof ModeError) {
182+
return {
183+
status: 'failed',
184+
error: {
185+
code: error.code as any,
186+
message: error.message
187+
}
188+
}
189+
}
190+
191+
if (error instanceof Error) {
192+
return {
193+
status: 'failed',
194+
error: {
195+
code: 'RUNTIME_ERROR' as any,
196+
message: error.message
197+
}
198+
}
199+
}
200+
201+
return {
202+
status: 'failed',
203+
error: {
204+
code: 'UNKNOWN_ERROR' as any,
205+
message: 'An unknown error occurred'
206+
}
207+
}
208+
}
209+
210+
/**
211+
* 常用验证器
212+
*/
213+
export const Validators = {
214+
/**
215+
* 验证 payload 存在
216+
*/
217+
required: <T>(fieldName = 'payload'): Validator<T> => (payload) => {
218+
if (!payload) {
219+
return { valid: false, error: `${fieldName} is required` }
220+
}
221+
return { valid: true }
222+
},
223+
224+
/**
225+
* 验证字符串字段
226+
*/
227+
string: <T>(field: keyof T): Validator<T> => (payload) => {
228+
const value = payload[field]
229+
if (!value || typeof value !== 'string' || !value.trim()) {
230+
return { valid: false, error: `${String(field)} is required and must be a non-empty string` }
231+
}
232+
return { valid: true }
233+
},
234+
235+
/**
236+
* 验证数字字段
237+
*/
238+
number: <T>(field: keyof T, min?: number, max?: number): Validator<T> => (payload) => {
239+
const value = (payload as any)[field]
240+
if (typeof value !== 'number') {
241+
return { valid: false, error: `${String(field)} must be a number` }
242+
}
243+
if (min !== undefined && value < min) {
244+
return { valid: false, error: `${String(field)} must be >= ${min}` }
245+
}
246+
if (max !== undefined && value > max) {
247+
return { valid: false, error: `${String(field)} must be <= ${max}` }
248+
}
249+
return { valid: true }
250+
},
251+
252+
/**
253+
* 组合多个验证器
254+
*/
255+
all: <T>(...validators: Validator<T>[]): Validator<T> => (payload) => {
256+
for (const validator of validators) {
257+
const result = validator(payload)
258+
if (!result.valid) {
259+
return result
260+
}
261+
}
262+
return { valid: true }
263+
}
264+
}

0 commit comments

Comments
 (0)