getzero11 commited on
Commit
c66cada
·
verified ·
1 Parent(s): 2118cce

Upload marketData.schema.js

Browse files
Files changed (1) hide show
  1. src/schema/marketData.schema.js +283 -0
src/schema/marketData.schema.js ADDED
@@ -0,0 +1,283 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Canonical Market Data Schema Definition
3
+ * Version: 1.0
4
+ *
5
+ * This schema defines the expected structure for healthcare market research data
6
+ * across all system components (OpenClaw Agent, n8n Workflow, WordPress Dashboard)
7
+ */
8
+
9
+ export const SCHEMA_VERSION = '1.0';
10
+
11
+ /**
12
+ * Schema version history
13
+ */
14
+ export const SCHEMA_VERSIONS = [
15
+ {
16
+ version: '1.0',
17
+ releaseDate: '2024-01-20',
18
+ changes: ['Initial schema definition with canonical structure'],
19
+ backwardCompatible: true
20
+ }
21
+ ];
22
+
23
+ /**
24
+ * Canonical Market Data Schema
25
+ * All components must conform to this structure
26
+ */
27
+ export const MARKET_DATA_SCHEMA = {
28
+ // Schema metadata
29
+ schemaVersion: SCHEMA_VERSION,
30
+
31
+ // Required top-level fields
32
+ required: [
33
+ 'marketTitle',
34
+ 'executiveOverview',
35
+ 'pastYear_2023',
36
+ 'currentYear_2025',
37
+ 'forecastYear_2033',
38
+ 'global_cagr_Forecast',
39
+ 'marketSegments',
40
+ 'marketDrivers',
41
+ 'competitiveLandscape'
42
+ ],
43
+
44
+ // Field type definitions
45
+ fields: {
46
+ marketTitle: { type: 'string', required: true },
47
+ executiveOverview: { type: 'string', required: true },
48
+ pastYear_2023: { type: 'number', required: true },
49
+ currentYear_2025: { type: 'number', required: true },
50
+ forecastYear_2033: { type: 'number', required: true },
51
+ global_cagr_Forecast: { type: 'number', required: true },
52
+
53
+ marketSegments: {
54
+ type: 'array',
55
+ required: true,
56
+ items: {
57
+ segmentCategory: { type: 'string', required: true },
58
+ segmentName: { type: 'string', required: true },
59
+ segmentName_cagr_Forecast: { type: 'number', required: false },
60
+ subSegments: {
61
+ type: 'array',
62
+ required: true,
63
+ items: {
64
+ subSegmentName: { type: 'string', required: true },
65
+ segment_marketShare_2023: { type: 'number', required: false },
66
+ sub_segment_marketShare_2023: { type: 'number', required: false },
67
+ segment_marketShare_2025: { type: 'number', required: false },
68
+ sub_segment_marketShare_2025: { type: 'number', required: false },
69
+ segment_marketShare_2033: { type: 'number', required: false },
70
+ sub_segment_marketShare_2033: { type: 'number', required: false },
71
+ sub_segmentName_cagr_Forecast: { type: 'number', required: false }
72
+ }
73
+ }
74
+ }
75
+ },
76
+
77
+ marketDrivers: { type: 'array', required: true },
78
+ emergingTrends: { type: 'array', required: false },
79
+
80
+ insights: {
81
+ type: 'object',
82
+ required: false,
83
+ fields: {
84
+ largestSegment2025: { type: 'string', required: false },
85
+ fastestGrowingSegment: { type: 'string', required: false },
86
+ keyOpportunities: { type: 'array', required: false },
87
+ majorChallenges: { type: 'array', required: false }
88
+ }
89
+ },
90
+
91
+ competitiveLandscape: {
92
+ type: 'array',
93
+ required: true,
94
+ items: {
95
+ company: { type: 'string', required: true },
96
+ player_marketShare_2025: { type: 'number', required: true },
97
+ positioning: { type: 'string', required: false }
98
+ }
99
+ },
100
+
101
+ regulatoryEnvironment: { type: 'string', required: false },
102
+ geographicAnalysis: { type: 'string', required: false },
103
+ futureOutlook: { type: 'string', required: false },
104
+ strategicRecommendations: { type: 'array', required: false }
105
+ }
106
+ };
107
+
108
+ /**
109
+ * Schema Validator Class
110
+ * Validates market data against the canonical schema
111
+ */
112
+ export class SchemaValidator {
113
+ constructor(schema = MARKET_DATA_SCHEMA) {
114
+ this.schema = schema;
115
+ }
116
+
117
+ /**
118
+ * Validate data against schema
119
+ * @param {Object} data - Data to validate
120
+ * @returns {Object} - { valid: boolean, errors: string[], warnings: string[] }
121
+ */
122
+ validate(data) {
123
+ const errors = [];
124
+ const warnings = [];
125
+
126
+ if (!data || typeof data !== 'object') {
127
+ errors.push('Data must be an object');
128
+ return { valid: false, errors, warnings };
129
+ }
130
+
131
+ // Check required top-level fields
132
+ for (const field of this.schema.required) {
133
+ if (!(field in data) || data[field] === null || data[field] === undefined) {
134
+ errors.push(`Missing required field: ${field}`);
135
+ }
136
+ }
137
+
138
+ // Validate field types
139
+ this.validateFields(data, this.schema.fields, '', errors, warnings);
140
+
141
+ // Validate marketSegments structure
142
+ if (Array.isArray(data.marketSegments)) {
143
+ data.marketSegments.forEach((segment, idx) => {
144
+ if (!segment.segmentName) {
145
+ errors.push(`marketSegments[${idx}]: missing segmentName`);
146
+ }
147
+ if (!Array.isArray(segment.subSegments)) {
148
+ errors.push(`marketSegments[${idx}]: subSegments must be an array`);
149
+ }
150
+ });
151
+ }
152
+
153
+ // Validate competitiveLandscape
154
+ if (Array.isArray(data.competitiveLandscape)) {
155
+ if (data.competitiveLandscape.length < 5) {
156
+ warnings.push('competitiveLandscape should include at least 5 companies');
157
+ }
158
+ data.competitiveLandscape.forEach((company, idx) => {
159
+ if (!company.company) {
160
+ errors.push(`competitiveLandscape[${idx}]: missing company name`);
161
+ }
162
+ if (typeof company.player_marketShare_2025 !== 'number') {
163
+ errors.push(`competitiveLandscape[${idx}]: player_marketShare_2025 must be a number`);
164
+ }
165
+ });
166
+ }
167
+
168
+ return {
169
+ valid: errors.length === 0,
170
+ errors,
171
+ warnings
172
+ };
173
+ }
174
+
175
+ /**
176
+ * Validate individual fields recursively
177
+ */
178
+ validateFields(data, fieldDefs, path, errors, warnings) {
179
+ for (const [fieldName, fieldDef] of Object.entries(fieldDefs)) {
180
+ const fullPath = path ? `${path}.${fieldName}` : fieldName;
181
+ const value = data[fieldName];
182
+
183
+ // Check if required field is missing
184
+ if (fieldDef.required && (value === null || value === undefined)) {
185
+ errors.push(`Missing required field: ${fullPath}`);
186
+ continue;
187
+ }
188
+
189
+ // Skip validation if field is optional and not present
190
+ if (!fieldDef.required && (value === null || value === undefined)) {
191
+ continue;
192
+ }
193
+
194
+ // Validate type
195
+ if (fieldDef.type === 'array') {
196
+ if (!Array.isArray(value)) {
197
+ errors.push(`${fullPath} must be an array`);
198
+ } else if (fieldDef.items && value.length > 0) {
199
+ // Validate array items
200
+ value.forEach((item, idx) => {
201
+ if (typeof fieldDef.items === 'object' && !Array.isArray(fieldDef.items)) {
202
+ this.validateFields(item, fieldDef.items, `${fullPath}[${idx}]`, errors, warnings);
203
+ }
204
+ });
205
+ }
206
+ } else if (fieldDef.type === 'object') {
207
+ if (typeof value !== 'object' || Array.isArray(value)) {
208
+ errors.push(`${fullPath} must be an object`);
209
+ } else if (fieldDef.fields) {
210
+ this.validateFields(value, fieldDef.fields, fullPath, errors, warnings);
211
+ }
212
+ } else if (fieldDef.type === 'string') {
213
+ if (typeof value !== 'string') {
214
+ errors.push(`${fullPath} must be a string`);
215
+ }
216
+ } else if (fieldDef.type === 'number') {
217
+ if (typeof value !== 'number' || isNaN(value)) {
218
+ errors.push(`${fullPath} must be a number`);
219
+ }
220
+ } else if (fieldDef.type === 'boolean') {
221
+ if (typeof value !== 'boolean') {
222
+ errors.push(`${fullPath} must be a boolean`);
223
+ }
224
+ }
225
+ }
226
+ }
227
+
228
+ /**
229
+ * Get schema version
230
+ */
231
+ getVersion() {
232
+ return this.schema.schemaVersion;
233
+ }
234
+
235
+ /**
236
+ * Get schema documentation
237
+ */
238
+ getDocumentation() {
239
+ return {
240
+ version: this.schema.schemaVersion,
241
+ required: this.schema.required,
242
+ fields: this.schema.fields,
243
+ versions: SCHEMA_VERSIONS
244
+ };
245
+ }
246
+ }
247
+
248
+ /**
249
+ * Validate market data (convenience function)
250
+ * @param {Object} data - Data to validate
251
+ * @returns {Object} - Validation result
252
+ */
253
+ export function validateMarketData(data) {
254
+ const validator = new SchemaValidator();
255
+ return validator.validate(data);
256
+ }
257
+
258
+ /**
259
+ * Error types for schema validation
260
+ */
261
+ export class ValidationError extends Error {
262
+ constructor(message, errors = []) {
263
+ super(message);
264
+ this.name = 'ValidationError';
265
+ this.errors = errors;
266
+ }
267
+ }
268
+
269
+ export class MissingDataError extends Error {
270
+ constructor(message, missingFields = []) {
271
+ super(message);
272
+ this.name = 'MissingDataError';
273
+ this.missingFields = missingFields;
274
+ }
275
+ }
276
+
277
+ export class TransformationError extends Error {
278
+ constructor(message, details = {}) {
279
+ super(message);
280
+ this.name = 'TransformationError';
281
+ this.details = details;
282
+ }
283
+ }