diff options
| -rw-r--r-- | docs/dev/roadmap.md | 2 | ||||
| -rw-r--r-- | pkgs/shared/package.json | 5 | ||||
| -rw-r--r-- | pkgs/shared/src/index.ts | 1 | ||||
| -rw-r--r-- | pkgs/shared/src/schemas/index.ts | 134 | ||||
| -rw-r--r-- | pnpm-lock.yaml | 11 |
5 files changed, 150 insertions, 3 deletions
diff --git a/docs/dev/roadmap.md b/docs/dev/roadmap.md index c4846ea..57fdaed 100644 --- a/docs/dev/roadmap.md +++ b/docs/dev/roadmap.md @@ -24,7 +24,7 @@ ### Shared Package - [x] Create pkgs/shared - [x] Define types (User, Deck, Card, ReviewLog) -- [ ] Zod validation schemas +- [x] Zod validation schemas ### Authentication - [ ] User registration endpoint diff --git a/pkgs/shared/package.json b/pkgs/shared/package.json index 26c95b7..d208456 100644 --- a/pkgs/shared/package.json +++ b/pkgs/shared/package.json @@ -9,5 +9,8 @@ "author": "nsfisis", "license": "MIT", "packageManager": "pnpm@10.23.0", - "type": "module" + "type": "module", + "dependencies": { + "zod": "^4.1.13" + } } diff --git a/pkgs/shared/src/index.ts b/pkgs/shared/src/index.ts index 2f88e30..0ca6112 100644 --- a/pkgs/shared/src/index.ts +++ b/pkgs/shared/src/index.ts @@ -1 +1,2 @@ +export * from "./schemas/index.js"; export * from "./types/index.js"; 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>; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 33205b7..4f13349 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -45,7 +45,11 @@ importers: specifier: ^4.0.14 version: 4.0.14(@types/node@24.10.1) - pkgs/shared: {} + pkgs/shared: + dependencies: + zod: + specifier: ^4.1.13 + version: 4.1.13 packages: @@ -944,6 +948,9 @@ packages: resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} engines: {node: '>=0.4'} + zod@4.1.13: + resolution: {integrity: sha512-AvvthqfqrAhNH9dnfmrfKzX5upOdjUVJYFqNSlkmGf64gRaTzlPwz99IHYnVs28qYAybvAlBV+H7pn0saFY4Ig==} + snapshots: '@biomejs/biome@2.3.8': @@ -1559,3 +1566,5 @@ snapshots: stackback: 0.0.2 xtend@4.0.2: {} + + zod@4.1.13: {} |
