refactor: prompt validation

This commit is contained in:
hypercross 2026-04-04 21:38:07 +08:00
parent af9254603a
commit 9e2947a8d6
4 changed files with 27 additions and 37 deletions

View File

@ -10,6 +10,7 @@ import {
registerCommand
} from "@/utils/command";
import type { GameModule } from './game-host';
import {PromptValidator} from "@/utils/command/command-runner";
export interface IGameContext<TState extends Record<string, unknown> = {} > {
get value(): TState;
@ -17,7 +18,7 @@ export interface IGameContext<TState extends Record<string, unknown> = {} > {
produceAsync(fn: (draft: TState) => void): Promise<void>;
run<T>(input: string): Promise<CommandResult<T>>;
runParsed<T>(command: Command): Promise<CommandResult<T>>;
prompt(schema: CommandSchema | string, validator?: (command: Command) => string | null, currentPlayer?: string | null): Promise<Command>;
prompt<T>(schema: CommandSchema | string, validator: PromptValidator<T>, currentPlayer?: string | null): Promise<T>;
addInterruption(promise: Promise<void>): void;
// test only
@ -82,32 +83,6 @@ export function createGameCommandRegistry<TState extends Record<string, unknown>
return createCommandRegistry<IGameContext<TState>>();
}
type CmdFunc<TState extends Record<string, unknown> = {}> = (this: IGameContext<TState>, ...args: any[]) => Promise<unknown>;
export function registerGameCommand<TState extends Record<string, unknown> = {}, TFunc extends CmdFunc<TState> = CmdFunc<TState>>(
registry: CommandRegistry<IGameContext<TState>>,
schema: CommandSchema | string,
run: TFunc
) {
const parsedSchema = typeof schema === 'string' ? parseCommandSchema(schema) : schema;
registerCommand(registry, {
schema: parsedSchema,
async run(this: CommandRunnerContext<IGameContext<TState>>, command: Command){
const params = command.params;
return await run.call(this.context, ...params);
},
});
return function(game: IGameContext<TState>, ...args: Parameters<TFunc>){
return game.runParsed({
options: {},
params: args,
flags: {},
name: parsedSchema.name,
});
}
}
export { GameHost, createGameHost } from './game-host';
export type { GameHostStatus, GameModule } from './game-host';

View File

@ -5,7 +5,7 @@
// Core types
export type { IGameContext } from './core/game';
export { createGameContext, createGameCommandRegistry, registerGameCommand } from './core/game';
export { createGameContext, createGameCommandRegistry } from './core/game';
export type { GameHost, GameHostStatus, GameModule } from './core/game';
export { createGameHost, createGameModule } from './core/game';

View File

@ -1,5 +1,12 @@
import type { Command, CommandSchema } from './types';
import type {CommandResult, CommandRunner, CommandRunnerContext, CommandRunnerEvents, PromptEvent} from './command-runner';
import type {
CommandResult,
CommandRunner,
CommandRunnerContext,
CommandRunnerEvents,
PromptEvent,
PromptValidator
} from './command-runner';
import { parseCommand } from './command-parse';
import { applyCommandSchema } from './command-validate';
import { parseCommandSchema } from './schema-parse';
@ -129,11 +136,11 @@ export function createCommandRunnerContext<TContext>(
}
};
const prompt = (
const prompt = <T>(
schema: CommandSchema | string,
validator?: (command: Command) => string | null,
validator: PromptValidator<T>,
currentPlayer?: string | null
): Promise<Command> => {
): Promise<T> => {
const resolvedSchema = typeof schema === 'string' ? parseCommandSchema(schema) : schema;
return new Promise((resolve, reject) => {
const tryCommit = (commandOrInput: Command | string) => {
@ -142,10 +149,16 @@ export function createCommandRunnerContext<TContext>(
if (!schemaResult.valid) {
return schemaResult.errors.join('; ');
}
const error = validator?.(schemaResult.command);
if (error) return error;
resolve(schemaResult.command);
try{
const result = validator(schemaResult.command);
resolve(result);
return null;
}catch(e){
if(e instanceof Error)
return e.message;
else
throw e;
}
};
const cancel = (reason?: string) => {
activePrompt = null;

View File

@ -31,11 +31,13 @@ export type CommandResult<T=unknown> = {
error: string;
}
export type PromptValidator<T> = (command: Command) => T;
export type CommandRunnerContext<TContext> = {
context: TContext;
run: <T=unknown>(input: string) => Promise<CommandResult<T>>;
runParsed: <T=unknown>(command: Command) => Promise<CommandResult<T>>;
prompt: (schema: CommandSchema | string, validator?: (command: Command) => string | null, currentPlayer?: string | null) => Promise<Command>;
prompt: <T>(schema: CommandSchema | string, validator: PromptValidator<T>, currentPlayer?: string | null) => Promise<T>;
on: <T extends keyof CommandRunnerEvents>(event: T, listener: (e: CommandRunnerEvents[T]) => void) => void;
off: <T extends keyof CommandRunnerEvents>(event: T, listener: (e: CommandRunnerEvents[T]) => void) => void;
};