127 lines
3.6 KiB
TypeScript
127 lines
3.6 KiB
TypeScript
|
|
/**
|
||
|
|
* Hypermodern Zod validation setup - automatic runtime validation without explicit .parse() calls
|
||
|
|
* Follows the same philosophy as typeguard for Python
|
||
|
|
*/
|
||
|
|
|
||
|
|
import { z } from 'zod';
|
||
|
|
|
||
|
|
// Global schemas for automatic validation
|
||
|
|
export const PlaylistSchema = z.object({
|
||
|
|
id: z.string().uuid(),
|
||
|
|
name: z.string().min(1),
|
||
|
|
description: z.string().optional(),
|
||
|
|
tracks: z.array(z.string()).default([]),
|
||
|
|
});
|
||
|
|
|
||
|
|
export const UserSchema = z.object({
|
||
|
|
id: z.number(),
|
||
|
|
username: z.string().min(1),
|
||
|
|
email: z.string().email(),
|
||
|
|
preferences: z
|
||
|
|
.object({
|
||
|
|
theme: z.enum(['light', 'dark']).default('light'),
|
||
|
|
notifications: z.boolean().default(true),
|
||
|
|
})
|
||
|
|
.default({}),
|
||
|
|
});
|
||
|
|
|
||
|
|
// Inferred types (no manual type definitions needed)
|
||
|
|
export type Playlist = z.infer<typeof PlaylistSchema>;
|
||
|
|
export type User = z.infer<typeof UserSchema>;
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Global schema registry for automatic validation
|
||
|
|
*/
|
||
|
|
class SchemaRegistry {
|
||
|
|
private static schemas = new Map<string, z.ZodTypeAny>([
|
||
|
|
['Playlist', PlaylistSchema as z.ZodTypeAny],
|
||
|
|
['User', UserSchema as z.ZodTypeAny],
|
||
|
|
]);
|
||
|
|
|
||
|
|
static register<T extends z.ZodTypeAny>(name: string, schema: T): void {
|
||
|
|
this.schemas.set(name, schema as z.ZodTypeAny);
|
||
|
|
}
|
||
|
|
|
||
|
|
static get(name: string): z.ZodTypeAny | undefined {
|
||
|
|
return this.schemas.get(name);
|
||
|
|
}
|
||
|
|
|
||
|
|
static validate<T>(typeName: string, data: unknown): T {
|
||
|
|
const schema = this.schemas.get(typeName);
|
||
|
|
if (!schema) {
|
||
|
|
throw new Error(`No schema registered for type: ${typeName}`);
|
||
|
|
}
|
||
|
|
return schema.parse(data) as T;
|
||
|
|
}
|
||
|
|
|
||
|
|
static safeValidate<T>(
|
||
|
|
typeName: string,
|
||
|
|
data: unknown
|
||
|
|
): { success: true; data: T } | { success: false; error: z.ZodError } {
|
||
|
|
const schema = this.schemas.get(typeName);
|
||
|
|
if (!schema) {
|
||
|
|
return {
|
||
|
|
success: false,
|
||
|
|
error: new z.ZodError([
|
||
|
|
{
|
||
|
|
code: 'custom',
|
||
|
|
message: `No schema registered for type: ${typeName}`,
|
||
|
|
path: [],
|
||
|
|
},
|
||
|
|
]),
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
const result = schema.safeParse(data);
|
||
|
|
return result.success
|
||
|
|
? { success: true, data: result.data as T }
|
||
|
|
: { success: false, error: result.error };
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Automatic validation wrapper - works like typeguard for Python
|
||
|
|
* In development/test: validates automatically
|
||
|
|
* In production: can be stripped by build process
|
||
|
|
*/
|
||
|
|
export class AutoValidator {
|
||
|
|
static validate<T>(typeName: string, data: unknown): T {
|
||
|
|
// Check if auto-validation is enabled (development/test mode)
|
||
|
|
if (typeof globalThis !== 'undefined' && (globalThis as any).__AUTO_VALIDATE__) {
|
||
|
|
return SchemaRegistry.validate<T>(typeName, data);
|
||
|
|
}
|
||
|
|
|
||
|
|
// In production, validation is skipped for performance
|
||
|
|
return data as T;
|
||
|
|
}
|
||
|
|
|
||
|
|
static safeValidate<T>(
|
||
|
|
typeName: string,
|
||
|
|
data: unknown
|
||
|
|
): { success: true; data: T } | { success: false; error: z.ZodError } {
|
||
|
|
if (typeof globalThis !== 'undefined' && (globalThis as any).__AUTO_VALIDATE__) {
|
||
|
|
return SchemaRegistry.safeValidate<T>(typeName, data);
|
||
|
|
}
|
||
|
|
|
||
|
|
// In production, assume data is valid
|
||
|
|
return { success: true, data: data as T };
|
||
|
|
}
|
||
|
|
|
||
|
|
static register<T extends z.ZodTypeAny>(name: string, schema: T): void {
|
||
|
|
SchemaRegistry.register(name, schema);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Enable automatic validation in development/test environments
|
||
|
|
declare global {
|
||
|
|
var __AUTO_VALIDATE__: boolean;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Check environment and log status
|
||
|
|
const isDev = typeof globalThis !== 'undefined' && (globalThis as any).__AUTO_VALIDATE__;
|
||
|
|
if (isDev) {
|
||
|
|
console.log('🛡️ Hypermodern Zod validation enabled (development/test mode)');
|
||
|
|
} else {
|
||
|
|
console.log('🏃♂️ Production mode - Zod validation optimized away');
|
||
|
|
}
|