aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--docs/dev/roadmap.md2
-rw-r--r--pkgs/shared/package.json5
-rw-r--r--pkgs/shared/src/index.ts1
-rw-r--r--pkgs/shared/src/schemas/index.ts134
-rw-r--r--pnpm-lock.yaml11
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: {}