| |
| |
| |
| |
| |
| |
|
|
|
|
| export const SCHEMA_VERSION = '1.0';
|
|
|
| |
| |
|
|
| export const SCHEMA_VERSIONS = [
|
| {
|
| version: '1.0',
|
| releaseDate: '2024-01-20',
|
| changes: ['Initial schema definition with canonical structure'],
|
| backwardCompatible: true
|
| }
|
| ];
|
|
|
| |
| |
| |
|
|
| export const MARKET_DATA_SCHEMA = {
|
|
|
| schemaVersion: SCHEMA_VERSION,
|
|
|
|
|
| required: [
|
| 'marketTitle',
|
| 'executiveOverview',
|
| 'pastYear_2023',
|
| 'currentYear_2025',
|
| 'forecastYear_2033',
|
| 'global_cagr_Forecast',
|
| 'marketSegments',
|
| 'marketDrivers',
|
| 'competitiveLandscape'
|
| ],
|
|
|
|
|
| fields: {
|
| marketTitle: { type: 'string', required: true },
|
| executiveOverview: { type: 'string', required: true },
|
| pastYear_2023: { type: 'number', required: true },
|
| currentYear_2025: { type: 'number', required: true },
|
| forecastYear_2033: { type: 'number', required: true },
|
| global_cagr_Forecast: { type: 'number', required: true },
|
|
|
| marketSegments: {
|
| type: 'array',
|
| required: true,
|
| items: {
|
| segmentCategory: { type: 'string', required: true },
|
| segmentName: { type: 'string', required: true },
|
| segmentName_cagr_Forecast: { type: 'number', required: false },
|
| subSegments: {
|
| type: 'array',
|
| required: true,
|
| items: {
|
| subSegmentName: { type: 'string', required: true },
|
| segment_marketShare_2023: { type: 'number', required: false },
|
| sub_segment_marketShare_2023: { type: 'number', required: false },
|
| segment_marketShare_2025: { type: 'number', required: false },
|
| sub_segment_marketShare_2025: { type: 'number', required: false },
|
| segment_marketShare_2033: { type: 'number', required: false },
|
| sub_segment_marketShare_2033: { type: 'number', required: false },
|
| sub_segmentName_cagr_Forecast: { type: 'number', required: false }
|
| }
|
| }
|
| }
|
| },
|
|
|
| marketDrivers: { type: 'array', required: true },
|
| emergingTrends: { type: 'array', required: false },
|
|
|
| insights: {
|
| type: 'object',
|
| required: false,
|
| fields: {
|
| largestSegment2025: { type: 'string', required: false },
|
| fastestGrowingSegment: { type: 'string', required: false },
|
| keyOpportunities: { type: 'array', required: false },
|
| majorChallenges: { type: 'array', required: false }
|
| }
|
| },
|
|
|
| competitiveLandscape: {
|
| type: 'array',
|
| required: true,
|
| items: {
|
| company: { type: 'string', required: true },
|
| player_marketShare_2025: { type: 'number', required: true },
|
| positioning: { type: 'string', required: false }
|
| }
|
| },
|
|
|
| regulatoryEnvironment: { type: 'string', required: false },
|
| geographicAnalysis: { type: 'string', required: false },
|
| futureOutlook: { type: 'string', required: false },
|
| strategicRecommendations: { type: 'array', required: false }
|
| }
|
| };
|
|
|
| |
| |
| |
|
|
| export class SchemaValidator {
|
| constructor(schema = MARKET_DATA_SCHEMA) {
|
| this.schema = schema;
|
| }
|
|
|
| |
| |
| |
| |
|
|
| validate(data) {
|
| const errors = [];
|
| const warnings = [];
|
|
|
| if (!data || typeof data !== 'object') {
|
| errors.push('Data must be an object');
|
| return { valid: false, errors, warnings };
|
| }
|
|
|
|
|
| for (const field of this.schema.required) {
|
| if (!(field in data) || data[field] === null || data[field] === undefined) {
|
| errors.push(`Missing required field: ${field}`);
|
| }
|
| }
|
|
|
|
|
| this.validateFields(data, this.schema.fields, '', errors, warnings);
|
|
|
|
|
| if (Array.isArray(data.marketSegments)) {
|
| data.marketSegments.forEach((segment, idx) => {
|
| if (!segment.segmentName) {
|
| errors.push(`marketSegments[${idx}]: missing segmentName`);
|
| }
|
| if (!Array.isArray(segment.subSegments)) {
|
| errors.push(`marketSegments[${idx}]: subSegments must be an array`);
|
| }
|
| });
|
| }
|
|
|
|
|
| if (Array.isArray(data.competitiveLandscape)) {
|
| if (data.competitiveLandscape.length < 5) {
|
| warnings.push('competitiveLandscape should include at least 5 companies');
|
| }
|
| data.competitiveLandscape.forEach((company, idx) => {
|
| if (!company.company) {
|
| errors.push(`competitiveLandscape[${idx}]: missing company name`);
|
| }
|
| if (typeof company.player_marketShare_2025 !== 'number') {
|
| errors.push(`competitiveLandscape[${idx}]: player_marketShare_2025 must be a number`);
|
| }
|
| });
|
| }
|
|
|
| return {
|
| valid: errors.length === 0,
|
| errors,
|
| warnings
|
| };
|
| }
|
|
|
| |
| |
|
|
| validateFields(data, fieldDefs, path, errors, warnings) {
|
| for (const [fieldName, fieldDef] of Object.entries(fieldDefs)) {
|
| const fullPath = path ? `${path}.${fieldName}` : fieldName;
|
| const value = data[fieldName];
|
|
|
|
|
| if (fieldDef.required && (value === null || value === undefined)) {
|
| errors.push(`Missing required field: ${fullPath}`);
|
| continue;
|
| }
|
|
|
|
|
| if (!fieldDef.required && (value === null || value === undefined)) {
|
| continue;
|
| }
|
|
|
|
|
| if (fieldDef.type === 'array') {
|
| if (!Array.isArray(value)) {
|
| errors.push(`${fullPath} must be an array`);
|
| } else if (fieldDef.items && value.length > 0) {
|
|
|
| value.forEach((item, idx) => {
|
| if (typeof fieldDef.items === 'object' && !Array.isArray(fieldDef.items)) {
|
| this.validateFields(item, fieldDef.items, `${fullPath}[${idx}]`, errors, warnings);
|
| }
|
| });
|
| }
|
| } else if (fieldDef.type === 'object') {
|
| if (typeof value !== 'object' || Array.isArray(value)) {
|
| errors.push(`${fullPath} must be an object`);
|
| } else if (fieldDef.fields) {
|
| this.validateFields(value, fieldDef.fields, fullPath, errors, warnings);
|
| }
|
| } else if (fieldDef.type === 'string') {
|
| if (typeof value !== 'string') {
|
| errors.push(`${fullPath} must be a string`);
|
| }
|
| } else if (fieldDef.type === 'number') {
|
| if (typeof value !== 'number' || isNaN(value)) {
|
| errors.push(`${fullPath} must be a number`);
|
| }
|
| } else if (fieldDef.type === 'boolean') {
|
| if (typeof value !== 'boolean') {
|
| errors.push(`${fullPath} must be a boolean`);
|
| }
|
| }
|
| }
|
| }
|
|
|
| |
| |
|
|
| getVersion() {
|
| return this.schema.schemaVersion;
|
| }
|
|
|
| |
| |
|
|
| getDocumentation() {
|
| return {
|
| version: this.schema.schemaVersion,
|
| required: this.schema.required,
|
| fields: this.schema.fields,
|
| versions: SCHEMA_VERSIONS
|
| };
|
| }
|
| }
|
|
|
| |
| |
| |
| |
|
|
| export function validateMarketData(data) {
|
| const validator = new SchemaValidator();
|
| return validator.validate(data);
|
| }
|
|
|
| |
| |
|
|
| export class ValidationError extends Error {
|
| constructor(message, errors = []) {
|
| super(message);
|
| this.name = 'ValidationError';
|
| this.errors = errors;
|
| }
|
| }
|
|
|
| export class MissingDataError extends Error {
|
| constructor(message, missingFields = []) {
|
| super(message);
|
| this.name = 'MissingDataError';
|
| this.missingFields = missingFields;
|
| }
|
| }
|
|
|
| export class TransformationError extends Error {
|
| constructor(message, details = {}) {
|
| super(message);
|
| this.name = 'TransformationError';
|
| this.details = details;
|
| }
|
| }
|
|
|