solver-core-compute
Version:
Shared solver library for floorplan optimization - compatible with Node.js and Deno
192 lines (164 loc) • 5.57 kB
text/typescript
/**
* IO Contract for Solver API
* TypeScript types and validation for SolveRequest/SolveResponse
*/
import { z } from 'zod';
import type { Position, Constraint } from './types/index.js';
// ===== SOLVE REQUEST SCHEMA =====
export const PositionSchema = z.object({
x: z.number().finite().min(-10000).max(10000),
y: z.number().finite().min(-10000).max(10000),
width: z.number().positive().max(10000),
height: z.number().positive().max(10000),
rotation: z.number().min(0).max(360).optional()
});
export const ConstraintSchema = z.object({
type: z.enum(['overlap', 'boundary', 'aspect_ratio', 'proximity', 'alignment', 'min_clearance', 'work_triangle'] as const),
params: z.any(),
id: z.string().optional(),
weight: z.number().positive().max(1000).optional()
});
export const SolveRequestSchema = z.object({
positions: z.array(PositionSchema).min(1).max(1000),
constraints: z.array(ConstraintSchema).min(0).max(10000),
options: z.object({
algorithm: z.enum(['parallel', 'hybrid', 'adaptive', 'sa', 'ga', 'ls'] as const).optional(),
timeout: z.number().positive().max(300000).optional(),
gridSize: z.number().positive().max(1000).optional(),
maxIterations: z.number().positive().max(100000).optional(),
useParallel: z.boolean().optional(),
useMultiObjective: z.boolean().optional(),
randomSeed: z.number().int().min(0).max(2147483647).optional()
}).optional(),
metadata: z.object({
requestId: z.string().uuid().optional(),
timestamp: z.number().optional(),
userId: z.string().optional()
}).optional()
});
export type SolveRequest = z.infer<typeof SolveRequestSchema>;
// ===== SOLVE RESPONSE SCHEMA =====
export const ViolationSchema = z.object({
constraintId: z.string().optional(),
constraintType: z.string(),
positions: z.array(z.any()),
penalty: z.number().min(0).max(10000),
description: z.string().optional()
});
export type Violation = z.infer<typeof ViolationSchema>;
export const ConstraintEvaluationSchema = z.object({
totalConstraints: z.number().min(0),
satisfied: z.number().min(0),
violated: z.number().min(0),
violations: z.array(ViolationSchema)
});
export type ConstraintEvaluation = z.infer<typeof ConstraintEvaluationSchema>;
export const SolutionSchema = z.object({
positions: z.array(PositionSchema),
score: z.number().min(0).max(10000),
violations: z.number().min(0).max(10000),
algorithm: z.string(),
executionTime: z.number().min(0).max(300000),
iterations: z.number().min(0).max(100000),
convergenceData: z.object({
initialScore: z.number(),
finalScore: z.number(),
improvement: z.number()
}).optional()
});
export type IOSolution = z.infer<typeof SolutionSchema>;
export const SolveResponseSchema = z.object({
success: z.boolean(),
solutions: z.array(SolutionSchema).min(0).max(100),
bestSolution: SolutionSchema.optional(),
metadata: z.object({
requestId: z.string().uuid().optional(),
timestamp: z.number(),
solver: z.string(),
totalTime: z.number().min(0).max(300000),
iterations: z.number().min(0).max(100000)
}),
constraintEvaluation: ConstraintEvaluationSchema.optional(),
errors: z.array(z.object({
code: z.string(),
message: z.string(),
details: z.any().optional()
})).optional(),
warnings: z.array(z.string()).optional()
});
export type { IOSolution as Solution };
export type SolveResponse = z.infer<typeof SolveResponseSchema>;
// ===== VALIDATION FUNCTIONS =====
/**
* Validate SolveRequest input
* Throws ZodError if validation fails
*/
export function validateSolveRequest(input: unknown): SolveRequest {
try {
return SolveRequestSchema.parse(input);
} catch (error) {
if (error instanceof z.ZodError) {
const errorMessages = error.issues.map(e =>
`${e.path.join('.')}: ${e.message}`
).join('; ');
const validationError = new Error(`SolveRequest validation failed: ${errorMessages}`);
(validationError as any).name = 'ValidationError';
(validationError as any).issues = error.issues;
throw validationError;
}
throw error;
}
}
/**
* Validate SolveResponse output
* Throws ZodError if validation fails
*/
export function validateSolveResponse(output: unknown): SolveResponse {
try {
return SolveResponseSchema.parse(output);
} catch (error) {
if (error instanceof z.ZodError) {
const errorMessages = error.issues.map(e =>
`${e.path.join('.')}: ${e.message}`
).join('; ');
const validationError = new Error(`SolveResponse validation failed: ${errorMessages}`);
(validationError as any).name = 'ValidationError';
(validationError as any).issues = error.issues;
throw validationError;
}
throw error;
}
}
/**
* Validate input and output around solver call
*/
export function validateIO<TInput, TOutput>(
input: TInput,
output: TOutput,
context?: string
): { input: SolveRequest; output: SolveResponse } {
const validatedInput = validateSolveRequest(input);
const validatedOutput = validateSolveResponse(output);
// Logging removed for Deno compatibility
return {
input: validatedInput,
output: validatedOutput
};
}
// ===== TYPE GUARDS =====
export function isValidSolveRequest(obj: unknown): obj is SolveRequest {
try {
validateSolveRequest(obj);
return true;
} catch {
return false;
}
}
export function isValidSolveResponse(obj: unknown): obj is SolveResponse {
try {
validateSolveResponse(obj);
return true;
} catch {
return false;
}
}