feat(shared): add domain types and zod schemas

This commit is contained in:
2026-05-20 20:33:53 +02:00
parent 4d0a5aee66
commit 59261b3bab
3 changed files with 120 additions and 1 deletions

View File

@@ -1 +1,2 @@
export {};
export * from './types.js';
export * from './schemas.js';

View File

@@ -0,0 +1,45 @@
import { z } from 'zod';
export const lessonCreateSchema = z.object({
parentId: z.number().int().nullable().optional(),
name: z.string().min(1).max(200),
description: z.string().max(2000).optional().nullable(),
bidirectional: z.boolean().optional(),
});
export const lessonUpdateSchema = lessonCreateSchema.partial();
export const lessonMoveSchema = z.object({
parentId: z.number().int().nullable(),
position: z.number().int().min(0),
});
export const cardCreateSchema = z.object({
question: z.string().min(1).max(2000),
answer: z.string().min(1).max(2000),
hint: z.string().max(2000).optional().nullable(),
});
export const cardUpdateSchema = cardCreateSchema.partial();
export const sessionStartSchema = z.object({
lessonId: z.number().int().positive(),
maxCards: z.number().int().min(1).max(500).nullable().optional(),
shuffle: z.boolean().optional(),
direction: z.enum(['forward', 'backward', 'both']).optional(),
});
export const attemptCreateSchema = z.object({
cardId: z.number().int().positive(),
direction: z.enum(['forward', 'backward']),
result: z.enum(['correct', 'incorrect']),
timeToAnswerMs: z.number().int().min(0).nullable().optional(),
});
export type LessonCreateInput = z.infer<typeof lessonCreateSchema>;
export type LessonUpdateInput = z.infer<typeof lessonUpdateSchema>;
export type LessonMoveInput = z.infer<typeof lessonMoveSchema>;
export type CardCreateInput = z.infer<typeof cardCreateSchema>;
export type CardUpdateInput = z.infer<typeof cardUpdateSchema>;
export type SessionStartInput = z.infer<typeof sessionStartSchema>;
export type AttemptCreateInput = z.infer<typeof attemptCreateSchema>;

View File

@@ -0,0 +1,73 @@
export type Direction = 'forward' | 'backward';
export type AttemptResult = 'correct' | 'incorrect';
export type SessionStatus = 'active' | 'completed' | 'abandoned';
export interface Lesson {
id: number;
parentId: number | null;
name: string;
description: string | null;
position: number;
bidirectional: boolean;
createdAt: number;
updatedAt: number;
}
export interface LessonTreeNode extends Lesson {
children: LessonTreeNode[];
cardCount: number;
}
export interface Card {
id: number;
lessonId: number;
question: string;
answer: string;
hint: string | null;
position: number;
createdAt: number;
updatedAt: number;
}
export interface CardProgress {
cardId: number;
direction: Direction;
box: number;
correctCount: number;
incorrectCount: number;
lastShownAt: number | null;
nextDueAt: number;
}
export interface SessionRow {
id: number;
lessonId: number;
startedAt: number;
endedAt: number | null;
durationSeconds: number | null;
cardsShown: number;
cardsCorrect: number;
cardsIncorrect: number;
status: SessionStatus;
}
export interface Attempt {
id: number;
sessionId: number;
cardId: number;
direction: Direction;
shownAt: number;
result: AttemptResult;
timeToAnswerMs: number | null;
}
export interface QueueItem {
cardId: number;
direction: Direction;
}
export interface SessionSettings {
maxCards: number | null;
shuffle: boolean;
direction: 'forward' | 'backward' | 'both';
}