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 "./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