openclaw / src /schema /marketData.schema.js
getzero11's picture
Upload marketData.schema.js
c66cada verified
/**
* Canonical Market Data Schema Definition
* Version: 1.0
*
* This schema defines the expected structure for healthcare market research data
* across all system components (OpenClaw Agent, n8n Workflow, WordPress Dashboard)
*/
export const SCHEMA_VERSION = '1.0';
/**
* Schema version history
*/
export const SCHEMA_VERSIONS = [
{
version: '1.0',
releaseDate: '2024-01-20',
changes: ['Initial schema definition with canonical structure'],
backwardCompatible: true
}
];
/**
* Canonical Market Data Schema
* All components must conform to this structure
*/
export const MARKET_DATA_SCHEMA = {
// Schema metadata
schemaVersion: SCHEMA_VERSION,
// Required top-level fields
required: [
'marketTitle',
'executiveOverview',
'pastYear_2023',
'currentYear_2025',
'forecastYear_2033',
'global_cagr_Forecast',
'marketSegments',
'marketDrivers',
'competitiveLandscape'
],
// Field type definitions
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 }
}
};
/**
* Schema Validator Class
* Validates market data against the canonical schema
*/
export class SchemaValidator {
constructor(schema = MARKET_DATA_SCHEMA) {
this.schema = schema;
}
/**
* Validate data against schema
* @param {Object} data - Data to validate
* @returns {Object} - { valid: boolean, errors: string[], warnings: string[] }
*/
validate(data) {
const errors = [];
const warnings = [];
if (!data || typeof data !== 'object') {
errors.push('Data must be an object');
return { valid: false, errors, warnings };
}
// Check required top-level fields
for (const field of this.schema.required) {
if (!(field in data) || data[field] === null || data[field] === undefined) {
errors.push(`Missing required field: ${field}`);
}
}
// Validate field types
this.validateFields(data, this.schema.fields, '', errors, warnings);
// Validate marketSegments structure
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`);
}
});
}
// Validate competitiveLandscape
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
};
}
/**
* Validate individual fields recursively
*/
validateFields(data, fieldDefs, path, errors, warnings) {
for (const [fieldName, fieldDef] of Object.entries(fieldDefs)) {
const fullPath = path ? `${path}.${fieldName}` : fieldName;
const value = data[fieldName];
// Check if required field is missing
if (fieldDef.required && (value === null || value === undefined)) {
errors.push(`Missing required field: ${fullPath}`);
continue;
}
// Skip validation if field is optional and not present
if (!fieldDef.required && (value === null || value === undefined)) {
continue;
}
// Validate type
if (fieldDef.type === 'array') {
if (!Array.isArray(value)) {
errors.push(`${fullPath} must be an array`);
} else if (fieldDef.items && value.length > 0) {
// Validate array items
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`);
}
}
}
}
/**
* Get schema version
*/
getVersion() {
return this.schema.schemaVersion;
}
/**
* Get schema documentation
*/
getDocumentation() {
return {
version: this.schema.schemaVersion,
required: this.schema.required,
fields: this.schema.fields,
versions: SCHEMA_VERSIONS
};
}
}
/**
* Validate market data (convenience function)
* @param {Object} data - Data to validate
* @returns {Object} - Validation result
*/
export function validateMarketData(data) {
const validator = new SchemaValidator();
return validator.validate(data);
}
/**
* Error types for schema validation
*/
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;
}
}