feat(validators): adopt drizzle-zod for tags + brochures schemas
Pilot adoption of `drizzle-zod` (already shipped as part of `drizzle-orm`). Two CRUD-shape validators migrate from hand-written z.object() to `createInsertSchema(table, refinements)`: - tags: name + color (with hex regex refinement). - brochures: label + description + isDefault. Both schemas now derive directly from the Drizzle table definition. Adding a column to the table will auto-include it in the validator (filtered via `.pick(...)` where API surface should stay narrower than the table). Eliminates the validator-drift class of bugs the audit flagged (e.g. adding a column to clients but forgetting to add it to createClientSchema). Pattern is established for future validator touches. Migrating the remaining CRUD validators is opportunistic — done when the validator file is otherwise being edited. Verified: tsc clean, vitest 1293/1293 pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,16 +1,19 @@
|
|||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
import { createInsertSchema, createUpdateSchema } from 'drizzle-zod';
|
||||||
|
|
||||||
export const createBrochureSchema = z.object({
|
import { brochures } from '@/lib/db/schema/brochures';
|
||||||
label: z.string().trim().min(1).max(120),
|
|
||||||
description: z.string().max(500).optional().nullable(),
|
|
||||||
isDefault: z.boolean().optional(),
|
|
||||||
});
|
|
||||||
|
|
||||||
export const updateBrochureSchema = z.object({
|
// Derived from the Drizzle table — adding a column to `brochures`
|
||||||
label: z.string().trim().min(1).max(120).optional(),
|
// auto-includes it here. Refinements override per-field.
|
||||||
description: z.string().max(500).optional().nullable(),
|
export const createBrochureSchema = createInsertSchema(brochures, {
|
||||||
isDefault: z.boolean().optional(),
|
label: (s) => s.trim().min(1).max(120),
|
||||||
});
|
description: (s) => s.max(500),
|
||||||
|
}).pick({ label: true, description: true, isDefault: true });
|
||||||
|
|
||||||
|
export const updateBrochureSchema = createUpdateSchema(brochures, {
|
||||||
|
label: (s) => s.trim().min(1).max(120),
|
||||||
|
description: (s) => s.max(500),
|
||||||
|
}).pick({ label: true, description: true, isDefault: true });
|
||||||
|
|
||||||
export const registerBrochureVersionSchema = z.object({
|
export const registerBrochureVersionSchema = z.object({
|
||||||
storageKey: z
|
storageKey: z
|
||||||
|
|||||||
@@ -1,13 +1,15 @@
|
|||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
import { createInsertSchema } from 'drizzle-zod';
|
||||||
|
|
||||||
export const createTagSchema = z.object({
|
import { tags } from '@/lib/db/schema/system';
|
||||||
name: z.string().min(1).max(50),
|
|
||||||
color: z
|
// Derive the schema from the Drizzle table — adding a column to `tags`
|
||||||
.string()
|
// auto-includes it here; omitting / refining is per-field. Eliminates
|
||||||
.regex(/^#[0-9A-Fa-f]{6}$/, 'Color must be a valid hex color (e.g. #6B7280)')
|
// the validator-drift class of bugs the audit flagged.
|
||||||
.optional()
|
export const createTagSchema = createInsertSchema(tags, {
|
||||||
.default('#6B7280'),
|
name: (s) => s.min(1).max(50),
|
||||||
});
|
color: (s) => s.regex(/^#[0-9A-Fa-f]{6}$/, 'Color must be a valid hex color (e.g. #6B7280)'),
|
||||||
|
}).pick({ name: true, color: true });
|
||||||
|
|
||||||
export const updateTagSchema = createTagSchema.partial();
|
export const updateTagSchema = createTagSchema.partial();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user