@sschepis/magazine 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +167 -0
- package/dist/magazine.cjs.js +5 -0
- package/dist/magazine.cjs.js.map +1 -0
- package/dist/magazine.es.js +4149 -0
- package/dist/magazine.es.js.map +1 -0
- package/dist/magazine.umd.js +5 -0
- package/dist/magazine.umd.js.map +1 -0
- package/package.json +74 -0
- package/src/components/ConfigManager.js +202 -0
- package/src/components/ConfigSchema.js +506 -0
- package/src/components/DataCompressor.js +378 -0
- package/src/components/DataTransformer.js +1172 -0
- package/src/components/DebugUtils.js +657 -0
- package/src/components/EventFilter.js +474 -0
- package/src/components/EventMetadata.js +273 -0
- package/src/components/EventStore.js +443 -0
- package/src/components/EventSyncer.js +353 -0
- package/src/components/Logger.js +299 -0
- package/src/components/NetworkManager.js +461 -0
- package/src/components/Paginator.js +343 -0
- package/src/index.js +571 -0
|
@@ -0,0 +1,506 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ConfigSchema - Configuration schema definition and validation
|
|
3
|
+
*/
|
|
4
|
+
export default class ConfigSchema {
|
|
5
|
+
constructor() {
|
|
6
|
+
// Define the configuration schema
|
|
7
|
+
this.schema = {
|
|
8
|
+
// Required fields
|
|
9
|
+
required: ['name', 'address', 'abi', 'provider'],
|
|
10
|
+
|
|
11
|
+
// Field definitions
|
|
12
|
+
fields: {
|
|
13
|
+
name: {
|
|
14
|
+
type: 'string',
|
|
15
|
+
description: 'Name of the magazine',
|
|
16
|
+
minLength: 1,
|
|
17
|
+
maxLength: 100,
|
|
18
|
+
pattern: /^[a-zA-Z0-9\s\-_]+$/,
|
|
19
|
+
example: 'My Event Magazine'
|
|
20
|
+
},
|
|
21
|
+
|
|
22
|
+
address: {
|
|
23
|
+
type: 'string',
|
|
24
|
+
description: 'Contract address',
|
|
25
|
+
pattern: /^0x[a-fA-F0-9]{40}$/,
|
|
26
|
+
example: '0x1234567890123456789012345678901234567890'
|
|
27
|
+
},
|
|
28
|
+
|
|
29
|
+
abi: {
|
|
30
|
+
type: 'array',
|
|
31
|
+
description: 'Contract ABI',
|
|
32
|
+
minItems: 1,
|
|
33
|
+
items: {
|
|
34
|
+
type: 'object',
|
|
35
|
+
required: ['type'],
|
|
36
|
+
properties: {
|
|
37
|
+
type: {
|
|
38
|
+
type: 'string',
|
|
39
|
+
enum: ['function', 'event', 'constructor', 'fallback', 'receive']
|
|
40
|
+
},
|
|
41
|
+
name: { type: 'string' },
|
|
42
|
+
inputs: { type: 'array' },
|
|
43
|
+
outputs: { type: 'array' },
|
|
44
|
+
anonymous: { type: 'boolean' }
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
|
|
49
|
+
provider: {
|
|
50
|
+
type: ['string', 'object'],
|
|
51
|
+
description: 'Ethereum provider URL or object',
|
|
52
|
+
validate: (value) => {
|
|
53
|
+
if (typeof value === 'string') {
|
|
54
|
+
return value.startsWith('http://') ||
|
|
55
|
+
value.startsWith('https://') ||
|
|
56
|
+
value.startsWith('ws://') ||
|
|
57
|
+
value.startsWith('wss://');
|
|
58
|
+
}
|
|
59
|
+
return typeof value === 'object' && value !== null;
|
|
60
|
+
},
|
|
61
|
+
example: 'http://localhost:8545'
|
|
62
|
+
},
|
|
63
|
+
|
|
64
|
+
networkId: {
|
|
65
|
+
type: 'number',
|
|
66
|
+
description: 'Network ID',
|
|
67
|
+
optional: true,
|
|
68
|
+
min: 1,
|
|
69
|
+
example: 1
|
|
70
|
+
},
|
|
71
|
+
|
|
72
|
+
gun: {
|
|
73
|
+
type: 'object',
|
|
74
|
+
description: 'Gun.js configuration',
|
|
75
|
+
optional: true,
|
|
76
|
+
properties: {
|
|
77
|
+
peers: {
|
|
78
|
+
type: 'array',
|
|
79
|
+
items: { type: 'string' },
|
|
80
|
+
example: ['http://localhost:8765/gun']
|
|
81
|
+
},
|
|
82
|
+
file: {
|
|
83
|
+
type: 'string',
|
|
84
|
+
example: 'radata'
|
|
85
|
+
},
|
|
86
|
+
localStorage: {
|
|
87
|
+
type: 'boolean',
|
|
88
|
+
default: true
|
|
89
|
+
},
|
|
90
|
+
radisk: {
|
|
91
|
+
type: 'boolean',
|
|
92
|
+
default: true
|
|
93
|
+
},
|
|
94
|
+
multicast: {
|
|
95
|
+
type: ['boolean', 'object'],
|
|
96
|
+
default: false
|
|
97
|
+
},
|
|
98
|
+
compression: {
|
|
99
|
+
type: 'object',
|
|
100
|
+
properties: {
|
|
101
|
+
enabled: {
|
|
102
|
+
type: 'boolean',
|
|
103
|
+
default: true
|
|
104
|
+
},
|
|
105
|
+
strategy: {
|
|
106
|
+
type: 'string',
|
|
107
|
+
enum: ['none', 'json', 'gzip', 'optimized'],
|
|
108
|
+
default: 'optimized'
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
|
|
115
|
+
logLevel: {
|
|
116
|
+
type: 'string',
|
|
117
|
+
description: 'Logging level',
|
|
118
|
+
optional: true,
|
|
119
|
+
enum: ['error', 'warn', 'info', 'debug', 'trace'],
|
|
120
|
+
default: 'info'
|
|
121
|
+
},
|
|
122
|
+
|
|
123
|
+
maxRetries: {
|
|
124
|
+
type: 'number',
|
|
125
|
+
description: 'Maximum retry attempts',
|
|
126
|
+
optional: true,
|
|
127
|
+
min: 0,
|
|
128
|
+
max: 10,
|
|
129
|
+
default: 3
|
|
130
|
+
},
|
|
131
|
+
|
|
132
|
+
retryDelay: {
|
|
133
|
+
type: 'number',
|
|
134
|
+
description: 'Delay between retries in milliseconds',
|
|
135
|
+
optional: true,
|
|
136
|
+
min: 100,
|
|
137
|
+
max: 60000,
|
|
138
|
+
default: 1000
|
|
139
|
+
},
|
|
140
|
+
|
|
141
|
+
timeout: {
|
|
142
|
+
type: 'number',
|
|
143
|
+
description: 'Operation timeout in milliseconds',
|
|
144
|
+
optional: true,
|
|
145
|
+
min: 1000,
|
|
146
|
+
max: 300000,
|
|
147
|
+
default: 30000
|
|
148
|
+
},
|
|
149
|
+
|
|
150
|
+
batchSize: {
|
|
151
|
+
type: 'number',
|
|
152
|
+
description: 'Event sync batch size',
|
|
153
|
+
optional: true,
|
|
154
|
+
min: 1,
|
|
155
|
+
max: 10000,
|
|
156
|
+
default: 1000
|
|
157
|
+
},
|
|
158
|
+
|
|
159
|
+
startBlockNumber: {
|
|
160
|
+
type: 'number',
|
|
161
|
+
description: 'Starting block number for sync',
|
|
162
|
+
optional: true,
|
|
163
|
+
min: 0,
|
|
164
|
+
default: 1
|
|
165
|
+
},
|
|
166
|
+
|
|
167
|
+
autoSync: {
|
|
168
|
+
type: 'boolean',
|
|
169
|
+
description: 'Enable automatic syncing',
|
|
170
|
+
optional: true,
|
|
171
|
+
default: false
|
|
172
|
+
},
|
|
173
|
+
|
|
174
|
+
syncInterval: {
|
|
175
|
+
type: 'number',
|
|
176
|
+
description: 'Auto-sync interval in milliseconds',
|
|
177
|
+
optional: true,
|
|
178
|
+
min: 10000,
|
|
179
|
+
max: 3600000,
|
|
180
|
+
default: 60000,
|
|
181
|
+
dependsOn: 'autoSync'
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
// Validation error messages
|
|
187
|
+
this.errorMessages = {
|
|
188
|
+
required: (field) => `${field} is required`,
|
|
189
|
+
type: (field, expected, actual) => `${field} must be of type ${expected}, got ${actual}`,
|
|
190
|
+
minLength: (field, min) => `${field} must be at least ${min} characters long`,
|
|
191
|
+
maxLength: (field, max) => `${field} must be at most ${max} characters long`,
|
|
192
|
+
pattern: (field) => `${field} has invalid format`,
|
|
193
|
+
min: (field, min) => `${field} must be at least ${min}`,
|
|
194
|
+
max: (field, max) => `${field} must be at most ${max}`,
|
|
195
|
+
enum: (field, values) => `${field} must be one of: ${values.join(', ')}`,
|
|
196
|
+
minItems: (field, min) => `${field} must have at least ${min} items`,
|
|
197
|
+
custom: (field, message) => `${field}: ${message}`
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Validate configuration against schema
|
|
203
|
+
* @param {Object} config - Configuration to validate
|
|
204
|
+
* @returns {Object} Validation result
|
|
205
|
+
*/
|
|
206
|
+
validate(config) {
|
|
207
|
+
const errors = [];
|
|
208
|
+
const warnings = [];
|
|
209
|
+
const validated = {};
|
|
210
|
+
|
|
211
|
+
// Check required fields
|
|
212
|
+
for (const field of this.schema.required) {
|
|
213
|
+
if (!config.hasOwnProperty(field) || config[field] === undefined || config[field] === null) {
|
|
214
|
+
errors.push({
|
|
215
|
+
field,
|
|
216
|
+
type: 'required',
|
|
217
|
+
message: this.errorMessages.required(field)
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Validate each field
|
|
223
|
+
for (const [field, definition] of Object.entries(this.schema.fields)) {
|
|
224
|
+
if (config.hasOwnProperty(field)) {
|
|
225
|
+
const result = this._validateField(field, config[field], definition);
|
|
226
|
+
|
|
227
|
+
if (result.errors.length > 0) {
|
|
228
|
+
errors.push(...result.errors);
|
|
229
|
+
} else {
|
|
230
|
+
validated[field] = result.value;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
if (result.warnings.length > 0) {
|
|
234
|
+
warnings.push(...result.warnings);
|
|
235
|
+
}
|
|
236
|
+
} else if (!definition.optional && !this.schema.required.includes(field)) {
|
|
237
|
+
// Field is not required but has a default value
|
|
238
|
+
if (definition.hasOwnProperty('default')) {
|
|
239
|
+
validated[field] = definition.default;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// Check for unknown fields
|
|
245
|
+
for (const field of Object.keys(config)) {
|
|
246
|
+
if (!this.schema.fields.hasOwnProperty(field)) {
|
|
247
|
+
warnings.push({
|
|
248
|
+
field,
|
|
249
|
+
type: 'unknown',
|
|
250
|
+
message: `Unknown configuration field: ${field}`
|
|
251
|
+
});
|
|
252
|
+
// Include unknown fields in validated config
|
|
253
|
+
validated[field] = config[field];
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// Check field dependencies
|
|
258
|
+
for (const [field, definition] of Object.entries(this.schema.fields)) {
|
|
259
|
+
if (definition.dependsOn && validated[field] !== undefined) {
|
|
260
|
+
if (!validated[definition.dependsOn]) {
|
|
261
|
+
warnings.push({
|
|
262
|
+
field,
|
|
263
|
+
type: 'dependency',
|
|
264
|
+
message: `${field} depends on ${definition.dependsOn} being set`
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
return {
|
|
271
|
+
valid: errors.length === 0,
|
|
272
|
+
errors,
|
|
273
|
+
warnings,
|
|
274
|
+
validated
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Validate a single field
|
|
280
|
+
* @private
|
|
281
|
+
*/
|
|
282
|
+
_validateField(field, value, definition) {
|
|
283
|
+
const errors = [];
|
|
284
|
+
const warnings = [];
|
|
285
|
+
let validatedValue = value;
|
|
286
|
+
|
|
287
|
+
// Type validation
|
|
288
|
+
const actualType = Array.isArray(value) ? 'array' : typeof value;
|
|
289
|
+
const expectedTypes = Array.isArray(definition.type) ? definition.type : [definition.type];
|
|
290
|
+
|
|
291
|
+
if (!expectedTypes.includes(actualType)) {
|
|
292
|
+
errors.push({
|
|
293
|
+
field,
|
|
294
|
+
type: 'type',
|
|
295
|
+
message: this.errorMessages.type(field, expectedTypes.join(' or '), actualType)
|
|
296
|
+
});
|
|
297
|
+
return { errors, warnings, value };
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// Apply transformation if defined
|
|
301
|
+
if (definition.transform && typeof definition.transform === 'function') {
|
|
302
|
+
validatedValue = definition.transform(validatedValue);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// String validations
|
|
306
|
+
if (actualType === 'string') {
|
|
307
|
+
if (definition.minLength && validatedValue.length < definition.minLength) {
|
|
308
|
+
errors.push({
|
|
309
|
+
field,
|
|
310
|
+
type: 'minLength',
|
|
311
|
+
message: this.errorMessages.minLength(field, definition.minLength)
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
if (definition.maxLength && validatedValue.length > definition.maxLength) {
|
|
316
|
+
errors.push({
|
|
317
|
+
field,
|
|
318
|
+
type: 'maxLength',
|
|
319
|
+
message: this.errorMessages.maxLength(field, definition.maxLength)
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
if (definition.pattern && !definition.pattern.test(validatedValue)) {
|
|
324
|
+
errors.push({
|
|
325
|
+
field,
|
|
326
|
+
type: 'pattern',
|
|
327
|
+
message: this.errorMessages.pattern(field)
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
if (definition.enum && !definition.enum.includes(validatedValue)) {
|
|
332
|
+
errors.push({
|
|
333
|
+
field,
|
|
334
|
+
type: 'enum',
|
|
335
|
+
message: this.errorMessages.enum(field, definition.enum)
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// Number validations
|
|
341
|
+
if (actualType === 'number') {
|
|
342
|
+
if (definition.min !== undefined && validatedValue < definition.min) {
|
|
343
|
+
errors.push({
|
|
344
|
+
field,
|
|
345
|
+
type: 'min',
|
|
346
|
+
message: this.errorMessages.min(field, definition.min)
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
if (definition.max !== undefined && validatedValue > definition.max) {
|
|
351
|
+
errors.push({
|
|
352
|
+
field,
|
|
353
|
+
type: 'max',
|
|
354
|
+
message: this.errorMessages.max(field, definition.max)
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
// Array validations
|
|
360
|
+
if (actualType === 'array') {
|
|
361
|
+
if (definition.minItems && validatedValue.length < definition.minItems) {
|
|
362
|
+
errors.push({
|
|
363
|
+
field,
|
|
364
|
+
type: 'minItems',
|
|
365
|
+
message: this.errorMessages.minItems(field, definition.minItems)
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
// Validate array items if schema is provided
|
|
370
|
+
if (definition.items) {
|
|
371
|
+
validatedValue.forEach((item, index) => {
|
|
372
|
+
const itemResult = this._validateField(
|
|
373
|
+
`${field}[${index}]`,
|
|
374
|
+
item,
|
|
375
|
+
definition.items
|
|
376
|
+
);
|
|
377
|
+
errors.push(...itemResult.errors);
|
|
378
|
+
warnings.push(...itemResult.warnings);
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
// Object validations
|
|
384
|
+
if (actualType === 'object' && definition.properties) {
|
|
385
|
+
for (const [prop, propDef] of Object.entries(definition.properties)) {
|
|
386
|
+
if (validatedValue.hasOwnProperty(prop)) {
|
|
387
|
+
const propResult = this._validateField(
|
|
388
|
+
`${field}.${prop}`,
|
|
389
|
+
validatedValue[prop],
|
|
390
|
+
propDef
|
|
391
|
+
);
|
|
392
|
+
errors.push(...propResult.errors);
|
|
393
|
+
warnings.push(...propResult.warnings);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
// Custom validation
|
|
399
|
+
if (definition.validate && typeof definition.validate === 'function') {
|
|
400
|
+
const isValid = definition.validate(validatedValue);
|
|
401
|
+
if (!isValid) {
|
|
402
|
+
errors.push({
|
|
403
|
+
field,
|
|
404
|
+
type: 'custom',
|
|
405
|
+
message: this.errorMessages.custom(field, 'Custom validation failed')
|
|
406
|
+
});
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
return { errors, warnings, value: validatedValue };
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
/**
|
|
414
|
+
* Get schema documentation
|
|
415
|
+
* @returns {Object} Schema documentation
|
|
416
|
+
*/
|
|
417
|
+
getDocumentation() {
|
|
418
|
+
const docs = {
|
|
419
|
+
description: 'Magazine configuration schema',
|
|
420
|
+
required: this.schema.required,
|
|
421
|
+
fields: {}
|
|
422
|
+
};
|
|
423
|
+
|
|
424
|
+
for (const [field, definition] of Object.entries(this.schema.fields)) {
|
|
425
|
+
docs.fields[field] = {
|
|
426
|
+
type: definition.type,
|
|
427
|
+
description: definition.description,
|
|
428
|
+
required: this.schema.required.includes(field),
|
|
429
|
+
optional: definition.optional,
|
|
430
|
+
default: definition.default,
|
|
431
|
+
example: definition.example
|
|
432
|
+
};
|
|
433
|
+
|
|
434
|
+
if (definition.enum) {
|
|
435
|
+
docs.fields[field].enum = definition.enum;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
if (definition.min !== undefined || definition.max !== undefined) {
|
|
439
|
+
docs.fields[field].range = {
|
|
440
|
+
min: definition.min,
|
|
441
|
+
max: definition.max
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
if (definition.pattern) {
|
|
446
|
+
docs.fields[field].pattern = definition.pattern.toString();
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
if (definition.dependsOn) {
|
|
450
|
+
docs.fields[field].dependsOn = definition.dependsOn;
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
return docs;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
/**
|
|
458
|
+
* Generate example configuration
|
|
459
|
+
* @param {Object} options - Generation options
|
|
460
|
+
* @returns {Object} Example configuration
|
|
461
|
+
*/
|
|
462
|
+
generateExample(options = {}) {
|
|
463
|
+
const { includeOptional = true, useDefaults = true } = options;
|
|
464
|
+
const example = {};
|
|
465
|
+
|
|
466
|
+
for (const [field, definition] of Object.entries(this.schema.fields)) {
|
|
467
|
+
if (this.schema.required.includes(field) || (includeOptional && definition.optional)) {
|
|
468
|
+
if (definition.example !== undefined) {
|
|
469
|
+
example[field] = definition.example;
|
|
470
|
+
} else if (useDefaults && definition.default !== undefined) {
|
|
471
|
+
example[field] = definition.default;
|
|
472
|
+
} else {
|
|
473
|
+
// Generate a placeholder based on type
|
|
474
|
+
example[field] = this._generatePlaceholder(definition);
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
return example;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
/**
|
|
483
|
+
* Generate placeholder value based on type
|
|
484
|
+
* @private
|
|
485
|
+
*/
|
|
486
|
+
_generatePlaceholder(definition) {
|
|
487
|
+
const type = Array.isArray(definition.type) ? definition.type[0] : definition.type;
|
|
488
|
+
|
|
489
|
+
switch (type) {
|
|
490
|
+
case 'string':
|
|
491
|
+
if (definition.enum) return definition.enum[0];
|
|
492
|
+
return 'example-value';
|
|
493
|
+
case 'number':
|
|
494
|
+
if (definition.min !== undefined) return definition.min;
|
|
495
|
+
return 0;
|
|
496
|
+
case 'boolean':
|
|
497
|
+
return false;
|
|
498
|
+
case 'array':
|
|
499
|
+
return [];
|
|
500
|
+
case 'object':
|
|
501
|
+
return {};
|
|
502
|
+
default:
|
|
503
|
+
return null;
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
}
|