feat(shared): add quiz session Zod schemas
Add the shared schemas for the quiz request/response cycle, defining the contract between the API and the frontend. - Reorganise packages/shared: move schemas into a schemas/ subdirectory grouped by domain. Delete the old flat schema.ts. - Add AnswerOption, GameQuestion, GameSession, AnswerSubmission, and AnswerResult alongside the existing GameRequest. - optionId is an integer 0-3 (positional, shuffled at session-build time so position carries no information). - questionId and sessionId are UUIDs (globally unique, opaque, natural keys for Valkey storage later). - gloss is rather than optional, for a predictable shape on the frontend. - options array enforced to exactly 4 elements at the schema level.
This commit is contained in:
parent
2bcf9d7a97
commit
ce6dc4fa32
3 changed files with 58 additions and 19 deletions
|
|
@ -1,2 +1,2 @@
|
|||
export * from "./constants.js";
|
||||
export * from "./schema.js";
|
||||
export * from "./schemas/game.js";
|
||||
|
|
|
|||
|
|
@ -1,18 +0,0 @@
|
|||
import * as z from "zod";
|
||||
|
||||
import {
|
||||
SUPPORTED_LANGUAGE_CODES,
|
||||
SUPPORTED_POS,
|
||||
GAME_ROUNDS,
|
||||
DIFFICULTY_LEVELS,
|
||||
} from "./constants.js";
|
||||
|
||||
export const GameRequestSchema = z.object({
|
||||
source_language: z.enum(SUPPORTED_LANGUAGE_CODES),
|
||||
target_language: z.enum(SUPPORTED_LANGUAGE_CODES),
|
||||
pos: z.enum(SUPPORTED_POS),
|
||||
difficulty: z.enum(DIFFICULTY_LEVELS),
|
||||
rounds: z.enum(GAME_ROUNDS),
|
||||
});
|
||||
|
||||
export type GameRequestType = z.infer<typeof GameRequestSchema>;
|
||||
57
packages/shared/src/schemas/game.ts
Normal file
57
packages/shared/src/schemas/game.ts
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
import * as z from "zod";
|
||||
|
||||
import {
|
||||
SUPPORTED_LANGUAGE_CODES,
|
||||
SUPPORTED_POS,
|
||||
GAME_ROUNDS,
|
||||
DIFFICULTY_LEVELS,
|
||||
} from "../constants.js";
|
||||
|
||||
export const GameRequestSchema = z.object({
|
||||
source_language: z.enum(SUPPORTED_LANGUAGE_CODES),
|
||||
target_language: z.enum(SUPPORTED_LANGUAGE_CODES),
|
||||
pos: z.enum(SUPPORTED_POS),
|
||||
difficulty: z.enum(DIFFICULTY_LEVELS),
|
||||
rounds: z.enum(GAME_ROUNDS),
|
||||
});
|
||||
|
||||
export type GameRequest = z.infer<typeof GameRequestSchema>;
|
||||
|
||||
export const AnswerOptionSchema = z.object({
|
||||
optionId: z.number().int().min(0).max(3),
|
||||
text: z.string().min(1),
|
||||
});
|
||||
|
||||
export type AnswerOption = z.infer<typeof AnswerOptionSchema>;
|
||||
|
||||
export const GameQuestionSchema = z.object({
|
||||
questionId: z.uuid(),
|
||||
prompt: z.string().min(1),
|
||||
gloss: z.string().min(1).nullable(),
|
||||
options: z.array(AnswerOptionSchema).length(4),
|
||||
});
|
||||
|
||||
export type GameQuestion = z.infer<typeof GameQuestionSchema>;
|
||||
|
||||
export const GameSessionSchema = z.object({
|
||||
sessionId: z.uuid(),
|
||||
questions: z.array(GameQuestionSchema),
|
||||
});
|
||||
|
||||
export type GameSession = z.infer<typeof GameSessionSchema>;
|
||||
|
||||
export const AnswerSubmissionSchema = z.object({
|
||||
sessionId: z.uuid(),
|
||||
questionId: z.uuid(),
|
||||
selectedOptionId: z.number().int().min(0).max(3),
|
||||
});
|
||||
|
||||
export type AnswerSubmission = z.infer<typeof AnswerSubmissionSchema>;
|
||||
|
||||
export const AnswerResultSchema = z.object({
|
||||
questionId: z.uuid(),
|
||||
isCorrect: z.boolean(),
|
||||
correctOptionId: z.number().int().min(0).max(3),
|
||||
});
|
||||
|
||||
export type AnswerResult = z.infer<typeof AnswerResultSchema>;
|
||||
Loading…
Add table
Add a link
Reference in a new issue