UNPKG

@prodbirdy/mockup-generator

Version:

Serverless-optimized TypeScript SDK for generating high-quality product mockups from PSD templates

247 lines (207 loc) 6.54 kB
/** * Input validation for MockupSDK */ import type { MockupGenerationConfig, BatchMockupConfig, UploadOptions, SmartObjectReplacement, ExportOptions, } from "./types"; import { ValidationError } from "./types"; export function validateGenerationConfig(config: MockupGenerationConfig): void { if (!config || typeof config !== "object") { throw new ValidationError( "Configuration is required and must be an object" ); } // Validate PSD input validatePSDInput(config.psd); // Validate replacements validateSmartObjects(config.replacements); // Validate exports if provided if (config.exports) { validateExportOptions(config.exports); } } export function validateBatchConfig(config: BatchMockupConfig): void { if (!config || typeof config !== "object") { throw new ValidationError( "Batch configuration is required and must be an object" ); } // Validate template PSD validatePSDInput(config.template); // Validate variations if (!Array.isArray(config.variations)) { throw new ValidationError("Variations must be an array"); } if (config.variations.length === 0) { throw new ValidationError("At least one variation is required"); } const seenIds = new Set<string>(); for (const [index, variation] of config.variations.entries()) { if (!variation.id || typeof variation.id !== "string") { throw new ValidationError( `Variation ${index}: id is required and must be a string` ); } if (seenIds.has(variation.id)) { throw new ValidationError( `Variation ${index}: duplicate id '${variation.id}'` ); } seenIds.add(variation.id); validateSmartObjects(variation.replacements); if (variation.exports) { validateExportOptions(variation.exports); } } } export function validateUploadOptions(options: UploadOptions): void { if (!options || typeof options !== "object") { return; // Options are optional } if (options.prefix && typeof options.prefix !== "string") { throw new ValidationError("Upload prefix must be a string"); } if ( options.uploadPSD !== undefined && typeof options.uploadPSD !== "boolean" ) { throw new ValidationError("uploadPSD must be a boolean"); } if ( options.uploadExports !== undefined && typeof options.uploadExports !== "boolean" ) { throw new ValidationError("uploadExports must be a boolean"); } } function validatePSDInput(psd: string | Buffer): void { if (!psd) { throw new ValidationError("PSD input is required"); } if (typeof psd === "string") { if (!isValidUrl(psd)) { throw new ValidationError("PSD URL must be a valid URL"); } } else if (!(psd instanceof Buffer)) { throw new ValidationError("PSD must be a URL string or Buffer"); } else if (psd.length === 0) { throw new ValidationError("PSD Buffer cannot be empty"); } } export function validateSmartObjects( replacements: SmartObjectReplacement[] ): void { if (!Array.isArray(replacements)) { throw new ValidationError("Replacements must be an array"); } if (replacements.length === 0) { throw new ValidationError( "At least one smart object replacement is required" ); } for (const [index, replacement] of replacements.entries()) { if (!replacement.name || typeof replacement.name !== "string") { throw new ValidationError( `Replacement ${index}: name is required and must be a string` ); } if (!replacement.image) { throw new ValidationError(`Replacement ${index}: image is required`); } if ( typeof replacement.image === "string" && !isValidUrl(replacement.image) ) { throw new ValidationError(`Replacement ${index}: image URL is invalid`); } if (replacement.image instanceof Buffer && replacement.image.length === 0) { throw new ValidationError(`Replacement ${index}: image Buffer is empty`); } if ( replacement.fillMode && !["fit", "fill", "stretch"].includes(replacement.fillMode) ) { throw new ValidationError( `Replacement ${index}: fillMode must be 'fit', 'fill', or 'stretch'` ); } if (replacement.printArea) { validatePrintArea(replacement.printArea, index); } } } export function validateExportOptions(exports: ExportOptions[]): void { if (!Array.isArray(exports)) { throw new ValidationError("Exports must be an array"); } const validFormats = ["png", "jpg", "jpeg", "webp", "pdf"]; for (const [index, exportOption] of exports.entries()) { if (!exportOption.format || !validFormats.includes(exportOption.format)) { throw new ValidationError( `Export ${index}: format must be one of ${validFormats.join(", ")}` ); } if (exportOption.quality !== undefined) { if ( typeof exportOption.quality !== "number" || exportOption.quality < 0 || exportOption.quality > 100 ) { throw new ValidationError( `Export ${index}: quality must be a number between 0 and 100` ); } } if (exportOption.width !== undefined) { if (typeof exportOption.width !== "number" || exportOption.width <= 0) { throw new ValidationError( `Export ${index}: width must be a positive number` ); } } if (exportOption.height !== undefined) { if (typeof exportOption.height !== "number" || exportOption.height <= 0) { throw new ValidationError( `Export ${index}: height must be a positive number` ); } } } } function validatePrintArea(printArea: any, index: number): void { const requiredFields = ["x", "y", "width", "height"]; for (const field of requiredFields) { if (typeof printArea[field] !== "number") { throw new ValidationError( `Replacement ${index}: printArea.${field} must be a number` ); } if (printArea[field] < 0) { throw new ValidationError( `Replacement ${index}: printArea.${field} must be non-negative` ); } } if (printArea.width === 0) { throw new ValidationError( `Replacement ${index}: printArea.width must be greater than 0` ); } if (printArea.height === 0) { throw new ValidationError( `Replacement ${index}: printArea.height must be greater than 0` ); } } function isValidUrl(url: string): boolean { try { new URL(url); return true; } catch { return false; } }