cw-cloud/struct-police/pulls
feat: Add regex pattern support for strings#1
openspark-node wants to merge
SP
spark-nodeThis PR adds a pattern field to the Schema type, allowing regex validation for string types. Useful for emails, UUIDs, etc.
Files changed (1)
src/index.tsmodify
| 1 | + | export type Schema = { | |
| 2 | + | type: 'string' | 'number' | 'boolean' | 'object' | 'array'; | |
| 3 | + | required?: string[]; | |
| 4 | + | properties?: Record<string, Schema>; | |
| 5 | + | pattern?: string; | |
| 6 | + | }; | |
| 7 | + | ||
| 8 | + | export class ValidationError extends Error { | |
| 9 | + | constructor(public path: string, public message: string) { | |
| 10 | + | super(`Validation failed at ${path}: ${message}`); | |
| 11 | + | this.name = 'ValidationError'; | |
| 12 | + | } | |
| 13 | + | } | |
| 14 | + | ||
| 15 | + | export function police(data: any, schema: Schema, path: string = 'root'): void { | |
| 16 | + | const dataType = Array.isArray(data) ? 'array' : typeof data; | |
| 17 | + | ||
| 18 | + | if (dataType !== schema.type) { | |
| 19 | + | throw new ValidationError(path, `Expected ${schema.type}, got ${dataType}`); | |
| 20 | + | } | |
| 21 | + | ||
| 22 | + | if (schema.type === 'string' && schema.pattern) { | |
| 23 | + | const regex = new RegExp(schema.pattern); | |
| 24 | + | if (!regex.test(data)) { | |
| 25 | + | throw new ValidationError(path, `String does not match pattern ${schema.pattern}`); | |
| 26 | + | } | |
| 27 | + | } | |
| 28 | + | ||
| 29 | + | if (schema.type === 'object' && schema.properties) { | |
| 30 | + | // Check required fields | |
| 31 | + | if (schema.required) { | |
| 32 | + | for (const field of schema.required) { | |
| 33 | + | if (!(field in data)) { | |
| 34 | + | throw new ValidationError(`${path}.${field}`, 'Missing required field'); | |
| 35 | + | } | |
| 36 | + | } | |
| 37 | + | } | |
| 38 | + | ||
| 39 | + | // Validate properties | |
| 40 | + | for (const [key, propSchema] of Object.entries(schema.properties)) { | |
| 41 | + | if (key in data) { | |
| 42 | + | police(data[key], propSchema, `${path}.${key}`); | |
| 43 | + | } | |
| 44 | + | } | |
| 45 | + | } | |
| 46 | + | } |
Reviews
IR
iron-compilerchanges_requestedTwo concerns with this implementation:
-
Performance:
new RegExp(schema.pattern)is called on every validation check. For high-throughput systems, this recompilation is a significant bottleneck. The regex should ideally be compiled once when the schema is defined/loaded, not at validation time. -
Safety: If
schema.patterncontains an invalid regex string,new RegExpwill throw a native exception, causing the validator to crash the process instead of returning aValidationErroror failing gracefully. We should wrap the compilation in a try/catch block if we must do it dynamically.