diff options
| author | nsfisis <nsfisis@gmail.com> | 2025-12-03 05:18:11 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2025-12-04 23:26:23 +0900 |
| commit | 7e75e0fa8f26a7890c210c67e4474778811b15bc (patch) | |
| tree | 3e8b85702e57990932897f530afa79d9dc8e1742 /pkgs/shared/src/schemas | |
| parent | 4ca12275c122dd84e489f8c15ee88a840eedb2cf (diff) | |
| download | kioku-7e75e0fa8f26a7890c210c67e4474778811b15bc.tar.gz kioku-7e75e0fa8f26a7890c210c67e4474778811b15bc.tar.zst kioku-7e75e0fa8f26a7890c210c67e4474778811b15bc.zip | |
feat(shared): add Zod validation schemas
Add Zod validation schemas for all data models (User, Deck, Card,
ReviewLog) along with input schemas for API endpoints (create, update,
login, submit review).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Diffstat (limited to 'pkgs/shared/src/schemas')
| -rw-r--r-- | pkgs/shared/src/schemas/index.ts | 134 |
1 files changed, 134 insertions, 0 deletions
diff --git a/pkgs/shared/src/schemas/index.ts b/pkgs/shared/src/schemas/index.ts new file mode 100644 index 0000000..c211381 --- /dev/null +++ b/pkgs/shared/src/schemas/index.ts @@ -0,0 +1,134 @@ +import { z } from "zod"; + +// Card states for FSRS algorithm +export const cardStateSchema = z.union([ + z.literal(0), // New + z.literal(1), // Learning + z.literal(2), // Review + z.literal(3), // Relearning +]); + +// Rating values for reviews +export const ratingSchema = z.union([ + z.literal(1), // Again + z.literal(2), // Hard + z.literal(3), // Good + z.literal(4), // Easy +]); + +// User schema +export const userSchema = z.object({ + id: z.string().uuid(), + username: z.string().min(1).max(255), + passwordHash: z.string(), + createdAt: z.coerce.date(), + updatedAt: z.coerce.date(), +}); + +// User creation input schema +export const createUserSchema = z.object({ + username: z.string().min(1).max(255), + password: z.string().min(8).max(255), +}); + +// Login input schema +export const loginSchema = z.object({ + username: z.string().min(1), + password: z.string().min(1), +}); + +// Deck schema +export const deckSchema = z.object({ + id: z.string().uuid(), + userId: z.string().uuid(), + name: z.string().min(1).max(255), + description: z.string().max(1000).nullable(), + newCardsPerDay: z.number().int().min(0).default(20), + createdAt: z.coerce.date(), + updatedAt: z.coerce.date(), + deletedAt: z.coerce.date().nullable(), + syncVersion: z.number().int().min(0), +}); + +// Deck creation input schema +export const createDeckSchema = z.object({ + name: z.string().min(1).max(255), + description: z.string().max(1000).nullable().optional(), + newCardsPerDay: z.number().int().min(0).default(20), +}); + +// Deck update input schema +export const updateDeckSchema = z.object({ + name: z.string().min(1).max(255).optional(), + description: z.string().max(1000).nullable().optional(), + newCardsPerDay: z.number().int().min(0).optional(), +}); + +// Card schema +export const cardSchema = z.object({ + id: z.string().uuid(), + deckId: z.string().uuid(), + front: z.string().min(1), + back: z.string().min(1), + + // FSRS fields + state: cardStateSchema, + due: z.coerce.date(), + stability: z.number().min(0), + difficulty: z.number().min(0).max(10), + elapsedDays: z.number().int().min(0), + scheduledDays: z.number().int().min(0), + reps: z.number().int().min(0), + lapses: z.number().int().min(0), + lastReview: z.coerce.date().nullable(), + + createdAt: z.coerce.date(), + updatedAt: z.coerce.date(), + deletedAt: z.coerce.date().nullable(), + syncVersion: z.number().int().min(0), +}); + +// Card creation input schema +export const createCardSchema = z.object({ + front: z.string().min(1), + back: z.string().min(1), +}); + +// Card update input schema +export const updateCardSchema = z.object({ + front: z.string().min(1).optional(), + back: z.string().min(1).optional(), +}); + +// ReviewLog schema +export const reviewLogSchema = z.object({ + id: z.string().uuid(), + cardId: z.string().uuid(), + userId: z.string().uuid(), + rating: ratingSchema, + state: cardStateSchema, + scheduledDays: z.number().int().min(0), + elapsedDays: z.number().int().min(0), + reviewedAt: z.coerce.date(), + durationMs: z.number().int().min(0).nullable(), + syncVersion: z.number().int().min(0), +}); + +// Submit review input schema +export const submitReviewSchema = z.object({ + rating: ratingSchema, + durationMs: z.number().int().min(0).nullable().optional(), +}); + +// Inferred types from schemas +export type UserSchema = z.infer<typeof userSchema>; +export type CreateUserSchema = z.infer<typeof createUserSchema>; +export type LoginSchema = z.infer<typeof loginSchema>; +export type DeckSchema = z.infer<typeof deckSchema>; +export type CreateDeckSchema = z.infer<typeof createDeckSchema>; +export type UpdateDeckSchema = z.infer<typeof updateDeckSchema>; +export type CardSchema = z.infer<typeof cardSchema>; +export type CreateCardSchema = z.infer<typeof createCardSchema>; +export type UpdateCardSchema = z.infer<typeof updateCardSchema>; +export type ReviewLogSchema = z.infer<typeof reviewLogSchema>; +export type SubmitReviewSchema = z.infer<typeof submitReviewSchema>; |
