File size: 2,561 Bytes
6d643ca
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
import { z } from 'zod';

import { VALIDATION_LIMITS } from './constants';

/**
 * Validation schemas using Zod
 *
 * This module contains all validation schemas for forms and API inputs.
 * Schemas provide both runtime validation and TypeScript type generation.
 */

// Common validation patterns
const requiredString = z.string().min(1, 'This field is required');

// Closet item validation
export const closetItemSchema = z.object({
  name: requiredString.max(
    VALIDATION_LIMITS.NAME_MAX_LENGTH,
    `Name must be less than ${VALIDATION_LIMITS.NAME_MAX_LENGTH} characters`
  ),
  brand: requiredString.max(
    VALIDATION_LIMITS.BRAND_MAX_LENGTH,
    `Brand must be less than ${VALIDATION_LIMITS.BRAND_MAX_LENGTH} characters`
  ),
  category: requiredString,
  imageFile: z.instanceof(File).optional(),
  imageUrl: z.string().url().optional(),
});

export type ClosetItemInput = z.infer<typeof closetItemSchema>;

// Search filters validation (simplified to only search query, page, limit)
export const searchFiltersSchema = z.object({
  query: z
    .string()
    .max(VALIDATION_LIMITS.SEARCH_QUERY_MAX_LENGTH, 'Search query too long')
    .optional(),
  page: z.number().min(1).optional(),
  limit: z
    .number()
    .min(1)
    .max(VALIDATION_LIMITS.MAX_PAGINATION_LIMIT)
    .optional(),
});

export type SearchFiltersInput = z.infer<typeof searchFiltersSchema>;

// API error validation
export const apiErrorSchema = z.object({
  message: z.string(),
  code: z.string().optional(),
  details: z.record(z.unknown()).optional(),
});

export type ApiError = z.infer<typeof apiErrorSchema>;

// Pagination validation
export const paginationSchema = z.object({
  page: z.number().min(1),
  limit: z.number().min(1).max(VALIDATION_LIMITS.MAX_PAGINATION_LIMIT),
  total: z.number().min(0),
  totalPages: z.number().min(0),
});

export type PaginationInfo = z.infer<typeof paginationSchema>;

// Generic list response validation
export const createListResponseSchema = <T extends z.ZodTypeAny>(
  itemSchema: T
) =>
  z.object({
    data: z.array(itemSchema),
    pagination: paginationSchema.optional(),
    meta: z.record(z.unknown()).optional(),
  });

// Validation helpers
export const validateInput = <T extends z.ZodTypeAny>(
  schema: T,
  data: unknown
): z.infer<T> => {
  const result = schema.safeParse(data);
  if (!result.success) {
    throw new Error(
      `Validation failed: ${result.error.issues.map((i) => i.message).join(', ')}`
    );
  }
  // eslint-disable-next-line @typescript-eslint/no-unsafe-return
  return result.data;
};