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 registerCommand
} from "@/utils/command"; } from "@/utils/command";
import type { GameModule } from './game-host'; import type { GameModule } from './game-host';
import {PromptValidator} from "@/utils/command/command-runner";
export interface IGameContext<TState extends Record<string, unknown> = {} > { export interface IGameContext<TState extends Record<string, unknown> = {} > {
get value(): TState; get value(): TState;
@ -17,7 +18,7 @@ export interface IGameContext<TState extends Record<string, unknown> = {} > {
produceAsync(fn: (draft: TState) => void): Promise<void>; produceAsync(fn: (draft: TState) => void): Promise<void>;
run<T>(input: string): Promise<CommandResult<T>>; run<T>(input: string): Promise<CommandResult<T>>;
runParsed<T>(command: Command): 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; addInterruption(promise: Promise<void>): void;
// test only // test only
@ -82,32 +83,6 @@ export function createGameCommandRegistry<TState extends Record<string, unknown>
return createCommandRegistry<IGameContext<TState>>(); 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 { GameHost, createGameHost } from './game-host';
export type { GameHostStatus, GameModule } from './game-host'; export type { GameHostStatus, GameModule } from './game-host';

View File

@ -5,7 +5,7 @@
// Core types // Core types
export type { IGameContext } from './core/game'; 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 type { GameHost, GameHostStatus, GameModule } from './core/game';
export { createGameHost, createGameModule } from './core/game'; export { createGameHost, createGameModule } from './core/game';

View File

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

View File

@ -31,11 +31,13 @@ export type CommandResult<T=unknown> = {
error: string; error: string;
} }
export type PromptValidator<T> = (command: Command) => T;
export type CommandRunnerContext<TContext> = { export type CommandRunnerContext<TContext> = {
context: TContext; context: TContext;
run: <T=unknown>(input: string) => Promise<CommandResult<T>>; run: <T=unknown>(input: string) => Promise<CommandResult<T>>;
runParsed: <T=unknown>(command: Command) => 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; 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; off: <T extends keyof CommandRunnerEvents>(event: T, listener: (e: CommandRunnerEvents[T]) => void) => void;
}; };