-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathvalidate-events.js
More file actions
244 lines (215 loc) Β· 9.26 KB
/
validate-events.js
File metadata and controls
244 lines (215 loc) Β· 9.26 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
#!/usr/bin/env node
// Event validation script for Critical Path game
// Usage: node validate-events.js
/* global process */
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
// Simple JSON schema validator (subset of functionality)
class SimpleValidator {
constructor(schema) {
this.schema = schema;
this.errors = [];
}
validate(data, schema = this.schema, path = '') {
this.errors = [];
this._validate(data, schema, path);
return {
valid: this.errors.length === 0,
errors: this.errors
};
}
_validate(data, schema, path) {
// Type validation
if (schema.type) {
if (schema.type === 'object' && (typeof data !== 'object' || Array.isArray(data) || data === null)) {
this.errors.push(`${path}: Expected object, got ${typeof data}`);
return;
}
if (schema.type === 'array' && !Array.isArray(data)) {
this.errors.push(`${path}: Expected array, got ${typeof data}`);
return;
}
if (schema.type === 'string' && typeof data !== 'string') {
this.errors.push(`${path}: Expected string, got ${typeof data}`);
return;
}
if (schema.type === 'number' && typeof data !== 'number') {
this.errors.push(`${path}: Expected number, got ${typeof data}`);
return;
}
if (schema.type === 'boolean' && typeof data !== 'boolean') {
this.errors.push(`${path}: Expected boolean, got ${typeof data}`);
return;
}
}
// Const validation
if (schema.const !== undefined && data !== schema.const) {
this.errors.push(`${path}: Expected constant value "${schema.const}", got "${data}"`);
}
// Enum validation
if (schema.enum && !schema.enum.includes(data)) {
this.errors.push(`${path}: Value "${data}" not in allowed values [${schema.enum.join(', ')}]`);
}
// Pattern validation
if (schema.pattern && typeof data === 'string') {
const regex = new RegExp(schema.pattern);
if (!regex.test(data)) {
this.errors.push(`${path}: String "${data}" does not match pattern ${schema.pattern}`);
}
}
// String length validation
if (schema.minLength && typeof data === 'string' && data.length < schema.minLength) {
this.errors.push(`${path}: String length ${data.length} is less than minimum ${schema.minLength}`);
}
// Number validation
if (schema.minimum !== undefined && typeof data === 'number' && data < schema.minimum) {
this.errors.push(`${path}: Number ${data} is less than minimum ${schema.minimum}`);
}
if (schema.maximum !== undefined && typeof data === 'number' && data > schema.maximum) {
this.errors.push(`${path}: Number ${data} is greater than maximum ${schema.maximum}`);
}
// Array validation
if (schema.type === 'array' && Array.isArray(data)) {
if (schema.minItems && data.length < schema.minItems) {
this.errors.push(`${path}: Array length ${data.length} is less than minimum ${schema.minItems}`);
}
if (schema.items) {
data.forEach((item, index) => {
this._validate(item, schema.items, `${path}[${index}]`);
});
}
}
// Object validation
if (schema.type === 'object' && typeof data === 'object' && !Array.isArray(data) && data !== null) {
// Required properties
if (schema.required) {
schema.required.forEach(prop => {
if (!(prop in data)) {
this.errors.push(`${path}: Missing required property "${prop}"`);
}
});
}
// Properties validation
if (schema.properties) {
Object.keys(data).forEach(prop => {
if (schema.properties[prop]) {
this._validate(data[prop], schema.properties[prop], `${path}.${prop}`);
} else if (schema.additionalProperties === false) {
this.errors.push(`${path}: Unexpected property "${prop}"`);
}
});
}
// Pattern properties validation
if (schema.patternProperties) {
Object.keys(data).forEach(prop => {
let matched = false;
Object.keys(schema.patternProperties).forEach(pattern => {
const regex = new RegExp(pattern);
if (regex.test(prop)) {
matched = true;
this._validate(data[prop], schema.patternProperties[pattern], `${path}.${prop}`);
}
});
if (!matched && schema.additionalProperties === false) {
this.errors.push(`${path}: Property "${prop}" does not match any pattern`);
}
});
}
}
// $ref validation (simplified)
if (schema.$ref && this.schema.definitions) {
const refPath = schema.$ref.replace('#/definitions/', '');
const refSchema = this.schema.definitions[refPath];
if (refSchema) {
this._validate(data, refSchema, path);
}
}
// anyOf validation (simplified - just check if at least one passes)
if (schema.anyOf) {
const originalErrors = [...this.errors];
let anyValid = false;
for (const subSchema of schema.anyOf) {
this.errors = [...originalErrors];
this._validate(data, subSchema, path);
if (this.errors.length === originalErrors.length) {
anyValid = true;
break;
}
}
if (!anyValid) {
this.errors = originalErrors;
this.errors.push(`${path}: Does not match any of the anyOf schemas`);
}
}
}
}
function validateEvents() {
console.log('π Validating events.json against schema...\n');
// Load schema
let schema;
try {
const schemaContent = fs.readFileSync('events.schema.json', 'utf8');
schema = JSON.parse(schemaContent);
} catch (error) {
console.error('β Failed to load schema file:', error.message);
process.exit(1);
}
// Load events data
let eventsData;
try {
const eventsContent = fs.readFileSync('events.json', 'utf8');
eventsData = JSON.parse(eventsContent);
} catch (error) {
console.error('β Failed to load events.json:', error.message);
process.exit(1);
}
// Validate
const validator = new SimpleValidator(schema);
const result = validator.validate(eventsData);
if (result.valid) {
console.log('β
events.json is valid according to the schema!');
console.log(`π Validation stats:`);
console.log(` β’ Special events: ${Object.keys(eventsData.specialEvents).length}`);
console.log(` β’ Default events: ${eventsData.defaultEvents.length}`);
// Count events by type
const eventTypes = {
withRequirements: 0,
withAILevelRange: 0,
withConditionalChoices: 0,
withCustomHandlers: 0,
withOtherTexts: 0,
disabled: 0
};
eventsData.defaultEvents.forEach(event => {
if (event.requires && event.requires.length > 0) eventTypes.withRequirements++;
if (event.aiLevelRange) eventTypes.withAILevelRange++;
if (event.choices && event.choices.some(c => c.condition)) eventTypes.withConditionalChoices++;
if (event.customHandler) eventTypes.withCustomHandlers++;
if (event.other_texts) eventTypes.withOtherTexts++;
if (event.weight === 0) eventTypes.disabled++;
});
console.log(` β’ Events with requirements: ${eventTypes.withRequirements}`);
console.log(` β’ Events with AI level ranges: ${eventTypes.withAILevelRange}`);
console.log(` β’ Events with conditional choices: ${eventTypes.withConditionalChoices}`);
console.log(` β’ Events with custom handlers: ${eventTypes.withCustomHandlers}`);
console.log(` β’ Events with other_texts: ${eventTypes.withOtherTexts}`);
console.log(` β’ Disabled events: ${eventTypes.disabled}`);
return true;
} else {
console.log('β events.json validation failed:');
result.errors.forEach(error => {
console.log(` β’ ${error}`);
});
return false;
}
}
// Export for ES modules
export { validateEvents, SimpleValidator };
// Run validation if this is the main module
if (import.meta.url === `file://${process.argv[1]}`) {
const isValid = validateEvents();
process.exit(isValid ? 0 : 1);
}